【问题标题】:String replace with condition字符串替换为条件
【发布时间】:2018-04-28 19:34:17
【问题描述】:

我有两个熊猫数据框。一个包含文本,另一个包含一组我想在文本中搜索和替换的术语。我有一种方法可以做到这一点,但是我想添加条件。条件是,如果该术语之前最多包含三个单词“no”或“none”,则不替换。

在下面的示例中,根据上述条件错误地替换了 ID 2。

示例文本:

d = {'ID': [1, 2, 3], 'Text': ['here is some random text', 'no such random text, none here', 'more random text']}
text_df = pd.DataFrame(data=d)

示例术语:

d = {'Replace_item': ['<RANDOM_REPLACED>', '<HERE_REPLACED>', '<SOME_REPLACED>'], 'Text': ['random', 'here', 'some']}
replace_terms_df = pd.DataFrame(data=d)

替换术语的方法(ID 2 根据条件不正确):

text_df['Text'] = [z.replace(x, y) for (x, y, z) in zip(replace_terms_df.Text, replace_terms_df.Replace_item, text_df.Text)]

目标数据框(考虑条件):

d = {'ID': [1, 2, 3], 'Text': ['<HERE_REPLACED> is <SOME_REPLACED> <RANDOM_REPLACED> text', 'no such random text, none here', 'more  <RANDOM_REPLACED> text']}
target_df = pd.DataFrame(data=d)

请询问您是否需要澄清。谢谢。

【问题讨论】:

  • 你只是替换了第一行的'random',第二行的'here'和第三行的'some'。这是预期的,还是您想替换每一行的替换项目?
  • 条件应该允许替换字符串,前提是“not”或“none”不在替换词的三个单词内。在 ID 2 的情况下,该术语的三个单词内有一个“not”和一个“none”,因此不应替换它们。我的示例不起作用,只是替换 - 这就是我想要帮助的:)
  • 但是我仍然不明白你想要的输出,使用上面的代码,第一行的输出是'here is some &lt;RANDOM_REPLACED&gt; text' 你想要那个而不是'&lt;HERE_REPLACED&gt; is &lt;SOME_REPLACED&gt; &lt;RANDOM_REPLACED&gt; text'?如果你真的想要第一个,为什么你有两个单独的数据框?这些信息不应该合二为一吗?除了行索引之外,这两个dfs 之间没有链接 ID,这并不总是有意的。
  • 我在我的问题中为您添加了我的目标数据框的示例。我希望这能提供清晰的信息。

标签: python pandas nlp


【解决方案1】:

使用正则表达式检查以下代码:

import re

# set up the regex pattern
# the words which should be skipped, must be whole word and case-insensitive
ptn_to_skip = re.compile(r'\b(?:no|none)\b', re.IGNORECASE)

# the pattern for mapping
# Note: any regex meta charaters need to be escaped, or it will fail.
ptn_to_map = re.compile(r'\b(' + '|'.join(replace_terms_df.Text.tolist()) + r')\b')

# map from text to Replace_item
terms_map = replace_terms_df.set_index('Text').Replace_item

def adjust_text(x):
    # if 1 - 3 ptn_to_skip found, return x, 
    # otherwise, map the matched group \1 with terms_map
    if 0 < len(ptn_to_skip.findall(x)) <= 3:
        return x
    else:
        return ptn_to_map.sub(lambda y: terms_map[y.group(1)], x)

# do the conversion:
text_df['new_text'] = text_df.Text.apply(adjust_text)

一些注意事项:

  • 我将replace_terms_df.Text 中的文本转换为正则表达式。默认文本都是没有正则表达式元字符的纯文本。
  • 如果有任何正则表达式元字符,如'$'、']' 等,您将不得不转义它们。正则表达式往往很慢,尤其是元字符,如果您有大量数据,请不要向您推荐此解决方案。

更新:

增加了一个新的逻辑,首先检查excluded-words ['no', 'none'],如果匹配,然后找到接下来的0-3个本身不是excluded-words的单词,将它们保存到\1,实际匹配的搜索词将保存在 \2 中。然后在正则表达式替换部分,以不同的方式处理它们。

以下是新代码:

import re

# pattern to excluded words (must match whole-word and case insensitive)
ptn_to_excluded = r'\b(?i:no|none)\b'

# ptn_1 to match the excluded-words ['no', 'none'] and the following maximal 3 words which are not excluded-words
# print(ptn_1)  -->    \b(?i:no|none)\b\s*(?:(?!\b(?i:no|none)\b)\S+\s*){,3}
# where (?:(?!\b(?i:no|none)\b)\S+\s*) matches any words '\S+' which is not in ['no', 'none'] followed by optional white-spaces
# {,3} to specify matches up to 3 words 
ptn_1 = r'{0}\s*(?:(?!{0})\S+\s*){{,3}}'.format(ptn_to_excluded)

# ptn_2 is the list of words you want to convert with your terms_map
# print(ptn_2)    -->    \b(?:random|here|some)\b
ptn_2 = r'\b(?:' + '|'.join(replace_terms_df.Text.tolist()) + r')\b'

# new pattern based on the alternation using ptn_1 and ptn_2
# regex:  (ptn_1)|(ptn_2)
new_ptn = re.compile('({})|({})'.format(ptn_1, ptn_2))

# map from text to Replace_item
terms_map = replace_terms_df.set_index('Text').Replace_item

# regex function to do the convertion
def adjust_map(x):
    return new_ptn.sub(lambda m:  m.group(1) or terms_map[m.group(2)], x)

# do the conversion:
text_df['new_text'] = text_df.Text.apply(adjust_map)

说明:

我定义了两个子模式:

  • ptn_1:尝试匹配你想要排除的单词,即单词'no','none'后跟最多3个不在['no','none']中的单词
  • ptn_2:尝试根据 replace_terms_df 匹配您要转换的单词之一。

它是如何工作的:

  • 使用替换“|”,正则表达式引擎将确保 ptn_1 在 ptn_2 之前匹配,如果两者都不匹配,则保留原始文本。
  • 匹配的ptn_1文本将保存在m.group(1)中,ptn_2结果保存到m.group(2)中
  • 在替换部件中。如果 m.group(1) 不是 Empty(意味着 ptn_1 匹配)则返回 m.group(1) (因此这部分匹配未触及),否则返回 terms_map[y.group(2)]

以下一些测试:

In []: print(new_ptn)
re.compile('(\\b(?i:no|none)\\b\\s*(?:(?!\\b(?i:no|none)\\b)\\S+\\s*){,3})|(\\b(random|here|some)\\b)')

In[]: for i in [
    'yes, no such a random text'
  , 'yes, no such a a random text'
  , 'no no no such a random text no such here here here no'
 ]: print('{}:\n  [{}]'.format(i, adjust_map(i)))
...:
yes, no such a random text:
  [yes, no such a random text]
yes, no such a a random text:
  [yes, no such a a <RANDOM_REPLACED> text]
no no no such a random text no such here here here no:
  [no no no such a random text no such here here <HERE_REPLACED> no]

让我知道这是否有效。

更多考虑:

  • 在 ptn_1 中,'\S+' 用于定义一个 WORD,如果其中一个单词是 ',none' 之类的,则会出现问题,前面的 'comma' 将让它跳过(?!\b(?:no|none)) 测试。
  • 其实应该排除',no', '"none"'吗?这将影响单词的计数方式。修改ptn_to_excluded 就足够了。

【讨论】:

  • 感谢 jxc,这是一个有趣的方法!但是,当我一直在测试它时,它似乎可以查看整个文本,而条件是“不”或“无”之前出现三个单词。
  • 嗨,@avocet。我修改了逻辑以匹配文本并检查之前的单词(最多 3 个单词),并且仅在没有看到排除的单词时才进行映射。正则表达式实际上从那些排除的单词(如果存在)向后检查,但应该具有相同的效果。让我知道它是否有效。
  • 嗨@jxc 感谢这个出色的解决方案!也为详细解释 - 非常感谢您的专业知识
【解决方案2】:

从创建替换项的字典开始会有所帮助。您可以执行以下操作:

# create a dict
make_dict = replace_terms_df.set_index('Text')['Replace_item'].to_dict()

# this function does the replacement work
def g_val(strin, dic):

    d = []
    if 'none' in strin or 'no' in strin:
        return strin
    else:
        for i in strin.split():
            if i not in dic:
                d.append(i)
            else:
                d.append(dic[i])
        return ' '.join(d)

## apply the function
text_df['new_text'] = text_df['Text'].apply(lambda x: g_val(x, dic=make_dict))

## check output
print(text_df['new_text'])

0    <HERE_REPLACED> is <SOME_REPLACED> <RANDOM_REP...
1                       no such random text, none here
2                          more <RANDOM_REPLACED> text

说明

在函数中,我们正在做:
1.如果字符串包含none或no,我们将字符串原样返回。
2.如果不包含none或no,则检查该词是否在字典中可用,如果是,则返回替换的值,否则返回现有值。

【讨论】:

  • 但条件不仅仅是“否”或“无”一词在任何地方。特别是如果这些词在替换词的 3 个词之内。这将无法正确解决 'No, I should replace random text here' 之类的问题
  • 感谢您的回答 YOLO!但是 ALollz 是正确的,该术语必须在替换前的 3 个单词内。
猜你喜欢
  • 2021-10-08
  • 2014-03-13
  • 2014-08-17
  • 2015-02-02
  • 2015-11-06
  • 2011-10-03
  • 2012-06-12
  • 2021-10-19
相关资源
最近更新 更多