如何通过字典d替换Pandas series s中的值已被多次询问和重新询问.

建议的方法(1234)是使用s.replace(d),或者如果在字典键中找到了所有序列值,则偶尔使用s.map(d).

然而,使用s.replace的性能通常非常慢,通常比简单的列表理解慢5-10倍.

另外,s.map(d)具有良好的性能,但只有在字典中找到所有键时才推荐使用.

为什么s.replace这么慢?如何提高性能?

import pandas as pd, numpy as np

df = pd.DataFrame({'A': np.random.randint(0, 1000, 1000000)})
lst = df['A'].values.tolist()

##### TEST 1 #####

d = {i: i+1 for i in range(1000)}

%timeit df['A'].replace(d)                          # 1.98s
%timeit [d[i] for i in lst]                         # 134ms

##### TEST 2 #####

d = {i: i+1 for i in range(10)}

%timeit df['A'].replace(d)                          # 20.1ms
%timeit [d.get(i, i) for i in lst]                  # 243ms

Note:这个问题没有被标记为重复,因为它正在寻找针对不同数据集的when to use种不同方法的具体建议.这在回答中是明确的,这是其他问题中通常不涉及的一个方面.

推荐答案

一个简单的解决方案是根据字典键覆盖值的完整程度来 Select 方法.

General case

  • 如果所有值都已映射,则使用df['A'].map(d);或
  • Use df['A'].map(d).fillna(df['A']).astype(int) if >5% values mapped.

Few, e.g. < 5%, values in d

  • 使用df['A'].replace(d)

约5%的"交叉点"特定于下面的基准测试.

有趣的是,在这两种情况下,简单的列表理解通常都不如map.

Benchmarking

import pandas as pd, numpy as np

df = pd.DataFrame({'A': np.random.randint(0, 1000, 1000000)})
lst = df['A'].values.tolist()

##### TEST 1 - Full Map #####

d = {i: i+1 for i in range(1000)}

%timeit df['A'].replace(d)                          # 1.98s
%timeit df['A'].map(d)                              # 84.3ms
%timeit [d[i] for i in lst]                         # 134ms

##### TEST 2 - Partial Map #####

d = {i: i+1 for i in range(10)}

%timeit df['A'].replace(d)                          # 20.1ms
%timeit df['A'].map(d).fillna(df['A']).astype(int)  # 111ms
%timeit [d.get(i, i) for i in lst]                  # 243ms

Explanation

s.replace的速度如此之慢,原因在于它不仅仅是简单地映射一本字典.它处理一些边缘 case 和可以说是罕见的情况,在任何情况下都值得更多的关注.

这是pandas\generic.py分之replace()的节选.

items = list(compat.iteritems(to_replace))
keys, values = zip(*items)
are_mappings = [is_dict_like(v) for v in values]

if any(are_mappings):
    # handling of nested dictionaries
else:
    to_replace, value = keys, values

return self.replace(to_replace, value, inplace=inplace,
                    limit=limit, regex=regex)

似乎涉及到很多步骤:

  • 将字典转换为列表.
  • 遍历列表并判断嵌套字典.
  • 将键和值的迭代器输入替换函数.

这可以与pandas\series.py分之map()的精简代码相比:

if isinstance(arg, (dict, Series)):
    if isinstance(arg, dict):
        arg = self._constructor(arg, index=arg.keys())

    indexer = arg.index.get_indexer(values)
    new_values = algos.take_1d(arg._values, indexer)

Python-3.x相关问答推荐

使用Python请求从特定URL下载图像时出错

如何从枚举中获取某个值?

将字符串转换为python日期时间时出错

Django 3.2/Django-cms 3.11:查找错误:型号帐户.客户用户未注册

selenium 无法执行网站上最简单的功能

对大型数据框中的选定列进行重新排序

通过匹配第一列的行值,逐个单元格地添加两个Pandas 数据框中的浮点数

Pandas matplotlib:条形图占总数的百分比

裁剪复数以解决 exp 中的溢出错误

使用 selenium 加速网页抓取

如何融化具有自定义名称的Pandas

总结基于条件的值,如果不匹配则保留当前值

无法使用 Python 和 Selenium 检索 href 属性

逗号分隔列表的 argparse 操作或类型

pythondecorator中的变量范围

创建日志(log)文件

cv2 python 没有 imread 成员

Python 3 与 Python 2 映射行为

map 对象不是 JSON 可序列化的

无法解码 Python Web 请求