Python Pandas.Series.str.contains整个单词匹配

8
df(Pandas数据帧)有三行。
col_name
"This is Donald."
"His hands are so small"
"Why are his fingers so short?"

我想提取包含“is”和“small”的行。
如果我这样做:
df.col_name.str.contains("is|small", case=False)

然后它也捕捉了“His”,而我不想要。
以下查询是否是在 df.series 中捕获整个单词的正确方式?
df.col_name.str.contains("\bis\b|\bsmall\b", case=False)
5个回答

10
不,正则表达式/bis/b|/bsmall/b会失败,因为你使用的是/b而不是\b,后者意思是“单词边界”。
更改一下就可以匹配了。我建议使用
\b(is|small)\b

这个正则表达式在一定程度上更快且更易读,至少对我来说是这样的。请记得将它放在原始字符串中(r"\b(is|small)\b"),这样你就不必转义反斜杠。


谢谢。我考虑了你的观点 /b -> \b。我还想再等几天,看看是否有其他方法来捕获整个单词。 - aerin
1
顺带一提,我不得不在字符串前面添加 r 才能使其正常工作:有人知道为什么吗?我没有找到任何参考资料。 - mccc
显然,|字符会隐式地转换为正则表达式,而\b则不会。 - mccc
@mccc 它将其转换为原始字符串(这是Python的一个特性,而不是Pandas或Regex的特性)。 - Laurel
@Laurel 我认为你的回答如果加上关于使用原始字符串参数的点,会更完整,因为这也是 OP 查询中缺失的部分。 - Mitali Cyrus

4

首先,您可能希望将所有内容转换为小写字母,移除标点符号和空格,然后将结果转换为单词集合。

import string

df['words'] = [set(words) for words in
    df['col_name']
    .str.lower()
    .str.replace('[{0}]*'.format(string.punctuation), '')
    .str.strip()
    .str.split()
]

>>> df
                        col_name                                words
0                This is Donald.                   {this, is, donald}
1         His hands are so small         {small, his, so, are, hands}
2  Why are his fingers so short?  {short, fingers, his, so, are, why}

你现在可以使用布尔索引来查看你的目标词是否都在这些新的单词集合中。
target_words = ['is', 'small']
# Convert target words to lower case just to be safe.
target_words = [word.lower() for word in target_words]

df['match'] = df.words.apply(lambda words: all(target_word in words 
                                               for target_word in target_words))


print(df)
# Output: 
#                         col_name                                words  match
# 0                This is Donald.                   {this, is, donald}  False
# 1         His hands are so small         {small, his, so, are, hands}  False
# 2  Why are his fingers so short?  {short, fingers, his, so, are, why}  False    

target_words = ['so', 'small']
target_words = [word.lower() for word in target_words]

df['match'] = df.words.apply(lambda words: all(target_word in words 
                                               for target_word in target_words))

print(df)
# Output:
# Output: 
#                         col_name                                words  match
# 0                This is Donald.                   {this, is, donald}  False
# 1         His hands are so small         {small, his, so, are, hands}   True
# 2  Why are his fingers so short?  {short, fingers, his, so, are, why}  False    

提取匹配的行:
>>> df.loc[df.match, 'col_name']
# Output:
# 1    His hands are so small
# Name: col_name, dtype: object

使用布尔索引将所有内容转换为单个语句:

df.loc[[all(target_word in word_set for target_word in target_words) 
        for word_set in (set(words) for words in
                         df['col_name']
                         .str.lower()
                         .str.replace('[{0}]*'.format(string.punctuation), '')
                         .str.strip()
                         .str.split())], :]

谢谢你的回答。我正在尝试使用Pandas的内置索引(因为我的表格包含约500k行),但我猜想你是在手动进行索引...? - aerin
3
不确定你的意思。这里使用了Pandas索引。 - Alexander
这将返回一个匹配项,但不是整个字符串的匹配! - Nico Coallier
@Nico,请详细说明。只需在相关列上使用布尔索引即可提取与上述示例匹配的行。 - Alexander
@Alexander,能否找出匹配句子中的哪个单词? - dondapati

1
"\bis\b|\bsmall\b"中,反斜杠符号 \b 在传递给正则表达式方法进行匹配/搜索之前首先被解析为 ASCII 退格符。有关更多信息,请查看此转义字符文档。该文档中提到:

当存在‘r’或‘R’前缀时,反斜杠后面的字符将原样包含在字符串中,所有反斜杠都保留在字符串中。

因此,有两个选项 -

  1. 使用r前缀
df.col_name.str.contains(r"\bis\b|\bsmall\b", case=False)

(或)转义字符\ -
df.col_name.str.contains("\\bis\\b|\\bsmall\\b", case=False)

如果您想看一个例子,这里是Fiddle


0

你的方法(使用/b)对我不起作用。我不确定为什么你不能使用逻辑运算符and (&),因为我认为那才是你真正想要的。

这是一个愚蠢的方法,但它可以工作:

mask = lambda x: ("is" in x) & ("small" in x)
series_name.apply(mask)

你提供的例子在这方面有些令人困惑,尽管我看到你已经重新措辞以使其更加清晰。这解决了你最初所说的问题:“我想提取包含“is”和“small”的行”。 - szeitlin

0

继续讨论,我想在正则表达式中使用变量,如下所示:

df = df_w[df_w['Country/Region'].str.match("\b(location.loc[i]['country'])\b",case=False)]

如果我不使用\b\b,代码会返回包含苏丹和南苏丹的所有列。但是,当我使用"\b(location.loc[i]['country'])\b"时,它会返回空数据框。请告诉我正确的用法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接