我正在寻找一种从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)
任何指针都将非常感谢!
答案 0 :(得分:123)
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
答案 1 :(得分:37)
我使用pandas替换功能,非常简单和强大,因为你可以使用正则表达式。下面我使用正则表达式\ D删除任何非数字字符,但显然你可以使用正则表达式获得相当的创意。
data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')
答案 2 :(得分:27)
在您知道要从dataframe列中删除的位置数的特定情况下,您可以在lambda函数中使用字符串索引来删除这些部分:
最后一个字符:
data['result'] = data['result'].map(lambda x: str(x)[:-1])
前两个字符:
data['result'] = data['result'].map(lambda x: str(x)[2:])
答案 3 :(得分:16)
这里有一个错误:目前无法将参数传递给str.lstrip
和str.rstrip
:
http://github.com/pydata/pandas/issues/2411
编辑:2012-12-07现在可以在开发分支上使用:
In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]:
1 52
2 62
3 44
4 30
5 110
Name: result
答案 4 :(得分:10)
一种非常简单的方法是使用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
答案 5 :(得分:9)
如何从列的字符串中删除不需要的部分?
最初提出问题的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
使用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
感到满意 上面基于访问器的解决方案,您可以在这里停止。但是,如果您是 对更快,更高性能的替代品感兴趣,请继续阅读。
在某些情况下,列表理解应优于熊猫字符串函数。原因是因为字符串函数本来就很难向量化(从字面意义上来说),所以大多数字符串和正则表达式函数只是循环包装,开销更大。
我的文章For loops with pandas - When should I care?更加详细。
可以使用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
模块生成的。
设置和时间
df_ = pd.DataFrame({
'time': ['09:00', '10:00', '11:00', '12:00', '13:00'],
'result': ['+52A', '+62B', '+44a', '+30b', '-110a']
})
kernels = [
eumiro, coder375, monkeybutter, wes, cs1, cs2_ted,
cs1_listcomp, cs2_listcomp, cs_eumiro_listcomp, cs_mb_listcomp
]
perfplot.show(
setup=lambda n: pd.concat([df_] * n, ignore_index=True),
kernels=kernels,
labels=[k.__name__ for k in kernels],
n_range=[2**k for k in range(1, 11)],
xlabel='N',
logy=True,
equality_check=lambda x, y: (x == y).all(axis=None),
)
该图绘制了相对性能; y轴对数增加。这些比较中的一些比较不公平,因为它们利用了OP数据的结构,但从中得到了好处。要注意的一件事是,每个列表理解功能比其等效的pandas函数要快。
功能
import perfplot
import pandas as pd
import re
p1 = re.compile(r'\D')
p2 = re.compile(r'\d+')
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']])
答案 6 :(得分:7)
我经常对这些类型的任务使用列表推导,因为它们通常更快。
执行此类操作的各种方法(即修改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
答案 7 :(得分:1)
假设您的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 :(得分:0)
尝试使用正则表达式:
import re
data['result'] = data['result'].map(lambda x: re.sub('[-+A-Za-z]',x)