删除列中字符串中不需要的部分
- 2025-01-03 08:41:00
- admin 原创
- 99
问题描述:
我正在寻找一种有效的方法来从 DataFrame 列中的字符串中删除不需要的部分。
数据如下:
time result
1 09:00 +52A
2 10:00 +62B
3 11:00 +44a
4 12:00 +30b
5 13:00 -110a
我需要将这些数据修剪为:
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
我尝试.str.lstrip('+-')
过 . str.rstrip('aAbBcC')
,但出现错误:
TypeError: wrapper() takes exactly 1 argument (2 given)
任何指点都将不胜感激!
解决方案 1:
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
解决方案 2:
如何从列中的字符串中删除不需要的部分?
在最初的问题发布六年后,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', '', regex=True)
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', '', regex=True).astype(int)
df.dtypes
time object
result int64
dtype: object
如果您不想df
就地修改,请使用DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'D', '', regex=True))
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
使用 时extract
,需要指定至少一个捕获组。expand=False
将返回一个包含来自第一个捕获组的捕获项的系列。
.str.split
并且
拆分有效,假设所有字符串都遵循这种一致的结构。.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 字符串函数更受青睐。原因是字符串函数本质上很难矢量化(真正意义上),因此大多数字符串和正则表达式函数只是循环的包装器,开销更大。
我的文章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']]
和,
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']])
解决方案 3:
我会使用 pandas 替换函数,非常简单且功能强大,因为您可以使用正则表达式。下面我使用正则表达式 \D 来删除任何非数字字符,但显然您可以使用正则表达式发挥创造力。
data['result'].replace(regex=True,inplace=True,to_replace=r'D',value=r'')
解决方案 4:
在特定情况下,如果您知道要从数据框列中删除的位置数,则可以在 lambda 函数中使用字符串索引来删除这些部分:
最后一个字符:
data['result'] = data['result'].map(lambda x: str(x)[:-1])
前两个字符:
data['result'] = data['result'].map(lambda x: str(x)[2:])
解决方案 5:
这里有一个错误:目前无法将参数传递给str.lstrip
和str.rstrip
:
http://github.com/pydata/pandas/issues/2411
编辑:2012-12-07 这现在可以在 dev 分支上运行:
In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]:
1 52
2 62
3 44
4 30
5 110
Name: result
解决方案 6:
一种非常简单的方法是使用extract
方法来选择所有数字。只需为其提供'd+'
提取任意数字的正则表达式即可。
df['result'] = df.result.str.extract(r'(d+)', expand=True).astype(int)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
解决方案 7:
假设你的 DF 在数字之间也有这些额外的字符。最后一个条目。
result time
0 +52A 09:00
1 +62B 10:00
2 +44a 11:00
3 +30b 12:00
4 -110a 13:00
5 3+b0 14:00
您可以尝试 str.replace 不仅从开始和结束处删除字符,还可以从中间删除字符。
DF['result'] = DF['result'].str.replace('+|a|b|-|A|B', '')
输出:
result time
0 52 09:00
1 62 10:00
2 44 11:00
3 30 12:00
4 110 13:00
5 30 14:00
解决方案 8:
我经常使用列表推导来完成这些类型的任务,因为它们通常更快。
执行此类操作(即修改 DataFrame 中系列的每个元素)的各种方法之间的性能可能会有很大差异。通常,列表理解可能是最快的 - 请参阅下面有关此任务的代码竞赛:
import pandas as pd
#Map
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
10000 loops, best of 3: 187 µs per loop
#List comprehension
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in data['result']]
10000 loops, best of 3: 117 µs per loop
#.str
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].str.lstrip('+-').str.rstrip('aAbBcC')
1000 loops, best of 3: 336 µs per loop
解决方案 9:
尝试使用正则表达式:
import re
data['result'] = data['result'].map(lambda x: re.sub('[-+A-Za-z]',x)
解决方案 10:
当数据维度很大时,使用“str.replace”比 lambda 和 map 更快:
your_data["result"]=your_data["result"].str.replace("+","")
your_data["result"]=your_data["result"].str.replace("-","")
解决方案 11:
df['result']=df['result'].apply(lambda x:x.lstrip('-+').rstrip('abcABC'))