Select by partial string from a pandas DataFrame
我有一个
换句话说,一个函数或lambda函数
1 | re.search(pattern, cell_in_question) |
返回布尔值。我熟悉
有人能指出我的正确方向吗?
基于Github发行620,您很快就能做到以下几点:
1 | df[df['A'].str.contains("hello")] |
更新:矢量化字符串方法(即series.str)在pandas 0.8.1及更高版本中提供。
我在ipython笔记本的MacOS上使用熊猫0.14.1。我尝试了上面的建议行:
1 | df[df['A'].str.contains("Hello|Britain")] |
并得到一个错误:
1 | "cannot index with vector containing NA / NaN values" |
但当添加"==true"条件时,它工作得很好,如下所示:
1 | df[df['A'].str.contains("Hello|Britain")==True] |
如果有人想知道如何执行相关问题:"按部分字符串选择列"
用途:
1 | df.filter(like='hello') # select columns which contain the word hello |
通过部分字符串匹配选择行,通过
1 2 | # selects rows which contain the word hello in their index label df.filter(like='hello', axis=0) |
快速说明:如果要根据索引中包含的部分字符串进行选择,请尝试以下操作:
1 2 | df['stridx']=df.index df[df['stridx'].str.contains("Hello|Britain")] |
假设您有以下
1 2 3 4 5 | >>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b']) >>> df a b 0 hello hello world 1 abcd defg |
始终可以在lambda表达式中使用
1 2 3 4 | >>> df.apply(lambda x: x['a'] in x['b'], axis=1) 0 True 1 False dtype: bool |
这里的技巧是使用
How do I select by partial string from a pandas DataFrame?
这篇文章是为那些想要
- 在字符串列中搜索子字符串(最简单的情况)
- 搜索多个子串(类似于
isin ) - 匹配文本中的整个单词(例如,"蓝色"应与"天空是蓝色"匹配,而不是"蓝色杰伊")。
- 匹配多个整词
…并希望了解更多关于哪些方法比其他方法更可取的信息。
(附言:我看过很多关于类似主题的问题,我想把这个放在这里会更好。)
基本子字符串搜索1 2 3 4 5 6 7 8 | df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']}) df1 col 0 foo 1 foobar 2 bar 3 baz |
要选择包含"foo"的所有行,请使用
1 2 3 4 5 | df1[df1['col'].str.contains('foo')] col 0 foo 1 foobar |
请注意,这是纯子字符串搜索,因此可以安全地禁用基于regex的匹配。
1 2 3 4 5 | df1[df1['col'].str.contains('foo', regex=False)] col 0 foo 1 foobar |
从性能上讲,这确实会有所不同。
1 2 3 4 5 6 7 | 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) |
如果不需要,请避免使用基于regex的搜索。
Note
Partial substring searches that are anchored at the start or end of strings can be done usingstr.startswith orstr.endswith
respectively.Additionally, for regex based searches anchored at the start, use
str.match .
基于regex的搜索大多数
1 2 3 4 | df1[df1['col'].str.contains(r'foo(?!$)')] col 1 foobar |
多个子字符串搜索
这最容易通过使用regex或pipe进行regex搜索来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 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 |
您还可以创建术语列表,然后加入它们:
1 2 3 4 5 6 7 | terms = ['foo', 'baz'] df4[df4['col'].str.contains('|'.join(terms))] col 0 foo abc 1 foobar xyz 3 baz 45 |
有时,如果术语中包含可以解释为regex元字符的字符,则最好不要使用这些字符。如果您的术语包含以下任何字符…
1 | . ^ $ * + ? { } [ ] \ | ( ) |
然后,您需要使用
1 2 3 4 5 6 7 | import re df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))] col 0 foo abc 1 foobar xyz 3 baz 45 |
1 2 | re.escape(r'.foo^') # '\\.foo\\^' |
匹配整个单词
默认情况下,子字符串搜索搜索搜索指定的子字符串/模式,而不管它是否为完整的字。为了只匹配完整的单词,我们需要特别使用正则表达式,我们的模式需要指定单词边界(
例如,
1 2 3 4 5 6 | df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']}) df3 col 0 the sky is blue 1 bluejay by the window |
现在考虑一下,
1 2 3 4 5 | df3[df3['col'].str.contains('blue')] col 0 the sky is blue 1 bluejay by the window |
V/S
1 2 3 4 | df3[df3['col'].str.contains(r'\bblue\b')] col 0 the sky is blue |
多个整词搜索
与上面类似,只是我们在连接模式中添加了一个单词边界(
1 2 3 4 5 6 | p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms))) df4[df4['col'].str.contains(p)] col 0 foo abc 3 baz 45 |
如果
1 2 | p # '\\b(?:foo|baz)\\b' |
一个很好的选择:使用列表理解!
因为你可以!你应该!它们通常比字符串方法快一点,因为字符串方法很难向量化,而且通常有循环实现。
而不是,
1 | df1[df1['col'].str.contains('foo', regex=False)] |
在列表组件中使用
1 2 3 4 5 | df1[['foo' in x for x in df1['col']]] col 0 foo abc 1 foobar |
而不是,
1 2 | regex_pattern = r'foo(?!$)' df1[df1['col'].str.contains(regex_pattern)] |
在列表组件中使用
1 2 3 4 5 | p = re.compile(regex_pattern, flags=re.IGNORECASE) df1[[bool(p.search(x)) for x in df1['col']]] col 1 foobar |
如果"col"有nan,则不是
1 | df1[df1['col'].str.contains(regex_pattern, na=False)] |
使用,
1 2 3 4 5 6 7 8 9 10 11 | 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 |
部分字符串匹配的更多选项:
除了
1 2 3 4 5 | df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1] col 0 foo abc 1 foobar xyz |
1 2 3 4 5 6 7 8 9 | 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解决方案:
1 2 3 4 5 6 7 | 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 |
1 2 3 4 5 | df1.query('col.str.contains("foo")', engine='python') col 0 foo 1 foobar |
有关
以下是我最后对部分字符串匹配所做的操作。如果有人有更有效的方法,请告诉我。
1 2 3 4 5 6 7 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 |