如何根据字典替换熊猫系列中的字符串组,并将值作为列表?

Posted

技术标签:

【中文标题】如何根据字典替换熊猫系列中的字符串组,并将值作为列表?【英文标题】:How to replace group of strings in pandas series based on a dictionary with values as list? 【发布时间】:2019-04-04 06:47:59 【问题描述】:

我在 *** 中找不到基于列表中值的字典进行替换的解决方案。

字典

dct  = "LOL": ["laught out loud", "laught-out loud"],
        "TLDR": ["too long didn't read", "too long; did not read"],
        "application": ["app"]

输入

input_df = pd.DataFrame([("haha too long didn't read and laught out loud :D"),
                         ("laught-out loud so I couldnt too long; did not read"),
                         ("what happened?")], columns=['text'])

预期输出

output_df = pd.DataFrame([("haha TLDR and LOL :D"),
                          ("LOL so I couldnt TLDR"),
                          ("what happened?")], columns=['text'])

编辑

在字典中添加了一个附加条目,即“应用程序”:[“应用程序”]

当前的解决方案给出的输出是“应用了什么?”

请提出修复建议。

【问题讨论】:

【参考方案1】:

构建一个反向映射并将Series.replaceregex=True 一起使用。

mapping = v : k for k, V in dct.items() for v in V
input_df['text'] = input_df['text'].replace(mapping, regex=True)

print(input_df)
                    text
0   haha TLDR and LOL :D
1  LOL so I couldnt TLDR

在哪里,

print(mapping)
'laught out loud': 'LOL',
 'laught-out loud': 'LOL',
 "too long didn't read": 'TLDR',
 'too long; did not read': 'TLDR'

要匹配完整的单词,请为每个单词添加单词边界:

mapping = rf'\bv\b' : k for k, V in dct.items() for v in V
input_df['text'] = input_df['text'].replace(mapping, regex=True)

print(input_df)
                    text
0   haha TLDR and LOL :D
1  LOL so I couldnt TLDR
2         what happened?

在哪里,

print(mapping)
'\\bapp\\b': 'application',
 '\\blaught out loud\\b': 'LOL',
 '\\blaught-out loud\\b': 'LOL',
 "\\btoo long didn't read\\b": 'TLDR',
 '\\btoo long; did not read\\b': 'TLDR'

【讨论】:

太棒了!请为以下问题提出修复建议。在字典“application”中添加了一个附加条目:[“app”] 当前的解决方案给出的输出是“what happlicationened?” @ML_Pro 你的意思是你只希望它匹配整个单词?嗯,在这种情况下,尝试将“app”更改为 r“\bapp\b”,并对要替换的每个字符串执行此操作。这是一个正则表达式单词边界,它只匹配整个单词。 谢谢。但是,我正在从 JSON 文件加载字典。如何使用 python 代码将“app”转换为 r“\bapp\b”?我找不到将字符串转换为原始字符串的函数。接受您的回复作为答案。 优秀。明白了。【参考方案2】:

这就是我要走的路:

import pandas as pd


dct  = "LOL": ["laught out loud", "laught-out loud"],
        "TLDR": ["too long didn't read", "too long; did not read"]
        

input_df = pd.DataFrame([("haha too long didn't read and laught out loud :D"),
       ("laught-out loud so I couldnt too long; did not read")], columns=['text'])

dct_inv = 
for key, vals in dct.items():
    for val in vals:
        dct_inv[val]=key

dct_inv

def replace_text(input_str):
    for key, val in dct_inv.items():
        input_str = str(input_str).replace(key, val)
    return input_str

input_df.apply(replace_text, axis=1).to_frame()

【讨论】:

【参考方案3】:

使用df.apply 和自定义函数

例如:

import pandas as pd


def custReplace(value):
    dct  = "LOL": ["laught out loud", "laught-out loud"],
        "TLDR": ["too long didn't read", "too long; did not read"]
        

    for k, v in dct.items():
        for i in v:
            if i in value:
                value = value.replace(i, k)
    return value

input_df = pd.DataFrame([("haha too long didn't read and laught out loud :D"),
       ("laught-out loud so I couldnt too long; did not read")], columns=['text'])

print(input_df["text"].apply(custReplace))

输出:

0     haha TLDR and LOL :D
1    LOL so I couldnt TLDR
Name: text, dtype: object

dct  = "LOL": ["laught out loud", "laught-out loud"],
        "TLDR": ["too long didn't read", "too long; did not read"]
        

dct =  "(" + "|".join(v) + ")": k for k, v in dct.items()
input_df = pd.DataFrame([("haha too long didn't read and laught out loud :D"),
       ("laught-out loud so I couldnt too long; did not read")], columns=['text'])

print(input_df["text"].replace(dct, regex=True))

【讨论】:

【参考方案4】:

我认为最合乎逻辑的起点是反转你的字典,这样你的键就是你的原始字符串,它映射到你的新字符串的值。您可以手动完成,也可以通过其他一百万种方式完成,例如:

import itertools
dict_rev = dict(itertools.chain.from_iterable([list(zip(v, [k]*len(v))) for k, v in dct.items()]))

这不是超级可读的。或者这个看起来更好的一个,我从另一个答案中偷走了:

dict_rev = v : k for k, V in dct.items() for v in V

这要求字典中的每个值都在一个列表(或其他可迭代的)中,例如"new key": ["single_val"] 否则会爆炸字符串中的每个字符。

然后您可以执行以下操作(基于此处的代码 How to replace multiple substrings of a string?)

import re
rep = dict((re.escape(k), v) for k, v in dict_rev.items())
pattern = re.compile("|".join(rep.keys()))
input_df["text"] = input_df["text"].str.replace(pattern, lambda m: rep[re.escape(m.group(0))])

这种方法的执行速度大约比更简单、更优雅的解决方案快 3 倍:

简单:

%timeit input_df["text"].replace(dict_rev, regex=True)

425 µs ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

更快:

%timeit input_df["text"].str.replace(pattern, lambda m: rep[re.escape(m.group(0))])

160 µs ± 7.78 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

【讨论】:

以上是关于如何根据字典替换熊猫系列中的字符串组,并将值作为列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何按对象计算熊猫组列中的不同值?

熊猫替换/字典缓慢

根据大熊猫中的其他列,用良好的数据替换空字段

用字典替换熊猫系列中的值

如何将字典附加到熊猫数据框?

Pandas:根据另一列的键在现有列上映射字典值以替换 NaN