मैं एक पंडों 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
इसके अलावा 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 () का उपयोग करके पांडा में गतिशील अभिव्यक्ति मूल्यांकन पर पाया जा सकता है ।
अनुशंसित उपयोग प्राथमिकता
- (पहला)
str.contains
, अपनी सादगी और आसानी से NaN और मिश्रित डेटा को संभालने के लिए
- सूची प्रदर्शन, इसके प्रदर्शन के लिए (विशेषकर यदि आपका डेटा विशुद्ध रूप से तार है)
np.vectorize
- (अंतिम)
df.query