【问题标题】:How do I identify rows containing a minimum number of distinct words from a candidate list?如何从候选列表中识别包含最少数量不同单词的行?
【发布时间】:2022-01-02 01:24:39
【问题描述】:

我有一个单词列表和一个数据集。我想识别数据集中至少包含列表中两个单词的行。

我能够识别包含至少两个列表词的行,但我的代码也有问题地识别了重复单个列表词的行。

这是我的代码:

import pandas as pd
    
data={'Name':['Redred','redblue','redgreen','blue']}

df=pd.DataFrame(data)

df['Good colours'] = (df['Name'].str.contains("(red.*|blue.*|green.*){2,}",case=False, regex=True))

print(df)

在结果数据集中,'redred' 行在不应该返回 true 时返回。

为了排除重复的单词,我尝试了(red.*){1,}(blue.*){1,}(green.*){1},但这意味着不再识别“redblue”和“redgreen”(代码必须识别这些行。)

如何编写正则表达式以识别列表词的两个实例,同时排除列表词的重复实例?

【问题讨论】:

  • 您要测试整行还是只测试单元格(即单列)?
  • 单列 :-)
  • 像“redredgreen”、“greenredred”或“redgreenred”这样的字符串呢?
  • 在我看来,这超出了正则表达式应该做的事情。我认为你需要做s.count('red')s.count('green')s.count('blue'),然后如果任何值> 1,则拒绝。这可能需要apply()
  • 我编辑了问题标题以反映对问题的新理解。

标签: python regex dataframe


【解决方案1】:

有时 regex 不是这项工作的工具。

我会简单地使用单独的调用来确定哪些行包含红色、绿色和蓝色,然后查看哪些行至少在其中两个组中。

def name_has_color(df, color):
    return df['Name'].str.contains(color, case=False).astype(int)

color_counts = sum(
    name_has_color(df, color)
    for color in ['red', 'green', 'blue']
)

df['good_colors'] = (color_counts >= 2)

这种方法自然具有可扩展性,如果您需要检查更多包含的值或需要更多查找的值,则不会变得更复杂。

【讨论】:

  • 我认为这个解决方案更快,可能更适合大型列表。将在真实数据集上进行测试。
  • 当我在数据集中添加一个新列时(即data={'Name':['123Redred','redblue','redredgreen','blue'],'OtherCol':['1','2','3','4']}我得到错误ValueError: Wrong number of items passed 2, placement implies 1。由于错误与新列有关,我认为代码可能需要指定行(即df['good_colors'] = df['Name'].apply(lambda x: color_counts(x) >= 2)),但这不起作用。您有什么建议吗?
  • 对不起,我打错了。我最初编写了过滤行的代码(df = df[color_counts >= 2])。 color_counts >= 2 已经是应该用于填充df['good_colors'] 问题的数据如您所描述的;使用它作为掩码会导致错误,因为数据不再排列。代码现已修复。
【解决方案2】:

你可以使用

(df['Name'].str.contains(r"^(?!(red|blue|green)\1+$)(?:.*(?:red|blue|green)){2}",case=False, regex=True))

如果您的字符串只能由列表中的单词组成,请删除.*,并将{2} 替换为{2,}$

(df['Name'].str.contains(r"^(?!(red|blue|green)\1+$)(?:red|blue|green){2,}$",case=False, regex=True))

详情

  • ^ - 字符串开头
  • (?!(red|blue|green)\1+$) - 如果存在匹配失败的否定前瞻
    • (red|blue|green) - 组 1:组中定义的任何子字符串
    • \1+ - 一次或多次重复第 1 组值,然后
    • $ - 字符串结束
  • (?:.*(?:red|blue|green)){2} - 出现两次零个或多个字符(换行符除外),尽可能多,然后出现一次组中定义的子字符串。
  • (?:red|blue|green){2,}$ - 组中的单词出现两次或多次,然后是字符串的结尾。

请参阅regex demo #1regex demo #2

【讨论】:

  • 澄清一下:蓝色应该是失败的。 'redredgreen' 不会失败。感谢您为这个 Wiktor 工作。
  • 抱歉,我不清楚。在列表词有两个实例的情况下,我希望该行为真。当列表词只有一个(重复)实例时,我希望该行为假。
  • @oymonk 好的,我更新了答案。如果您有很长的 bluered 等关键字列表,您可能想要使用 trieregex 库。
  • 我确实有很长的列表,所以感谢重定向到 trieregex。
  • 看起来“123redred”返回True,我知道这是不应该的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
相关资源
最近更新 更多