如何从列中的字符串中删除不需要的部分?
在最初的问题发布6年后,pandas现在有很多“向量化”的字符串函数,可以简洁地执行这些字符串操作。
这个答案将探讨这些字符串函数中的一些,建议更快的替代方案,并在最后进行计时比较。
.str.replace
指定要匹配的子字符串/模式,以及要替换它的子字符串。
pd.__version__
# '0.24.1'
df
time result
1 09:00 +52A
2 10:00 +62B
3 11:00 +44a
4 12:00 +30b
5 13:00 -110a
df['result'] = df['result'].str.replace(r'\D', '')
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
如果需要将结果转换为整数,可以使用Series.astype,
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
如果你不想就地修改df,使用DataFrame.assign:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
.str.extract
用于提取想要保留的子字符串。
df['result'] = df['result'].str.extract(r'(\d+)', expand=False)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
使用提取,必须指定至少一个捕获组。expand=False将返回一个包含从第一个捕获组捕获的项目的Series。
.str。分裂和。str.get
假设你所有的字符串都遵循这个一致的结构,拆分就可以工作。
# df['result'] = df['result'].str.split(r'\D').str[1]
df['result'] = df['result'].str.split(r'\D').str.get(1)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
如果你正在寻找一个通用的解决方案,不建议。
如果你对简洁易读的str感到满意
以上基于访问器的解决方案,可以在此停止。然而,如果你是
对更快、性能更好的替代方案感兴趣,请继续阅读。
优化:列表推导式
在某些情况下,列表推导式应该优于pandas字符串函数。原因是字符串函数本质上很难向量化(真正意义上的词),所以大多数字符串和正则表达式函数只是循环的包装器,开销更大。
我的文章,熊猫中的for循环真的很糟糕吗?我什么时候该在乎?,将更详细地介绍。
str.replace选项可以使用re.sub重写
import re
# Pre-compile your regex pattern for more performance.
p = re.compile(r'\D')
df['result'] = [p.sub('', x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
str.extract的例子可以使用re.search的列表理解来重写,
p = re.compile(r'\d+')
df['result'] = [p.search(x)[0] for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
如果可能存在nan或不匹配,则需要重新编写上面的代码,以包含一些错误检查。我用函数来做这个。
def try_extract(pattern, string):
try:
m = pattern.search(string)
return m.group(0)
except (TypeError, ValueError, AttributeError):
return np.nan
p = re.compile(r'\d+')
df['result'] = [try_extract(p, x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
我们还可以使用列表推导式重写@eumiro和@MonkeyButter的答案:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
And,
df['result'] = [x[1:-1] for x in df['result']]
同样的规则用于处理nan等。
性能比较
使用perfplot生成的图形。完整的代码清单,供您参考。下面列出了相关功能。
其中一些比较是不公平的,因为它们利用了OP数据的结构,但从中可以得到你想要的东西。需要注意的一点是,每个列表理解函数都比其等效的pandas变体更快或具有可比性。
功能
def eumiro(df):
return df.assign(
result=df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC')))
def coder375(df):
return df.assign(
result=df['result'].replace(r'\D', r'', regex=True))
def monkeybutter(df):
return df.assign(result=df['result'].map(lambda x: x[1:-1]))
def wes(df):
return df.assign(result=df['result'].str.lstrip('+-').str.rstrip('aAbBcC'))
def cs1(df):
return df.assign(result=df['result'].str.replace(r'\D', ''))
def cs2_ted(df):
# `str.extract` based solution, similar to @Ted Petrou's. so timing together.
return df.assign(result=df['result'].str.extract(r'(\d+)', expand=False))
def cs1_listcomp(df):
return df.assign(result=[p1.sub('', x) for x in df['result']])
def cs2_listcomp(df):
return df.assign(result=[p2.search(x)[0] for x in df['result']])
def cs_eumiro_listcomp(df):
return df.assign(
result=[x.lstrip('+-').rstrip('aAbBcC') for x in df['result']])
def cs_mb_listcomp(df):
return df.assign(result=[x[1:-1] for x in df['result']])