gpt4 book ai didi

python - 用 Pandas 快速删除标点符号

转载 作者:太空宇宙 更新时间:2023-11-03 20:48:07 27 4
gpt4 key购买 nike

这是一个自我回答的帖子。下面,我概述了NLP域中的一个常见问题,并提出了一些有效的方法来解决它。

通常需要在文本清理和预处理期间删除标点符号。标点符号定义为string.punctuation中的任何字符:

>>> import string
string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'


这是一个足够普遍的问题,已经在广告恶心之前提出过。最惯用的解决方案使用pandas str.replace。但是,对于涉及大量文本的情况,可能需要考虑使用性能更高的解决方案。

处理成千上万条记录时, str.replace的哪些性能好替代品是什么?

最佳答案

设定

为了演示的目的,让我们考虑一下这个DataFrame。

df = pd.DataFrame({'text':['a..b?!??', '%hgh&12','abc123!!!', '$$$1234']})
df
text
0 a..b?!??
1 %hgh&12
2 abc123!!!
3 $$$1234


下面,我按照性能从高到低的顺序逐一列出了替代方案

str.replace

包括此选项是为了建立默认方法,作为比较其他性能更高的解决方案的基准。

这使用了熊猫内置的 str.replace函数,该函数执行基于正则表达式的替换。

df['text'] = df['text'].str.replace(r'[^\w\s]+', '')




df
text
0 ab
1 hgh12
2 abc123
3 1234


这很容易编码,可读性很强,但是很慢。



regex.sub

这涉及使用 sub库中的 re函数。预编译正则表达式模式以提高性能,并在列表推导内调用 regex.sub。如果您可以节省一些内存,请事先将 df['text']转换为列表,这样您将获得一点点性能提升。

import re
p = re.compile(r'[^\w\s]+')
df['text'] = [p.sub('', x) for x in df['text'].tolist()]




df
text
0 ab
1 hgh12
2 abc123
3 1234


注意:如果您的数据具有NaN值,则此方法(以及下面的下一个方法)将无法正常工作。请参阅“其他注意事项”部分。



str.translate

python的 str.translate函数是用C实现的,因此非常快。

这是如何工作的:


首先,使用您选择的单个(或多个)字符分隔符将所有字符串连接在一起以形成一个巨大的字符串。您必须使用可以保证不会属于数据内部的字符/子字符串。
对大字符串执行 str.translate,删除标点符号(不包括步骤1中的分隔符)。
在步骤1中用于连接的分隔符上拆分字符串。结果列表的长度必须与初始列的长度相同。


这里,在此示例中,我们考虑管道分隔符 |。如果数据包含管道,则必须选择另一个分隔符。

import string

punct = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{}~' # `|` is not present here
transtab = str.maketrans(dict.fromkeys(punct, ''))

df['text'] = '|'.join(df['text'].tolist()).translate(transtab).split('|')




df
text
0 ab
1 hgh12
2 abc123
3 1234




性能

到目前为止, str.translate表现最好。请注意,下图包含 MaxU's answer的另一个变体 Series.str.translate

(有趣的是,我第二次再次执行此操作,结果与以前略有不同。在第二次运行中,似乎 re.sub胜过 str.translate的数据量很小。)
enter image description here

使用 translate存在固有的风险(尤其是自动确定使用哪个分隔符的过程很重要的问题),但是权衡是值得的。



其他注意事项

使用列表理解方法处理NaN;请注意,只有您的数据没有NaN时,此方法(以及下一个方法)才有效。处理NaN时,您必须确定非空值的索引并仅替换它们。尝试这样的事情:

df = pd.DataFrame({'text': [
'a..b?!??', np.nan, '%hgh&12','abc123!!!', '$$$1234', np.nan]})

idx = np.flatnonzero(df['text'].notna())
col_idx = df.columns.get_loc('text')
df.iloc[idx,col_idx] = [
p.sub('', x) for x in df.iloc[idx,col_idx].tolist()]

df
text
0 ab
1 NaN
2 hgh12
3 abc123
4 1234
5 NaN


处理DataFrames;如果要处理DataFrame,其中每一列都需要替换,则过程很简单:

v = pd.Series(df.values.ravel())
df[:] = translate(v).values.reshape(df.shape)


要么,

v = df.stack()
v[:] = translate(v)
df = v.unstack()


请注意, translate函数在下面用基准测试代码定义。

每个解决方案都需要权衡取舍,因此决定哪种解决方案最适合您的需求将取决于您愿意付出的代价。两个非常常见的注意事项是性能(我们已经看过)和内存使用情况。 str.translate是需要大量内存的解决方案,因此请谨慎使用。

另一个考虑因素是您的正则表达式的复杂性。有时,您可能希望删除任何不是字母数字或空格的内容。有时,您将需要保留某些字符,例如连字符,冒号和句子终止符 [.!?]。明确指定这些选项会给您的正则表达式增加复杂性,进而可能影响这些解决方案的性能。确保您测试这些解决方案
在决定使用什么之前先对数据进行检查。

最后,此解决方案将删除unicode字符。您可能想调整您的正则表达式(如果使用基于正则表达式的解决方案),否则请直接使用 str.translate

要获得更高的性能(对于更大的N),请查看 Paul Panzer的答案。



附录

功能

def pd_replace(df):
return df.assign(text=df['text'].str.replace(r'[^\w\s]+', ''))


def re_sub(df):
p = re.compile(r'[^\w\s]+')
return df.assign(text=[p.sub('', x) for x in df['text'].tolist()])

def translate(df):
punct = string.punctuation.replace('|', '')
transtab = str.maketrans(dict.fromkeys(punct, ''))

return df.assign(
text='|'.join(df['text'].tolist()).translate(transtab).split('|')
)

# MaxU's version (https://stackoverflow.com/a/50444659/4909087)
def pd_translate(df):
punct = string.punctuation.replace('|', '')
transtab = str.maketrans(dict.fromkeys(punct, ''))

return df.assign(text=df['text'].str.translate(transtab))




绩效基准代码

from timeit import timeit

import pandas as pd
import matplotlib.pyplot as plt

res = pd.DataFrame(
index=['pd_replace', 're_sub', 'translate', 'pd_translate'],
columns=[10, 50, 100, 500, 1000, 5000, 10000, 50000],
dtype=float
)

for f in res.index:
for c in res.columns:
l = ['a..b?!??', '%hgh&12','abc123!!!', '$$$1234'] * c
df = pd.DataFrame({'text' : l})
stmt = '{}(df)'.format(f)
setp = 'from __main__ import df, {}'.format(f)
res.at[f, c] = timeit(stmt, setp, number=30)

ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");

plt.show()

关于python - 用 Pandas 快速删除标点符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56434560/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com