安装程序
为了演示的目的,让我们考虑一下这个数据框架。
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.替换
执行基于regex的替换的函数。
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()]
东风
文本
0 AB
1小时12分
2台ABC123
3 1234号
注:
如果数据具有NaN值,则此方法(以及下面的下一个方法)将无法正常工作。参见“的章节
其他注意事项
“。
str.translate
巨蟒的
str.翻译
函数在C中实现,因此
非常快
.
工作原理是:
-
首先,将所有的字符串连接在一起形成一个
巨大的
使用单个(或多个)字符的字符串
分离器
那个
你
选择。你
必须
使用可以保证不属于数据的字符/子字符串。
-
执行
str.翻译
在大字符串上,删除标点符号(步骤1中的分隔符除外)。
-
拆分步骤1中用于联接的分隔符上的字符串。结果列表
必须
与初始列的长度相同。
这里,在这个例子中,我们考虑管道分隔符
|
. 如果数据包含管道,则必须选择其他分隔符。
import string
punct = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{}~' # `|` is not present here
transtab = str.maketrans(dict.fromkeys(punct, ''))
df['text'] = '|'.join(df['text'].tolist()).translate(transtab).split('|')
东风
文本
0 AB
1小时12分
2台ABC123
3 1234号
性能
str.翻译
迄今为止表现最好。请注意,下面的图表包含另一个变量
Series.str.translate
从
MaxU's answer
.
(有趣的是,我第二次重新运行,结果与以前略有不同。在第二次跑步中,似乎
re.sub
赢得了胜利
str.翻译
对于非常少量的数据。)
使用时存在固有风险
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
处理数据帧;
如果你在处理数据帧,
每一个
列需要替换,过程很简单:
v = pd.Series(df.values.ravel())
df[:] = translate(v).values.reshape(df.shape)
或者,
v = df.stack()
v[:] = translate(v)
df = v.unstack()
注意,
翻译
功能在下面的标准代码中定义。
每一个解决方案都有权衡,所以决定什么解决方案最适合您的需求取决于您愿意牺牲什么。两个非常常见的考虑因素是性能(我们已经看到)和内存使用。
str.翻译
是一个内存不足的解决方案,因此请谨慎使用。
另一个考虑因素是regex的复杂性。有时,您可能希望删除任何不是字母数字或空格的内容。其他情况下,您需要保留某些字符,如连字符、冒号和句子结束符。
[.!?]
. 明确地指定这些参数会增加regex的复杂性,这反过来可能会影响这些解决方案的性能。确保测试这些解决方案
在决定使用什么之前,请先查看您的数据。
最后,Unicode字符将随此解决方案一起删除。您可能需要调整regex(如果使用基于regex的解决方案),或者只需
str.翻译
否则。
为偶数
更多
性能(对于较大的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()