python - 为什么熊猫数据帧 to_dict("records") 与另一个幼稚的实现相比性能很差?
问题描述
与幼稚的实现相比,Pandas to_dict("records") 的性能似乎要差得多。下面是我的实现的代码片段:
def fast_to_dict_records(df):
data = df.values.tolist()
columns = df.columns.tolist()
return [
dict(zip(columns, datum))
for datum in data
]
要比较性能,请尝试以下代码片段:
import pandas as pd
import numpy as np
df_test = pd.DataFrame(
np.random.normal(size=(10000, 300)),
columns=range(300)
)
%timeit df_test.to_dict('records')
%timeit fast_to_dict_records(df_test)
输出是:
2.21 s ± 71.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
293 ms ± 15.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
即我的实现比熊猫原生实现快约 7.5。此外,应该很容易验证这两种方法提供相同的结果。我还针对不同大小的数据框测试了性能,似乎我的实现始终优于其对应物(尽管幅度可能不同)。
我很好奇我在这里遗漏了什么吗?我只是不相信,在我的印象中相当有竞争力的 pandas 原生实现性能可以被一个不那么复杂的替代方案击败那么多......
解决方案
TL;DR: Pandas 大部分是用纯 Python编写的,就像您的实现一样,尽管它经常在内部使用矢量化 Numpy 调用来加快计算速度。不幸的是,这里不是这种情况。结果,Pandas 的实现效率低下。您的实现速度更快,但需要更多内存。
深入研究:
to_list
你可以在这里找到实现。itertuples
它使用内部迭代数据(有关其代码,请参见此处)。在 2021 年 3 月 12 日生成的(略微简化的)Pandas 代码如下:
def maybe_box_native(value: Scalar) -> Scalar:
if is_datetime_or_timedelta_dtype(value): # branch never taken here
value = maybe_box_datetimelike(value)
elif is_float(value): # branch always taken here
value = float(value) # slow manual conversion for EACH values!
elif is_integer(value):
value = int(value)
elif is_bool(value):
value = bool(value)
return value
def pandas_to_list(df):
# From itertuples:
fields = list(df.columns)
arrays = [df.iloc[:, k] for k in range(len(df.columns))]
tmpRes = zip(*arrays)
# From to_list:
columns = df.columns.tolist()
rows = (dict(zip(columns, row)) for row in tmpRes)
return [dict((k, maybe_box_native(v)) for k, v in row.items()) for row in rows]
to_list
当 Pandas 在内部使用 Python 生成器时,您的实现会在内存中生成一个大的临时列表。在大多数简单的情况下,这个列表在实践中不应该是一个问题,因为dict
最终应该会大得多。
但是, (在您的实现中)还可以在内部使用矢量化 Numpy 调用to_list
有效地转换 Numpy 类型,而 Pandas 使用非常慢的方法。确实, Pandas使用纯 Python 函数和慢 if/else逐一检查和转换所有值……因此,Pandas 实现速度较慢也就不足为奇了。话虽如此,请注意您的代码在日期方面的行为可能会有所不同。maybe_box_native
当前的 Pandas 实现效率低下,未来显然可以改进(可能不需要更多内存)。
推荐阅读
- java - 如何在 Java 中重定向第二个批处理实例的 cmd 输出
- optimization - 如何有效地遍历 Julia 中的对象列表?
- angular - 为什么我从引用自身的订阅者那里收到“初始化前无法访问‘变量’”错误?
- c++ - 在 Eclipse 中编辑代码模板以强制执行 doxygen 文档
- c# - 在 C# 中,如何查看帐户中的第一个字符是否为特定值?
- google-app-engine - Go 应用引擎 dev_appserver.py 不支持运行时 go112
- pdf - p:media 在 FireFox 中不起作用,但在 Chrome 和 Opera 中有效
- mongodb - 使用 Studio 3T 时在 MongoDB 中选择不同的值
- pdf - 数字 PDF 文档签名
- python-3.x - 如何使用谷歌云路径(gs://)获取谷歌云存储桶中的所有训练文件和测试文件来训练ai平台中的作业