问题:从熊猫DataFrame中按部分字符串选择
我有一个DataFrame
4列,其中2个包含字符串值。我想知道是否有一种方法可以根据针对特定列的部分字符串匹配来选择行?
换句话说,一个函数或lambda函数将执行以下操作
re.search(pattern, cell_in_question)
返回一个布尔值。我熟悉的语法,df[df['A'] == "hello world"]
但似乎找不到用部分字符串匹配说的方法'hello'
。
有人可以指出正确的方向吗?
回答 0
基于github问题#620,看来您很快将能够执行以下操作:
df[df['A'].str.contains("hello")]
更新:熊猫0.8.1及更高版本中提供了矢量化字符串方法(即Series.str)。
回答 1
我尝试了上面提出的解决方案:
df[df["A"].str.contains("Hello|Britain")]
并得到一个错误:
ValueError:无法使用包含NA / NaN值的数组进行遮罩
您可以将NA值转换为False
,如下所示:
df[df["A"].str.contains("Hello|Britain", na=False)]
回答 2
如何从熊猫DataFrame中按部分字符串选择?
这篇文章是为想要
- 在字符串列中搜索子字符串(最简单的情况)
- 搜索多个子字符串(类似于
isin
) - 匹配文本中的整个单词(例如,“蓝色”应匹配“天空是蓝色”,而不是“ bluejay”)
- 匹配多个完整词
- 了解“ ValueError:无法使用包含NA / NaN值的向量进行索引”背后的原因
…并想进一步了解应优先采用哪种方法。
(PS:我在类似主题上看到了很多问题,我认为最好把它留在这里。)
基本子串搜索
# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1
col
0 foo
1 foobar
2 bar
3 baz
str.contains
可用于执行子字符串搜索或基于正则表达式的搜索。搜索默认为基于正则表达式,除非您明确禁用它。
这是一个基于正则表达式的搜索示例,
# 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
多个子串搜索
通过使用正则表达式OR管道进行正则表达式搜索,最容易实现这一点。
# 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
伏/秒
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
在列表组合中使用运算符,
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
(用于缓存正则表达式)+ Pattern.search
,
p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]
col
1 foobar
如果“ col”具有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
这是一个循环的包装器,但是比大多数pandas 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
通过python引擎支持字符串方法。这没有提供明显的性能优势,但是对于了解是否需要动态生成查询很有用。
df1.query('col.str.contains("foo")', engine='python')
col
0 foo
1 foobar
有关更多信息query
和eval
方法系列,请参见使用pd.eval()在大熊猫中进行动态表达评估。。
推荐用法
- (第一)
str.contains
,因为它简单易用,可以处理NaN和混合数据 - 列出其性能的理解(特别是如果您的数据是纯字符串)
np.vectorize
- (持续)
df.query
回答 3
如果有人想知道如何执行相关问题:“按部分字符串选择列”
采用:
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)
回答 4
快速说明:如果要基于索引中包含的部分字符串进行选择,请尝试以下操作:
df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]
回答 5
说您有以下内容DataFrame
:
>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b'])
>>> df
a b
0 hello hello world
1 abcd defg
您始终可以in
在lambda表达式中使用运算符来创建过滤器。
>>> df.apply(lambda x: x['a'] in x['b'], axis=1)
0 True
1 False
dtype: bool
这里的技巧是使用中的axis=1
选项apply
将元素逐行(而不是逐列)传递给lambda函数。
回答 6
这就是我为部分字符串匹配所做的最终结果。如果有人有更有效的方法,请告诉我。
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
回答 7
对于包含特殊字符的字符串,使用contains效果不佳。找到工作了。
df[df['A'].str.find("hello") != -1]
回答 8
在此之前,有一些答案可以完成所要求的功能,无论如何,我想以最普遍的方式展示:
df.filter(regex=".*STRING_YOU_LOOK_FOR.*")
这样,无论编写哪种方式,您都可以获取要查找的列。
(显然,您必须为每种情况编写正确的regex表达式)
回答 9
也许您想在Pandas数据框的所有列中搜索一些文本,而不仅仅是在它们的子集中。在这种情况下,以下代码将有所帮助。
df[df.apply(lambda row: row.astype(str).str.contains('String To Find').any(), axis=1)]
警告。此方法相对较慢,但很方便。
回答 10
如果您需要在pandas dataframe列中进行不区分大小写的搜索,请执行以下操作:
df[df['A'].str.contains("hello", case=False)]