【问题标题】:What could be the better approach for pattern match and replace in Python?在 Python 中进行模式匹配和替换的更好方法是什么?
【发布时间】:2019-10-16 22:31:54
【问题描述】:

我有一个包含术语和句子的平面文件。如果在句子中找到任何术语,我需要附加 |present (term|present)。基本上,模式匹配(不区分大小写)和追加 |present。此外,我们需要保留与句子中相同的大小写。哪种方法在 Python 中可行且更快。我尝试使用 Oracle 正则表达式,处理 70k 条记录需要几天时间。

现在我正在使用下面的代码。有没有更好的方法。而且使用当前方法,它适用于 50 条记录,但 df['words'] 在运行整个 70k 记录时为空。不知道是什么原因。

from pandas import DataFrame

df = {'term': ['Ford', 'EXpensive', 'TOYOTA', 'Mercedes Benz', 'electric', 'cars'],
        'sentence': ['Ford is less expensive than Mercedes Benz.' ,'toyota, hyundai mileage is good compared to ford','tesla is an electric-car','toyota too has electric cars','CARS','CArs are expensive.']
        }
from pandas import DataFrame
import re
df = DataFrame(df,columns= ['term','sentence'])

pattern = "|".join(f"\w*(?<![A-Za-z-;:,/|]){i}\\b" for i in df["term"])

df["words"]= df['sentence'].str.findall(pattern, flags=re.IGNORECASE)

def replace_values(row):
    if len(row.words)>0:
        pat = r"(\b"+"|".join(row.words) +r")(\b)"
        row.sentence = re.sub(pat, "\\1|present\\2", row.sentence)
    return row

df = df.apply(replace_values, axis=1)


【问题讨论】:

  • 你注意到你的pattern了吗?这是非常低效的。此外,术语可以是空格分隔的短语,因此您的单词边界方法将部分起作用,而无需先对术语进行排序。例如。如果您同时拥有Mercedes BenzMercedes,则永远不会匹配前者。
  • 请解释你为什么选择\w*(?&lt;![A-Za-z-;:,/|])而不是普通的\b
  • 只是为了确保像 Cars 这样的特殊字符旁边的术语;替换时考虑。
  • 所以,基本上,\b(?:term1|term2)\b 应该在排序后工作。
  • 意思是添加 \b(?:term1|term2)\b 作为模式的一部分?

标签: python regex replace


【解决方案1】:

您的模式非常低效,因为它有许多未锚定的替代方案,以相同的模式开始,可以回溯很多。此外,您要使用的单词边界还需要按长度降序对术语进行排序,以找到最长的术语以防它们重叠(例如,MercededMercedes Benz)。

所以,使用

pattern = r'(?i)\b(?:{})\b'.format('|'.join(sorted(df["term"],key=len,reverse=True)))

或者,如果您在条款中有特殊字符,

pattern = r'(?i)(?<!\w)(?:{})(?!\w)'.format('|'.join(map(re.escape, sorted(df["term"],key=len,reverse=True))))

然后

df["words"]= df["sentence"].str.findall(pattern)
df["sentence"].replace(pattern, r"\g<0>|present", inplace=True, regex=True)

该模式看起来像(?i)\b(?:Mercedes Benz|EXpensive|electric|TOYOTA|Ford|cars)\b,它将匹配 - 由于(?i),以不区分大小写的方式 - 整个单词Mercedes BenzEXpensiveelectricTOYOTAFord,@ 987654335@ 和df["sentence"].str.findall(pattern) 中,它将找到模式匹配的所有非重叠出现,并且在df["sentence"].replace(pattern, r"\g&lt;0&gt;|present", inplace=True, regex=True) 中,所有匹配都将被替换为它们自己(\g&lt;0&gt; 是对整个匹配的反向引用)+ |present 附加给他们。

【讨论】:

  • 谢谢。让我试试。有没有办法我们可以排除几个特殊字符旁边的替换?比如说,我们有一个术语方式,而句子有四个方式。替换不应发生,因为它是子字符串。或者如果该术语是 URL ford.com 的一部分。这只会弄乱现有的 URL。
  • @ABY 那是您认为单词边界的问题。如果您希望它是除了左侧的- 之外的任何非单词字符,只需在第一个\b 之后添加(?&lt;!-)。如果您需要空白边界,请使用r'(?i)(?&lt;!\S)(?:{})(?!\S)'.format('|'.join(map(re.escape, sorted(df["term"],key=len,reverse=True))))。请注意,如果您有数千个术语,则需要创建一个regex trie
  • :) 你的解释真的很有帮助。再次感谢。我发布了另一个问题(58428167),这是对此的轻微修改。如果可能,感谢任何帮助。
猜你喜欢
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 2012-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多