根据另一列匹配部分文本

Posted

技术标签:

【中文标题】根据另一列匹配部分文本【英文标题】:match part of text based on another column 【发布时间】:2020-10-28 02:52:42 【问题描述】:

我有这个数据集;

text                                    num

test one 3.5 and 60 test tow            3.5/60
test one 3/4 test tow                     3/4
test one 5.0 test 10 tow                  5.0

如果数字匹配,我需要从文本列中删除数字 所以我这样做了:

df['text']=[re.sub(''.format(number), '', the_text) for the_text, number in zip(df['text'], df['num'])]

结果变成这样

text                                    num

test one 3.5 and 60 test tow            3.5/60
test one test tow                         3/4
test one test 10 tow                      5.0

如您所见,除了第一行之外,匹配的数字已被删除,因为它不是完全匹配的。 所以我想使用包含函数匹配或任何类似于匹配部分文本的东西。

我希望第一行会变成

测试一个并测试两个

我已经这样做了,但出现错误:

[re.sub(r"\b\b".format(word), "", the_text) for the_text, word in zip(df['text'], word='/'.join([r''.format(words) for words in df['num']]) )]

TypeError: zip() takes no keyword arguments

有什么帮助吗?

【问题讨论】:

如果您说您遇到了错误,请始终发布错误消息。 代替''.format(number),试试r'(?<!\d)(?<!\d\.)(?:)(?!\.?\d)'.format('|'.join([re.escape(x) for x in number.split('/')])) @WiktorStribiżew 不起作用 @JoelFan 谢谢你 你的示例数据框是df = pd.DataFrame('text': ['test one 3.5 and 60 test tow','test one 3/4 test tow', 'test one 5.0 test tow'], 'num': ['3.5/60', '3/4', '5.0'])吗? 【参考方案1】:

这行得通:

import re

txt='''\
text                                    num

test one 3.5 and 60 test tow            3.5/60
test one 3/4 test tow                     3/4
test one 5.0 test tow                     5.0'''

for line in txt.splitlines():
    m=re.search(r'^(.*?[ \t]2,(?=\d))([0-9.\/]+)$', line)
    if m:
        a,_,b=m.group(2).partition('/')
        if re.search(fr'\bm.group(2)\b', m.group(1)):
            l=len(m.group(1))
            s=re.sub(fr'[ ]?\bm.group(2)\b', '', m.group(1))
            line=s+' '*(l-len(s))+m.group(2)
        elif re.search(fr'a[^/]+b', m.group(1)):
            l=len(m.group(1))
            s=re.sub(fr'[ ]?\ba\b','',m.group(1))
            s=re.sub(fr'[ ]?\bb\b','',s)
            line=s+' '*(l-len(s))+m.group(2)
                
    print(line)     

打印:

text                                    num

test one and test tow                   3.5/60
test one test tow                         3/4
test one test tow                         5.0

【讨论】:

【参考方案2】:

你可以使用

df['text'] = df.apply(lambda x: re.sub(r'(?<!\d)(?<!\d\.)(?:|)(?!\.?\d)'.format(re.escape(x['num']), '|'.join([re.escape(l) for l in x['num'].split('/')])), '', x['text']), axis=1)

感谢df.applyaxis=1,我们遍历所有行。

正则表达式是根据num 列中的值动态生成的,并应用于text 列。

r'(?&lt;!\d)(?&lt;!\d\.)(?:|)(?!\.?\d)'.format(re.escape(x['num']), '|'.join([re.escape(l) for l in x['num'].split('/')])) 创建一个类似的正则表达式

(?<!\d)(?<!\d\.)(?:3/4|3|4)(?!\.?\d)

分别匹配num 列中的完整值和/ 之间的数字。

(?&lt;!\d)(?&lt;!\d\.) 是一个后向序列,如果在当前位置的左边有一个数字或一个数字 + 点,则匹配失败,如果有一个数字或一个点 +,(?!\.?\d) 则匹配失败当前位置右侧的数字有效地禁止数字匹配较长的数字。

【讨论】:

【参考方案3】:

创建一个数字列表并添加/

nums = '|'.join(df['num'].tolist()).replace('/', '|') + '|/'
nums
'3.5|60|3|4|5.0|/'

然后str替换

df['text'].str.replace(nums, '')

0    test one  and  test tow
1         test one  test tow
2         test one  test tow

【讨论】:

请注意,. 是一个特殊的正则表达式元字符,5.0 将匹配 5 05+0 等等。你需要逃避它。此外,没有任何界限,您将冒着将3 替换为43 的风险。 我想传递 num 列,以便每一行都匹配 text 列上的同一行 @WiktorStribiżew 所以如果文本上有任何其他数字,如果不匹配则不会删除

以上是关于根据另一列匹配部分文本的主要内容,如果未能解决你的问题,请参考以下文章

如何查找单元格中是不是包含部分文本并引用另一列数据

将语料库中的名称部分匹配到 Pandas 数据框中另一列中的名称

如果列中的单元格部分包含另一列中的文本

MariaDB/mysql SQL查询问题:我想根据另一列的组值对一列进行部分总和

Pandas:根据字符串的一部分是不是在另一列中的任何位置创建新列

如何检索Oracle中长度不同的文本列的最后一部分(即长度不同的数字)并将其存储在另一列中?