Hacker News 中文摘要

RSS订阅

Pandas 应该被淘汰 -- Pandas Should Go Extinct

Pandas 应该灭绝。不是外交用的可爱熊猫,而是 Python 的 DataFrame 库。作者认为,Pandas 的低效迫使你在工作负载尚不足以证明其合理性之前,就转向分布式查询系统,而多数工作负载永远不会需要这些被过度营销的银弹。要理解这点,先看典型采用路径:人们通常从 Excel 起步,到 GB 级数据时转向 Pandas,它能撑到几十 GB,随后遇到内存不足、计算慢和 API 繁琐的问题,传统答案是升级到 Spark、DataBricks、Snowflake 等昂贵工具。

正文

你说得没错,Pandas 应该灭绝。不是那种用于国际外交的毛茸茸可爱动物,而是 Python 的 DataFrame 库。

为什么?因为 Pandas 效率低下,迫使你在工作负载还未复杂到需要分布式查询系统时,就提前采用它们。我认为大多数工作负载永远不需要那些系统,它们只是营销得很好的“银弹”。

要理解我的意思,先得了解 Pandas 典型的采用路径。

我们为什么用 Pandas?

下图粗略展示了根据数据量大小,通常何时会考虑采用某个 DataFrame 库。从左向右看,也能看到数据分析工具的典型采用路径,以及 Pandas 用户在特定数据量后遇到的悬崖。

人们通常从 Excel 开始,在 GB 量级左右转到 Pandas。Pandas 在几十 GB 范围内表现良好,之后就会出现内存不足、计算缓慢,或对它复杂 API 感到沮丧。传统做法是转向为大数据设计的“真正”(读作:昂贵)工具,如 Spark、DataBricks、Snowflake 或 Dask。

按数据量采用 DataFrame 库的粗略指南

关键在于:“Pandas 悬崖”和真正需要分布式系统的规模之间,存在一个不断扩大的缺口。这个缺口大约在 100GB 左右,可以由现代高性能单机工具有效填补。我主要指的是 Polars 和 DuckDB。

为何如此在意约 100GB 这个阈值?答案在于弄清楚真实世界中有多少“大数据”。

我有大数据,对吧?

2024 年,亚马逊发表了一篇论文《为什么 TPC 不够:亚马逊 Redshift 集群分析》。旨在将亚马逊自家的分布式分析数据库 Redshift 的遥测数据,与行业标准数据库基准的查询模式比较。作为分析的一部分,亚马逊公布了关于查询运行时间和表大小的集群统计。

Redshift 集群的分桶查询运行时间

Redshift 集群的分桶表大小

如果愿意做几个假设,就能对亚马逊客户如何使用分析数据库得出有趣结论。假设: - Redshift 表平均行大小为 1KB - 每个 Redshift 集群由 10 台机器组成,每台能以 8GB/s 从 S3 吞数据,且只做这个

我们发现: - Redshift 集群中 94.68% 的表数据少于 100GB - 86.9% 的查询操作的数据在 80GB 或更少

如果想更深入看这组数据,MotherDuck 的 Jordan Tigani 做过深度分析。注意:MotherDuck 是售卖 DuckDB 托管的 SaaS 公司,或许应持怀疑态度。

计算说明:累加运行时间表前3行得出86.9%查询在1秒内完成。按集群10台机器、每台8GB/s吞数据计算:10机器 * 8GB * 1秒 = 80GB数据(8GB/s基于过时的基准)。为得出“94.68%表少于100GB”,累加至10^8限制的行列得94.68%行,按1KB/行算:10^8行 * 1KB = 100GB。即便假设10KB/行,表大小也才1TB。

但这些意味着什么?

你很可能没有大数据,以后也不会有。你有中等数据问题,需要中等数据方案。

认识替代方案

如前所述,我提出的替代是 DuckDB 和 Polars。大体上,Polars 是基于 Rust 的 DataFrame 库,用起来像 Pandas,但在几个重要方面不同。DuckDB 是内存分析数据库——本质是预防分析的 SQLite。为感受这些工具及与 Pandas 的区别,看个例子。

十亿行挑战要求写最快的 Java 程序,计算含气象站数据的十亿行 CSV 的最小值、均值和最大值。获接受的最快实现运行了1.5秒。

原挑战用裸金属 Hetzner AX161 服务器,32核128GB内存,Debian 12。测试改用 AWS m7a.8xlarge,同样 Debian 12。基本配置同原挑战:AMD CPU 32核128GB内存。但非裸金属专用硬件可能稍影响可复现性。

闭嘴,给我看代码

闲话少说,看几个实现。

Pandas

碰过 Pandas 的人都很眼熟。从 CSV 读数据,按气象站分组,计算聚合的最小、平均、最大值。

输出序列化在哪?性能测试跳过了原挑战指定的输出序列化。各实现都包含序列化输出能力,用于单元测试。因十亿行挑战输出格式非标准,测序列化对各库不相关。

关键是 Pandas 顺序急切执行每步,读入整个数据集再分组聚合。

Polars

Polars 代码类似,但运行时大不同。用 scancsv 惰性扫描分块,groupby 和 agg,最后 collect(newstreaming=True) 流式计算。数据被分块扫描、分组、聚合。关键:scancsv 惰性评估,collect 执行查询管线,像数据库一样优化查询图,并行多线程。也能查看优化和未优化查询计划。查询计划不外乎扫描 CSV、投影两列、聚合,类似数据库谓词下推。

DuckDB

DuckDB 代码像普通 SQL,建表选列聚合分组。关键:提供 SQL 接口覆盖存储各处的数据,也能查内存 Python 对象。DuckDB 同样惰性、多线程、分块执行查询计划,可查看。

性能结果

性能通过为每个库写独立脚本,指向磁盘上十亿行 CSV 测量。脚本由轻量基准工具启动新 Python 解释器运行,以 50ms 间隔轮询内存 CPU 至退出。每基准两次预热,三十次重复。

| Library | Median Duration | Median Max CPU % | Median Max USS | Median Max Swap | | --- | --- | --- | --- | --- | | Pandas | 4m 28s | 113.0% | 38.12 GB | 0 MB | | Polars | 5.04s | 3202.60% | 18.02 GB | 0 MB | | DuckDB | 5.19s | 3174.64% | 1.93 GB | 0 MB |

结果不言自明:Polars 和 DuckDB 远快于 Pandas,内存分别少2倍和19倍,接近手工 Java 实现。

Polars 内存使用似乎仍偏高,怀疑未全流式。DuckDB 完美,极少代码无调优即得卓越性能。

本地开发性能

更好生产性能只是一部分。DuckDB 和 Polars 也加速本地开发循环。

在强力但稍旧笔记本重测。用 Framework 13,i5-1135G7,8核16GB。

| Library | Median Duration | Median Max CPU % | Median Max USS | Median Max Swap | | --- | --- | --- | --- | --- | | Pandas | 12m 15s | 110.35% | 15.67 GB | 21.02 GB | | Polars | 39s | 765.75% | 15.22 GB | 35.85 MB | | DuckDB | 47s | 807.0% | 546.87 MB | 0 MB |

再次见 Polars 和 DuckDB 佳绩,Polars 耗内存稍入交换。Pandas 像糖蜜般慢且吞内存。

白送了什么?

基准凸显几项优于传统 Pandas 工作流的关键优势: - 无痛多线程:Polars 和 DuckDB 自动用满所有 CPU 核,无需管理线程进程。 - 高效内存使用与流式:两者都能分块处理,降内存。 - 惰性评估:提前定义整个计算,两库优化执行计划,应用谓词下推等数据库技术。 - 潜在落盘:当操作超内存,内置机制智能落盘中间结果,通常比 OS 通用交换高效。

用 Apache Arrow 无痛采用

不重写一切就试这些工具的关键是 Apache Arrow。Arrow 正成列式数据内存表示事实标准,由 Pandas 原作者 Wes McKinney 创立。Pandas 自 2023年4月 2.0 版起支持 Arrow。

Polars 和 DuckDB 原生支持 Arrow。意味着可在间零拷贝移动 DataFrame,切换近乎免费。注意 Pandas 默认不建 Arrow 支撑的 DataFrame,需指定 dtype_backend 为 pyarrow。

例:纽约出租车数据

分析 NYC 出租车数据(2009至今行程,月度 Parquet 文件),看疫情期间(2019-2022)现金支付是否变少。需处理约3GB Parquet。

例中以 DuckDB 和 Pandas 实现读 Parquet 和计算函数。Polars 版留作读者练习。

Pandas 代码循环读 Parquet 文件,按时间过滤,提取月年,分组聚合算现金占比。DuckDB 代码用 SQL 从文件夹读 Parquet,过滤,分组计数算现金百分比。混合调用显示不同组合。

| Approach | Median Duration | Median Max CPU % | Median Max USS | Median Max Swap | | --- | --- | --- | --- | --- | | Pure Pandas | 41.88s | 146.10% | 14.52 GB | 1.92 GB | | Duck Reads Panda Thinks | 28.39s | 793.7% | 14.79 GB | 1.22 GB | | Panda Reads Duck Thinks | 29.25s | 765.4% | 12.39 GB | 0 MB | | Pure DuckDB | 21.70s | 814.95% | 216.76 MB | 0 MB |

再次类似模式。DuckDB 充分利用 CPU 核且少占内存。涉及 Pandas 则慢如爬、内存飙。

若关心现金使用是否下降,确实降了。但相关非因果,别妄下结论。

2019年1月至2023年1月纽约出租车现金使用占比图

为何不该听我的?

技术界怀疑是健康的,我列了几点不该听我的理由: 1. 我只是跑基准的个人。所有代码开源,你可自读判断缺陷,我恳请你如此。 2. 若深陷 Pandas 生态,切换成本或许太高。但“太高”的门槛自我演讲已大变。 3. 让 Pandas 慢慢熬。Pandas 在改进,虽因生态位关键而慢。但 DuckDB 和 Polars 除性能外有益:API 更不混淆,DuckDB 的 SQL 高度可迁移。

Polars 对 DuckDB,哪个更好?

完全看工作负载、经验和偏好。数据工程师爱 SQL,软件工程师爱 Polars。都试试。

唯一不该做的是仅因 Pandas 性能差就盲目采用分布式查询系统及其复杂。长期真需要的概率很小。

性能前所未有的易得,货比三家!

评论

社区对“pandas 应灭绝”的主张呈分裂态势:多数人承认 pandas 有缺陷但强调其生态黏性与小数据适用性使其短期不会消失,少数人积极转向 Polars 或 DuckDB 并分享正反经验,同时有用户坚决反对淘汰而看重其 ETL 通用性。

认为 pandas 虽不完美但根深蒂固难以替代

  • 多数数据项目规模远低于十 GB 阈值,无需更换工具;团队迁移阻力大,且许多用户本就用 Excel 等,pandas 对已够用(较多人) > 九成九导入 pandas 的项目数据量远小于几十 GB 的门槛(@sjtrny)

推崇 Polars 与 DuckDB 作为更优替代

  • 语法更友好、性能更优,声明式虚拟列能力强大;DuckDB 内存控制精准,替换后读取 Excel 从两分钟降至两秒(较多人) > Polars 的 pl.col 接口可随处声明式生成虚拟列而不实际占内存,非常强大(@stephenlf)

反对淘汰 pandas,强调其在 ETL 中的瑞士军刀地位

  • 真正做重过滤、变形、切分等数据构建时 pandas 最合适,可小内存运行且有 Modin 等兼容升级,并良好整合机器学习库(少数人) > 涉及数据集构建与清洗的扎实 ETL 工作,pandas 才是完美用例(@bijowo1676)

指出新工具存在反直觉局限与组织心态错位

  • Polars 的惰性接口仍可能瞬间吃满内存且格式支持不全;小数据集上其排序甚至慢数倍;许多中型数据组织却偏爱摆大数据姿态(较多人) > 本以为 Polars 能免内存扫描海量数据,结果调用即内存暴涨,令人沮丧(@viccis)

建议将计算推至数据库,Python 仅做胶水

  • 近年正确用 SQL 把管线尽量卸载到数据库,Python 保持简单;若必须在 Python 做类库操作,DuckDB 优于 pandas/Polars(少数人) > 现在尽量把工作推给数据库,Python 只做简单胶水,比用 pandas 舒服(@evolve-maz)

认为 AI 编码工具削弱了死守 pandas 的理由

  • 有了 AI 辅助,重写代码与熟悉度壁垒降低,类似盲目 rust 化热潮减退;但组织求“大数据”人设仍阻碍理性选型(少数人) > AI 编码工具让重写与熟悉度论证不那么站得住脚(@willsmith72)

有价值的补充

  • 文章未测试 Dask 且将其排除基准,但 Dask 本为兼顾易用与可扩展而设计,值得考虑(@dweinus)
  • chdb 的 DataStore 提供 pandas API 的懒加载替代,兼具 chdb 性能,适合快速数据处理(@sirfz)
  • 中型数据下从 pyarrow 后端 pandas 迁移到 DuckDB 或 Polars 十分轻易(@mharrison)
  • 有读者欣赏标题的 bait-and-switch 幽默:误以为讨论动物熊猫灭绝,实则库的废弃主张,称此类 clickbait 可敬(@rmunn)