【问题标题】:Using replace efficiently in pandas在 pandas 中有效地使用替换
【发布时间】:2017-06-20 02:55:37
【问题描述】:

我希望在 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 更集中、更清晰的问题,解决方案类似。

【问题讨论】:

标签: python pandas indexing dataframe series


【解决方案1】:

使用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,mapreplace 或循环快 4 倍左右

【讨论】:

  • df['1st'].map(idxDict.get) 实际上比只传递字典本身要快。
  • @root 我没有观察到:In [15]: %timeit df['1st'].map(idxDict) %timeit df['1st'].map(idxDict.get) %timeit df['1st'].map(idxDict) 100 loops, best of 3: 8.38 ms per loop 100 loops, best of 3: 4.22 ms per loop 仅传递 dict 速度快 2 倍
  • 嗯...我想这取决于字典的大小和要查找的值。请参阅我列为重复的问题。
  • @root 是的,我可以想象这里只重复查找 3 个值会比使用更大的字典更快
  • 谢谢!!为什么会这样?
【解决方案2】:

虽然map 确实更快,但replace 在版本 19.2 (details here) 中进行了更新以提高其速度,从而显着减少差异:

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

此外,我修改了 EdChum 的 map 代码以包含 update,虽然速度较慢,但​​可以防止将未包含在不完整映射中的值更改为 nans。

【讨论】:

    【解决方案3】:

    如果不需要 NaN 传播——你想替换值但保留在 dict 中不匹配的值——还有其他两个选项:

    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 的实现感觉有点老套,但速度更快。

    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)
    

    【讨论】:

    • 太棒了,由于某种原因,您的 numpy_series_replace 比 pd.map 函数快
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    相关资源
    最近更新 更多