python - 为什么 Pandas 数据帧 to_dict ("records") 与另一个简单的实现相比性能很差?

标签 python pandas performance dataframe

与幼稚的实现相比,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)
也就是说,我的实现比 Pandas 本地实现快 7.5 倍。此外,应该很容易验证这两种方法提供相同的结果。我还针对不同大小的数据帧测试了性能,似乎我的实现始终优于其对应项(尽管幅度可能不同)。
我很好奇我在这里遗漏了什么吗?我只是不相信 Pandas 的本地实现性能,在我的印象中非常有竞争力,可以被一个不那么复杂的替代方案打败......

最佳答案

TL;DR: Pandas 主要是用纯 Python 编写的,就像您的实现一样,尽管它经常在内部使用矢量化 Numpy 调用来加快计算速度。不幸的是,这里不是这种情况。因此,Pandas 的实现效率低下。您的实现速度更快,但需要更多内存。

深入研究:
您可以找到 to_list here 的实现。它在内部使用 itertuples 迭代数据(参见 here 的代码)。 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 最终应该更大。
但是,to_list(在您的实现中)还使用 向量化 Numpy 在内部调用 有效地转换 Numpy 类型,而 Pandas 使用非常慢的方法。确实, Pandas 使用 maybe_box_native 纯 Python 函数和慢 if/else 对所有值进行 一一检查和转换……因此,Pandas 实现速度较慢也就不足为奇了。话虽如此,请注意您的代码可能会因日期而异。
当前的 Pandas 实现效率低下, future 显然可以改进(可能不需要更多内存)。

关于python - 为什么 Pandas 数据帧 to_dict ("records") 与另一个简单的实现相比性能很差?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67050899/

相关文章:

python - 在 Flask 应用程序中返回 Excel 文件

python - 无法处理 Pandas 数据框中的 NaN

python - 如何过滤数据框并更新特定单元格的选择性行

javascript - 'this' 的速度与 javascript 中的缓存变量的对比?

python - 查看字符串列表时什么更快? "In"还是 "index"?

python - 使用 Python pdfMiner 每页提取文本?

python - 如何停用鸡蛋?

python - 关于 Python 上的函数

python - 在 Pandas 数据框替换功能中使用正则表达式匹配组

c++ - VS2012 "Generating Code"大型硬编码数组速度慢