我希望在 python3 中以有效的方式使用
replace
函数。我拥有的代码正在完成任务,但速度太慢,因为我正在处理大型数据集。因此,每当需要权衡时,我的首要任务是效率而不是优雅。这是我想做的一个玩具:
import pandas as pd
df = pd.DataFrame([[1,2],[3,4],[5,6]], columns = ['1st', '2nd'])
1st 2nd
0 1 2
1 3 4
2 5 6
idxDict= dict()
idxDict[1] = 'a'
idxDict[3] = 'b'
idxDict[5] = 'c'
for k,v in idxDict.items():
df ['1st'] = df ['1st'].replace(k, v)
这给出了
1st 2nd
0 a 2
1 b 4
2 c 6
如我所愿,但这需要太长时间。最快的方法是什么?
编辑:这是一个比 this 更集中、更清晰的问题,其解决方案类似。
map
执行查找:
In [46]:
df['1st'] = df['1st'].map(idxDict)
df
Out[46]:
1st 2nd
0 a 2
1 b 4
2 c 6
避免出现没有有效钥匙可以通过的情况
na_action='ignore'
您也可以使用
df['1st'].replace(idxDict)
,但要回答您有关效率的问题:
时间
In [69]:
%timeit df['1st'].replace(idxDict)
%timeit df['1st'].map(idxDict)
1000 loops, best of 3: 1.57 ms per loop
1000 loops, best of 3: 1.08 ms per loop
In [70]:
%%timeit
for k,v in idxDict.items():
df ['1st'] = df ['1st'].replace(k, v)
100 loops, best of 3: 3.25 ms per loop
因此使用
map
速度快了 3 倍以上
在更大的数据集上:
In [3]:
df = pd.concat([df]*10000, ignore_index=True)
df.shape
Out[3]:
(30000, 2)
In [4]:
%timeit df['1st'].replace(idxDict)
%timeit df['1st'].map(idxDict)
100 loops, best of 3: 18 ms per loop
100 loops, best of 3: 4.31 ms per loop
In [5]:
%%timeit
for k,v in idxDict.items():
df ['1st'] = df ['1st'].replace(k, v)
100 loops, best of 3: 18.2 ms per loop
对于 30K 行 df,
map
的速度要快约 4 倍,因此它的扩展性比 replace
或循环 更好
map
确实更快,但 replace
在 19.2 版本中进行了更新(详情)以提高其速度,从而使差异显着减小:
In [1]:
import pandas as pd
df = pd.DataFrame([[1,2],[3,4],[5,6]], columns = ['1st', '2nd'])
df = pd.concat([df]*10000, ignore_index=True)
df.shape
Out [1]:
(30000, 2)
In [2]:
idxDict = {1:'a', 3:"b", 5:"c"}
%timeit df['1st'].replace(idxDict, inplace=True)
%timeit df['1st'].update(df['1st'].map(idxDict))
Out [2]:
100 loops, best of 3: 12.8 ms per loop
100 loops, best of 3: 7.95 ms per loop
update
,虽然速度较慢,但可以防止未包含在不完整地图中的值更改为 nan。
如果不需要 NaN 传播——您想要替换值但保留字典中不匹配的值——还有其他两个选项:
def numpy_series_replace(series: pd.Series, mapping: dict) -> pd.Series:
"""Replace values in a series according to a mapping."""
result = series.copy().values
for k, v in mapping.items():
result[series.values==k] = v
return pd.Series(result, index=series.index)
或
def apply_series_replace(series: pd.Series, mapping: dict) -> pd.Series:
return series.apply(lambda y: mapping.get(y,y))
numpy 实现感觉有点 hacky,但速度更快。
v = pd.Series(np.random.randint(0, 10, 1000000))
mapper = {0: 1, 3: 2}
%timeit numpy_series_replace(v, mapper)
60.1 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit apply_series_replace(v, mapper)
311 ms ± 10.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
您不必循环浏览您的字典! Pandas 将通过
replace
或 map
的一个命令应用整个字典。这些方法也可以使用Series。
您可能知道,您还可以以更少的努力构建您的字典。
d = {
1: 'a',
3: 'b',
5: 'c',
}
df['1st'] = df['1st'].replace(d)
其他人注意到映射和替换速度之间存在微小差异,但循环显然是您的问题。请注意,映射和替换之间还有其他区别。其一,
replace
将保留您的字典键中没有的所有条目 - map
将清除它们。