एक पंडों से आंशिक स्ट्रिंग द्वारा चयन करें DataFrame


448

मेरे पास DataFrame4 कॉलम हैं जिनमें से 2 में स्ट्रिंग मान हैं। मैं सोच रहा था कि क्या किसी विशेष स्तंभ के खिलाफ आंशिक स्ट्रिंग मैच के आधार पर पंक्तियों का चयन करने का कोई तरीका था?

दूसरे शब्दों में, एक फ़ंक्शन या लैम्ब्डा फ़ंक्शन जो कुछ ऐसा करेगा

re.search(pattern, cell_in_question) 

एक बूलियन लौट रहा है। मैं वाक्य रचना से परिचित हूं, df[df['A'] == "hello world"]लेकिन आंशिक स्ट्रिंग मिलान के साथ ऐसा करने का तरीका खोजने के लिए प्रतीत नहीं हो सकता 'hello'

क्या कोई मुझे सही दिशा में इंगित करने में सक्षम होगा?

जवाबों:


786

जीथब्यू अंक # 620 के आधार पर , ऐसा लगता है कि आप जल्द ही निम्नलिखित कार्य करने में सक्षम होंगे:

df[df['A'].str.contains("hello")]

अपडेट: वेक्टर किए गए स्ट्रिंग तरीके (यानी, Series.str) पांडा 0.8.1 और ऊपर में उपलब्ध हैं।


1
हम "हैलो" और "ब्रिटेन" के बारे में कैसे जाने अगर मैं उन्हें "ओआर" स्थिति के साथ ढूंढना चाहता हूं।
लोनली सूल

56
चूँकि str। * मेथड्स इनपुट पैटर्न को एक रेगुलर एक्सप्रेशन के रूप में मानते हैं, आप इसका उपयोग कर सकते हैंdf[df['A'].str.contains("Hello|Britain")]
Garrett

7
क्या एपीआई.str.contains का उपयोग करना परिवर्तित करना संभव है ? .query()
zyxue


3
df[df['value'].astype(str).str.contains('1234.+')]गैर-स्ट्रिंग-प्रकार के स्तंभों को फ़िल्टर करने के लिए।
फ्राँस्वा लेब्लांक

213

मैंने ऊपर प्रस्तावित समाधान की कोशिश की:

df[df["A"].str.contains("Hello|Britain")]

और एक त्रुटि मिली:

ValueError: NA / NaN मान वाले सरणी के साथ मुखौटा नहीं कर सकता

आप NA मानों को Falseइस तरह बदल सकते हैं :

df[df["A"].str.contains("Hello|Britain", na=False)]

54
या आप कर सकते हैं: df [df ['A']। Str.contains ("हैलो | ब्रिटेन", ना = गलत)]
joshlk

2
df[df['A'].astype(str).str.contains("Hello|Britain")]साथ ही काम किया
नागभूषण एसएन

108

मैं एक पंडों DataFrame से आंशिक स्ट्रिंग द्वारा कैसे चयन करूं?

यह पोस्ट उन पाठकों के लिए है जो चाहते हैं

  • एक स्ट्रिंग कॉलम में सबस्ट्रिंग की खोज (सबसे सरल मामला)
  • कई पदार्थों के लिए खोज (समान isin)
  • पाठ से एक पूरे शब्द का मिलान करें (उदाहरण के लिए, "नीला" का मिलान "आकाश नीला है" लेकिन "नीलाजय" से नहीं होना चाहिए)
  • कई शब्दों का मिलान करें
  • "ValueError: एनए / NaN मान युक्त वेक्टर के साथ अनुक्रमणिका नहीं बना सकता" के पीछे के कारण को समझें

... और अधिक जानना चाहते हैं कि दूसरों पर किन तरीकों को प्राथमिकता दी जानी चाहिए।

(पुनश्च: मैंने ऐसे ही विषयों पर बहुत सारे प्रश्न देखे हैं, मुझे लगा कि इसे यहाँ छोड़ना अच्छा होगा।)


मूल पदार्थ खोज

# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1

      col
0     foo
1  foobar
2     bar
3     baz

str.containsया तो सब्स्क्राइब्ड खोजों या रेगेक्स आधारित खोज को करने के लिए इस्तेमाल किया जा सकता है। जब तक आप इसे स्पष्ट रूप से अक्षम नहीं करते हैं, तब तक खोज regex- आधारित पर निर्भर करती है।

यहाँ रेगेक्स-आधारित खोज का एक उदाहरण है,

# find rows in `df1` which contain "foo" followed by something
df1[df1['col'].str.contains(r'foo(?!$)')]

      col
1  foobar

कभी-कभी रेगेक्स खोज की आवश्यकता नहीं होती है, इसलिए regex=Falseइसे अक्षम करने के लिए निर्दिष्ट करें ।

#select all rows containing "foo"
df1[df1['col'].str.contains('foo', regex=False)]
# same as df1[df1['col'].str.contains('foo')] but faster.

      col
0     foo
1  foobar

प्रदर्शन बुद्धिमान, रेगेक्स खोज खोज विकल्प की तुलना में धीमी है:

df2 = pd.concat([df1] * 1000, ignore_index=True)

%timeit df2[df2['col'].str.contains('foo')]
%timeit df2[df2['col'].str.contains('foo', regex=False)]

6.31 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.8 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

यदि आपको इसकी आवश्यकता नहीं है, तो रेगेक्स-आधारित खोज का उपयोग करने से बचें।

को संबोधित करते ValueErrorरों
, कभी कभी एक सबस्ट्रिंग खोज करते समय और परिणाम के आधार पर फ़िल्टर में परिणाम होगा

ValueError: cannot index with vector containing NA / NaN values

यह आमतौर पर आपके ऑब्जेक्ट कॉलम में मिश्रित डेटा या NaN के कारण होता है,

s = pd.Series(['foo', 'foobar', np.nan, 'bar', 'baz', 123])
s.str.contains('foo|bar')

0     True
1     True
2      NaN
3     True
4    False
5      NaN
dtype: object


s[s.str.contains('foo|bar')]
# ---------------------------------------------------------------------------
# ValueError                                Traceback (most recent call last)

जो कुछ भी स्ट्रिंग नहीं है उस पर लागू स्ट्रिंग तरीके नहीं हो सकते हैं, इसलिए परिणाम NaN (स्वाभाविक रूप से) है। इस स्थिति में, na=Falseगैर-स्ट्रिंग डेटा को अनदेखा करने के लिए निर्दिष्ट करें ,

s.str.contains('foo|bar', na=False)

0     True
1     True
2    False
3     True
4    False
5    False
dtype: bool

एकाधिक प्रतिस्थापन खोज

यह सबसे आसानी से रेगेक्स या पाइप का उपयोग करके रेगेक्स खोज के माध्यम से प्राप्त किया जाता है।

# Slightly modified example.
df4 = pd.DataFrame({'col': ['foo abc', 'foobar xyz', 'bar32', 'baz 45']})
df4

          col
0     foo abc
1  foobar xyz
2       bar32
3      baz 45

df4[df4['col'].str.contains(r'foo|baz')]

          col
0     foo abc
1  foobar xyz
3      baz 45

आप शर्तों की एक सूची भी बना सकते हैं, फिर उनसे जुड़ सकते हैं:

terms = ['foo', 'baz']
df4[df4['col'].str.contains('|'.join(terms))]

          col
0     foo abc
1  foobar xyz
3      baz 45

कभी-कभी, आपकी शर्तों से बचने के लिए बुद्धिमान होता है जब उनके पास ऐसे चरित्र होते हैं जिन्हें रीगेक्स मेटाचैकर के रूप में व्याख्या किया जा सकता है । यदि आपकी शर्तों में निम्न वर्ण हैं ...

. ^ $ * + ? { } [ ] \ | ( )

फिर, आपको उनसे बचने के लिए उपयोग re.escapeकरने की आवश्यकता होगी :

import re
df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))]

          col
0     foo abc
1  foobar xyz
3      baz 45

re.escape विशेष पात्रों से बचने का प्रभाव पड़ता है, इसलिए उनका शाब्दिक रूप से इलाज किया जाता है।

re.escape(r'.foo^')
# '\\.foo\\^'

संपूर्ण शब्द मिलान

डिफ़ॉल्ट रूप से, सबस्ट्रिंग खोज निर्दिष्ट सबस्ट्रिंग / पैटर्न के लिए खोज करता है, भले ही यह पूर्ण शब्द हो या न हो। केवल पूर्ण शब्दों से मेल खाने के लिए, हमें यहाँ नियमित अभिव्यक्तियों का उपयोग करने की आवश्यकता होगी - विशेष रूप से, हमारे पैटर्न को शब्द सीमाओं ( \b) को निर्दिष्ट करने की आवश्यकता होगी ।

उदाहरण के लिए,

df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']})
df3

                     col
0        the sky is blue
1  bluejay by the window

अब विचार करें,

df3[df3['col'].str.contains('blue')]

                     col
0        the sky is blue
1  bluejay by the window

v / s

df3[df3['col'].str.contains(r'\bblue\b')]

               col
0  the sky is blue

एकाधिक पूरे शब्द खोज

उपरोक्त के समान, सिवाय हम \bशामिल हुए पैटर्न में शब्द सीमा ( ) जोड़ते हैं ।

p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms)))
df4[df4['col'].str.contains(p)]

       col
0  foo abc
3   baz 45

जहां pऐसा दिखता है,

p
# '\\b(?:foo|baz)\\b'

एक बढ़िया विकल्प: सूची बोध का उपयोग करें !

क्योंकि, तुम कर सकते हो! और आपको करना चाहिए! वे आमतौर पर स्ट्रिंग विधियों की तुलना में थोड़ा तेज होते हैं, क्योंकि स्ट्रिंग विधियां वेक्टर के लिए कठिन होती हैं और आमतौर पर लूप कार्यान्वयन होते हैं।

के बजाय,

df1[df1['col'].str.contains('foo', regex=False)]

inएक सूची COMP के अंदर ऑपरेटर का उपयोग करें ,

df1[['foo' in x for x in df1['col']]]

       col
0  foo abc
1   foobar

के बजाय,

regex_pattern = r'foo(?!$)'
df1[df1['col'].str.contains(regex_pattern)]

उपयोग करें re.compile(अपने regex को कैश करने के लिए) + Pattern.searchएक सूची COMP के अंदर,

p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]

      col
1  foobar

यदि "कर्नल" में NaN है, तो इसके बजाय

df1[df1['col'].str.contains(regex_pattern, na=False)]

उपयोग,

def try_search(p, x):
    try:
        return bool(p.search(x))
    except TypeError:
        return False

p = re.compile(regex_pattern)
df1[[try_search(p, x) for x in df1['col']]]

      col
1  foobar

आंशिक स्ट्रिंग मिलान के लिए अधिक विकल्प: np.char.find, np.vectorize, DataFrame.query

इसके अलावा str.containsऔर सूची की समझ के लिए, आप निम्नलिखित विकल्पों का भी उपयोग कर सकते हैं।

np.char.find
सबस्ट्रिंग खोजों का समर्थन करता है (केवल पढ़ें: कोई रेगेक्स)।

df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1]

          col
0     foo abc
1  foobar xyz

np.vectorize
यह एक लूप के चारों ओर एक आवरण है, लेकिन अधिकांश पांडा strतरीकों की तुलना में कम ओवरहेड है ।

f = np.vectorize(lambda haystack, needle: needle in haystack)
f(df1['col'], 'foo')
# array([ True,  True, False, False])

df1[f(df1['col'], 'foo')]

       col
0  foo abc
1   foobar

रेगेक्स समाधान संभव:

regex_pattern = r'foo(?!$)'
p = re.compile(regex_pattern)
f = np.vectorize(lambda x: pd.notna(x) and bool(p.search(x)))
df1[f(df1['col'])]

      col
1  foobar

DataFrame.query
अजगर इंजन के माध्यम से स्ट्रिंग विधियों का समर्थन करता है। यह कोई दृश्य प्रदर्शन लाभ प्रदान नहीं करता है, लेकिन फिर भी यह जानना उपयोगी है कि क्या आपको अपने प्रश्नों को गतिशील रूप से उत्पन्न करने की आवश्यकता है।

df1.query('col.str.contains("foo")', engine='python')

      col
0     foo
1  foobar

तरीकों की queryऔर अधिक जानकारी और evalपरिवार pd.eval () का उपयोग करके पांडा में गतिशील अभिव्यक्ति मूल्यांकन पर पाया जा सकता है ।


अनुशंसित उपयोग प्राथमिकता

  1. (पहला) str.contains, अपनी सादगी और आसानी से NaN और मिश्रित डेटा को संभालने के लिए
  2. सूची प्रदर्शन, इसके प्रदर्शन के लिए (विशेषकर यदि आपका डेटा विशुद्ध रूप से तार है)
  3. np.vectorize
  4. (अंतिम) df.query

क्या आप दो या दो से अधिक स्तंभों में स्ट्रिंग खोजते समय उपयोग करने के लिए सही विधि में संपादित कर सकते हैं? मूल रूप से: any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2']))और विविधताएं मैंने सभी चोक करने की कोशिश की (इसके बारे में शिकायत करता है any()और ठीक ही ऐसा है ... लेकिन डॉक्टर इस तरह की क्वेरी करने के रूप में स्पष्ट रूप से स्पष्ट नहीं है।
डेनिस डी बर्नार्डी

@ डेनिसडेबर्नेरीdf[['col1', 'col2']].apply(lambda x: x.str.contains('foo|bar')).any(axis=1)
cs95

@ सीएस 95 पंडों डीएफ में + के बाद व्हॉट्सएप वाले विकल्प के साथ पंक्तियां निकालना यह जल्द ही उत्तर दिया गया था, लेकिन आप इसे देखना चाह सकते हैं।
अंकि

@ankiiiiiii ऐसा लगता है कि आपने मेरे उत्तर के उस भाग को याद किया जहाँ मैंने रेगेक्स मेटाचैकर्स का उल्लेख किया था: "कभी-कभी, आपकी शर्तों से बचने के लिए समझदारी है कि उनके पास ऐसे पात्र हैं जिन्हें रेगेक्स मेटाचैकर के रूप में व्याख्या किया जा सकता है"।
cs95

1
@ इस मामले में 00schneider r का उपयोग कच्चे स्ट्रिंग शाब्दिक को इंगित करने के लिए किया जाता है। इनसे नियमित अभिव्यक्ति के तार लिखना आसान हो जाता है। stackoverflow.com/q/2081640
सीएस 95

53

अगर किसी को आश्चर्य हो कि संबंधित समस्या का निष्पादन कैसे किया जाए: "आंशिक स्ट्रिंग द्वारा कॉलम चुनें"

उपयोग:

df.filter(like='hello')  # select columns which contain the word hello

और आंशिक स्ट्रिंग मिलान द्वारा पंक्तियों का चयन करने के axis=0लिए, फ़िल्टर करने के लिए पास करें:

# selects rows which contain the word hello in their index label
df.filter(like='hello', axis=0)  

6
यह आसुत हो सकता है:df.loc[:, df.columns.str.contains('a')]
elPastor

18
जिसे और अधिक आसवित किया जा सकता हैdf.filter(like='a')
टेड पेट्रो

यह एक अपना प्रश्न + उत्तर होना चाहिए, पहले से ही 50 लोगों ने इसके लिए खोज की ...
पीवी

1
@ PV8 प्रश्न पहले से मौजूद है: stackoverflow.com/questions/31551412/… । लेकिन जब मैं "पंडों का चयन करें आंशिक स्तंभ द्वारा स्तंभ चुनें" के लिए Google पर खोज करता हूं, तो यह धागा पहले दिखाई देता है
फिलिप श्वार्ज़

28

त्वरित नोट: यदि आप अनुक्रमणिका में निहित आंशिक स्ट्रिंग के आधार पर चयन करना चाहते हैं, तो निम्न प्रयास करें:

df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]

5
आप बस df [df.index.to_series ()। Str.contains ('LLChit')]
यूरी बेडा

21

कहो कि आपके पास निम्नलिखित हैं DataFrame:

>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b'])
>>> df
       a            b
0  hello  hello world
1   abcd         defg

आप inअपने फ़िल्टर को बनाने के लिए हमेशा लंबर एक्सप्रेशन में ऑपरेटर का उपयोग कर सकते हैं ।

>>> df.apply(lambda x: x['a'] in x['b'], axis=1)
0     True
1    False
dtype: bool

स्तंभ द्वारा स्तंभ के विपरीत, पंक्ति द्वारा तत्वों को पास करने के axis=1विकल्प में विकल्प का उपयोग करने के लिए यहां चाल है apply


मैं ऊपर कैसे संशोधित करूं कि x ['a'] केवल x ['b'] की शुरुआत में मौजूद है?
कॉम्प्लेक्सडाटा

1
प्रदर्शन और याददाश्त के मामले में आवेदन एक बुरा विचार है। इस जवाब को देखें ।
cs95

8

यहाँ मैं आंशिक स्ट्रिंग मैचों के लिए कर रहा हूँ। अगर किसी के पास ऐसा करने का अधिक कुशल तरीका है तो कृपया मुझे बताएं।

def stringSearchColumn_DataFrame(df, colName, regex):
    newdf = DataFrame()
    for idx, record in df[colName].iteritems():

        if re.search(regex, record):
            newdf = concat([df[df[colName] == record], newdf], ignore_index=True)

    return newdf

3
यदि आप लूप से पहले regex संकलित करते हैं तो 2x से 3x तेज होना चाहिए: regex = re.compile (regex) और फिर यदि regex.search (रिकॉर्ड)
MarkokraM

1
@MarkokraM docs.python.org/3.6/library/re.html#re.compile का कहना है कि आपके लिए हाल ही में रीगेक्स कैश्ड हैं, इसलिए आपको खुद को संकलित करने की आवश्यकता नहीं है।
तेईपेम्म

DataFrame पर पुनरावृति करने के लिए पुनरावृत्तियों का उपयोग न करें। यह वांछनीयता और प्रदर्शन के मामले में अंतिम स्थान पर है
सीएस 95

5

विशेष वर्णों के साथ मेरे स्ट्रिंग के लिए अच्छी तरह से काम करना शामिल नहीं है। हालांकि काम मिल गया।

df[df['A'].str.find("hello") != -1]

2

इससे पहले कि उत्तर हैं जो पूछे गए फीचर को पूरा करते हैं, वैसे भी मैं सबसे आम तौर पर दिखाना चाहूंगा:

df.filter(regex=".*STRING_YOU_LOOK_FOR.*")

इस तरह से आप जो भी रास्ता लिखते हैं उसके लिए आप जिस कॉलम को देखते हैं, उसे प्राप्त करते हैं।

(वास्तव में, आपको प्रत्येक मामले के लिए उचित रेगेक्स अभिव्यक्ति लिखना होगा)


1
यह कॉलम हेडर पर फिल्टर करता है । यह सामान्य नहीं है, यह गलत है।
CS95

@MicheldeRuiter अभी भी गलत है, जो इसके बजाय इंडेक्स लेबल पर फ़िल्टर करेगा!
CS95

सवाल का जवाब नहीं देता। लेकिन मैंने कुछ सीखा। :)
मिशेल डी रुएटर

2

हो सकता है कि आप पंडों के डेटाफ़्रेम के सभी स्तंभों में कुछ पाठ खोजना चाहते हैं, न कि केवल उनके सबसेट में। इस स्थिति में, निम्न कोड मदद करेगा।

df[df.apply(lambda row: row.astype(str).str.contains('String To Find').any(), axis=1)]

चेतावनी। यह विधि अपेक्षाकृत धीमी है, यद्यपि सुविधाजनक है।


2

क्या आपको पंडों के डेटाफ्रेम कॉलम में स्ट्रिंग के लिए असंवेदनशील खोज करने की आवश्यकता है :

df[df['A'].str.contains("hello", case=False)]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.