ChatGPT解决这个技术问题 Extra ChatGPT

按子字符串标准过滤 pandas DataFrame

我有一个带有一列字符串值的 pandas DataFrame。我需要根据部分字符串匹配选择行。

像这样的成语:

re.search(pattern, cell_in_question) 

返回一个布尔值。我熟悉 df[df['A'] == "hello world"] 的语法,但似乎无法找到一种方法来处理部分字符串匹配,比如 'hello'


h
hlin117

根据 github 问题 #620,您似乎很快就可以执行以下操作:

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

更新:vectorized string methods (i.e., Series.str) 在 pandas 0.8.1 及更高版本中可用。


如果我想用“OR”条件找到它们,我们该如何处理“Hello”和“Britain”。
由于 str.* 方法将输入模式视为正则表达式,因此您可以使用 df[df['A'].str.contains("Hello|Britain")]
是否可以将 .str.contains 转换为使用 .query() api
df[df['value'].astype(str).str.contains('1234.+')] 用于过滤掉非字符串类型的列。
s
sharon

我在 ipython 笔记本的 macos 上使用 pandas 0.14.1。我尝试了上面建议的行:

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

并得到一个错误:

无法使用包含 NA / NaN 值的向量进行索引

但是当添加“==True”条件时它工作得很好,如下所示:

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

df[df['A'].astype(str).str.contains("Hello|Britain")] 也有效
另一种解决方案是:``` df[df["A"].str.contains("Hello|Britain") == True] ```
c
cs95

如何从 pandas DataFrame 中选择部分字符串?

这篇文章是为那些想要

在字符串列中搜索子字符串(最简单的情况)

搜索多个子字符串(类似于 isin)

匹配文本中的整个单词(例如,“blue”应该匹配“the sky is blue”而不是“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)

如果不需要,请避免使用基于正则表达式的搜索。

寻址 ValueErrors
有时,对结果执行子字符串搜索和过滤会导致

ValueError:无法使用包含 NA / NaN 值的向量进行索引

这通常是因为对象列中的混合数据或 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

如何一次将其应用于多个列?
答案就在问题中。使用 DataFrame.apply

# `axis=1` tells `apply` to apply the lambda function column-wise.
df.apply(lambda col: col.str.contains('foo|bar', na=False), axis=1)

       A      B
0   True   True
1   True  False
2  False   True
3   True  False
4  False  False
5  False  False

下面的所有解决方案都可以使用按列的 apply 方法“应用”到多个列(只要您没有太多列,这在我的书中是可以的)。

如果您有一个包含混合列的 DataFrame,并且只想选择对象/字符串列,请查看 select_dtypes

多子串搜索

这最容易通过使用正则表达式 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

有时,明智的做法是避开您的术语,以防它们包含可以解释为 regex metacharacters 的字符。如果您的条款包含以下任何字符...

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

然后,您需要使用 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'

一个很好的选择:使用列表理解!

因为你能! And you should! 它们通常比字符串方法快一点,因为字符串方法难以向量化并且通常具有循环实现。

代替,

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

np.vectorize
这是循环的包装器,但与大多数 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

有关 queryeval 系列方法的更多信息,请参见 Dynamic Expression Evaluation in pandas using pd.eval()

推荐使用优先级

(第一)str.contains,因为它的简单和易于处理 NaN 和混合数据列表推导,因为它的性能(特别是如果你的数据是纯字符串)np.vectorize(最后)df.query


在两列或多列中搜索字符串时,您能否使用正确的方法进行编辑?基本上:any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2'])) 和我尝试过的所有变体都窒息(它抱怨 any() 是正确的......但是文档非常不清楚如何进行这样的查询。
@DenisdeBernardy df[['col1', 'col2']].apply(lambda x: x.str.contains('foo|bar')).any(axis=1)
在这种情况下,@00schneider r 用于指示原始字符串文字。这些使编写正则表达式字符串变得更加容易。 stackoverflow.com/q/2081640
@arno_v 很高兴听到,看起来熊猫的性能正在提高!
非常有帮助!特别是“重新导入”功能改变了游戏规则。起首!
a
ayhan

如果有人想知道如何执行相关问题:“按部分字符串选择列”

利用:

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)  

这可以提炼为:df.loc[:, df.columns.str.contains('a')]
可以进一步提炼为df.filter(like='a')
这应该是一个自己的问题+答案,已经有 50 人搜索了它......
@PV8 问题已存在:stackoverflow.com/questions/31551412/…。但是当我在谷歌上搜索“pandas Select column by partial string”时,这个线程首先出现
C
Christian

快速说明:如果您想根据索引中包含的部分字符串进行选择,请尝试以下操作:

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

你可以 df[df.index.to_series().str.contains('LLChit')]
更简洁地说,不需要 to_seriesdf[df.index.str.contains('Hello|Britain')]
M
Mike

假设您有以下 DataFrame

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

您始终可以在 lambda 表达式中使用 in 运算符来创建过滤器。

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

这里的技巧是使用 apply 中的 axis=1 选项将元素逐行传递给 lambda 函数,而不是逐列传递。


我如何在上面修改说 x['a'] 仅存在于 x['b'] 的开头?
就性能和内存而言,apply 在这里是个坏主意。请参阅this answer
c
cardamom

如果您需要对 pandas 数据框列中的字符串进行不区分大小写的搜索:

df[df['A'].str.contains("hello", case=False)]

s
svp

您可以尝试将它们视为字符串:

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

非常感谢,您的回答对我帮助很大,因为我正在努力通过数据为 bool 类型的列过滤数据框。您的解决方案帮助我完成了我需要的过滤器。为你+1。
N
Niels Henkens

假设我们在数据框 df 中有一个名为“ENTITY”的列。我们可以过滤我们的 df,以获得整个数据帧 df,其中“实体”列的行不包含“DM”,方法是使用如下掩码:

mask = df['ENTITY'].str.contains('DM')

df = df.loc[~(mask)].copy(deep=True)

e
euforia

这是我最终为部分字符串匹配所做的。如果有人有更有效的方法,请告诉我。

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

如果在循环之前编译正则表达式,应该快 2 到 3 倍: regex = re.compile(regex) 然后 if regex.search(record)
@MarkokraM docs.python.org/3.6/library/re.html#re.compile 表示为您缓存了最新的正则表达式,因此您无需自己编译。
不要使用 ititems 来迭代 DataFrame。它在可扩展性和性能方面排名最后
遍历数据帧违背了 pandas 的全部目的。改用加勒特的解决方案
K
Katu

对于带有特殊字符的字符串,使用 contains 效果不佳。虽然找到工作。

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

G
Grant Shannon

一个更通用的示例 - 如果在字符串中查找单词的一部分或特定单词:

df = pd.DataFrame([('cat andhat', 1000.0), ('hat', 2000000.0), ('the small dog', 1000.0), ('fog', 330000.0),('pet', 330000.0)], columns=['col1', 'col2'])

句子或单词的特定部分:

searchfor = '.*cat.*hat.*|.*the.*dog.*'

创建显示受影响行的列(可以随时根据需要过滤掉)

df["TrueFalse"]=df['col1'].str.contains(searchfor, regex=True)

    col1             col2           TrueFalse
0   cat andhat       1000.0         True
1   hat              2000000.0      False
2   the small dog    1000.0         True
3   fog              330000.0       False
4   pet 3            30000.0        False

S
Serhii Kushchenko

也许您想在 Pandas 数据框的所有列中搜索一些文本,而不仅仅是在它们的子集中。在这种情况下,以下代码将有所帮助。

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

警告。这种方法虽然很方便,但速度相对较慢。


x
xpeiro

在此之前有一些答案可以完成所要求的功能,无论如何我想展示最普遍的方式:

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

通过这种方式,无论以何种方式编写,您都可以获得您要查找的列。

(显然,您必须为每种情况编写正确的正则表达式)


这会过滤列标题。这不是一般的,它是不正确的。
@MicheldeRuiter 仍然不正确,而是过滤索引标签!
b
buhtz

我的 2c 价值:

我做了以下事情:

sale_method = pd.DataFrame(model_data['Sale Method'].str.upper())
sale_method['sale_classification'] = \
    np.where(sale_method['Sale Method'].isin(['PRIVATE']),
             'private',
             np.where(sale_method['Sale Method']
                      .str.contains('AUCTION'),
                      'auction',
                      'other'
             )
    )

r
rachwa

有点类似于@cs95 的答案,但在这里您不需要指定引擎:

df.query('A.str.contains("hello").values')