Pandas 性能优化指南:2024 年何时切换到 Polars 或 DuckDB

从Pandas代码级优化到Polars、DuckDB替代方案,附基准测试数据和迁移策略,帮你突破大数据处理性能瓶颈。

  • MIT
  • 更新于 2026-05-18

Pandas自2008年诞生以来,一直是Python数据处理的标配。但当数据集超过1GB、行数突破千万级时,那个熟悉的df.groupby()df.merge()可能让你的工作站卡死数分钟。2026年的今天,你不必再忍受这种等待——无论是优化现有Pandas代码,还是切换到Polars、DuckDB等新一代工具,都有成熟的解决方案。

本文覆盖三个层次:Pandas代码级优化技巧、Polars和DuckDB的架构优势、以及true实基准测试数据驱动的决策框架。

Pandas为什么在大数据面前力不从心? #

Pandas的性能瓶颈根植于其架构设计,这不是靠写"更好的代码"就能彻底解决的:

  • 单线程执行:Pandas的CPU利用率永远锁定在一个核心,其余15个核围观
  • Eager Evaluation(即时计算):每次操作立即执行并生成新副本,无法自动合并多个操作
  • 高内存占用:NumPy数组存储格式对缺失值和字符串类型极不友好,object dtype的内存开销可达实际数据的5-10倍
  • 无查询优化器df[df.A > 0].groupby('B').sum()这种链式操作会生成中间DataFrame,无法自动优化执行顺序

2024年Apache Arrow社区的基准测试显示,在10GB Parquet文件的groupby聚合任务上,Pandas的峰值内存占用达到原始数据的8-12倍。这意味着处理10GB数据可能需要80-120GB内存——远超大多数开发机配置。

Pandas代码级优化:先榨干现有工具的潜力 #

在更换工具之前,以下优化手段通常能带来2-10倍性能提升,且无需改动技术栈。

数据类型优化 #

这是投入产出比最高的优化:

  • Categorical类型:对重复值多的列(如性别、城市、类别标签)使用astype('category'),内存可降低50-90%
  • 数值向下转型int64int32float64float32,内存减半且计算更快
  • parse_dates:读取CSV时直接解析日期列,避免事后pd.to_datetime()
  • 禁用低内存模式dtype_backend='pyarrow'(Pandas 2.0+)利用Arrow后端显著降低字符串列内存

代码模式优化 #

| 低效写法 | 高效写法 | 预期加速 | |———-

|———-

|———-

| | df.apply(func, axis=1) | 向量化操作或df.eval() | 10-100x | | df['col'][idx] 链式索引 | df.loc[idx, 'col'] | 2-5x | | pd.read_csv('large.csv') | pd.read_csv(..., chunksize=...) | 内存可控 | | df.merge(df2, on='key') | 先排序再merge | 2-3x | | Python循环遍历行 | df.itertuples() | 40x | | df.A + df.B > 0 | df.eval('A + B > 0') | 2x |

文件格式选择 #

Parquet格式相比CSV有压倒性优势:

  • 读取速度:Parquet比CSV快5-20倍(列式存储+内置压缩)
  • 文件体积:Parquet通常比CSV小60-80%
  • 类型保留:Parquet存储完整的schema信息,无需读取时推断类型
  • 分区查询:DuckDB/Polars可直接跳过不满足条件的Parquet行组
h
o
n
# 一次性转换CSV为Parquet
df = pd.read_csv('large.csv')
df.to_parquet('large.parquet', engine='pyarrow', compression='zstd')

内存分析工具 #

使用memory_profilerpandas.options.display.memory_usage定位内存大户:

h
o
n
import pandas as pd
pd.set_option('display.memory_usage', True)
# 查看每列内存占用
df.memory_usage(deep=True).sort_values(ascending=False)

Polars:Rust驱动的DataFrame革命 #

Polars由荷兰工程师Ritchie Vink于2020年创建,基于Rust语言和Apache Arrow内存格式构建。2024年Polars发布1.0正式版,目前已获GitHub超过33,000星标,成为Pandas最受瞩目的替代者。

Polars的架构优势 #

  1. true正的多线程:查询引擎自动将工作负载分配到所有CPU核心,8核机器通常能看到6-7x的并行加速
  2. Lazy Evaluation:通过LazyFrame构建查询计划,自动优化谓词下推、投影下推、常量折叠
  3. Arrow内存格式:列式存储、零拷贝操作、与Python/JS/R无缝互操作
  4. Streaming模式:处理超出内存的数据集,流式读取和处理无需一次性加载全部数据
  5. 零外部依赖:单二进制文件,无需NumPy或PyArrow预装

Polars Lazy API实战 #

Lazy API是Polars的杀手级特性:

h
o
n
import polars as pl

# 构建查询计划(此时不执行)
lf = pl.scan_parquet("big_dataset.parquet")  # 懒加载
result = (
    lf.filter(pl.col("revenue") > 1000)      # 谓词下推到读取层
    .group_by("category")                     # 聚合
    .agg([pl.col("revenue").sum().alias("total")])
    .sort("total", descending=True)
)

# true正执行并自动优化
print(result.collect())  # collect()触发执行

在这个例子中,Polars不会先加载整个Parquet文件再过滤——它会将revenue > 1000条件下推到文件读取层,只读取符合条件的行组,I/O和内存双重节省。

Polars vs Pandas语法迁移 #

| 操作 | Pandas | Polars | |——

|——–

|——–

| | 读取CSV | pd.read_csv() | pl.read_csv() / pl.scan_csv() | | 过滤 | df[df.A > 0] | df.filter(pl.col('A') > 0) | | 新增列 | df['C'] = df.A + df.B | df.with_columns((pl.col('A')+pl.col('B')).alias('C')) | | GroupBy | df.groupby('A').agg({'B': 'sum'}) | df.group_by('A').agg(pl.col('B').sum()) | | 合并 | df1.merge(df2, on='key') | df1.join(df2, on='key') | | 缺失值 | df.dropna() | df.drop_nulls() |

Polars的语法设计更函数式、显式,学习曲线比Pandas略陡,但API一致性强,不易出现Pandas中loc/iloc/at混淆的问题。

DuckDB:进程内OLAP数据库的新范式 #

DuckDB由荷兰CWI数据库组(与Python同源)于2019年发布,定位为"数据科学的SQLite"——一个零配置、无外部依赖、可嵌入Python的OLAP数据库。

DuckDB的四大杀手锏 #

  1. SQL原生:用你已有的SQL技能处理DataFrame,零学习成本
  2. 成本优化器:内置查询优化器自动选择最佳执行计划,join顺序、谓词下推自动完成
  3. 零依赖pip install duckdb即可,无需PostgreSQL/MySQL服务器
  4. Pandas互操作:查询结果直接返回Pandas DataFrame,反之可用SQL查询Pandas DataFrame

DuckDB + Pandas集成模式 #

DuckDB的独特价值在于它不强迫你放弃Pandas——两者可以无缝协作:

h
o
n
import duckdb
import pandas as pd

# 直接查询Pandas DataFrame
df = pd.read_parquet("sales.parquet")
result = duckdb.query("""
    SELECT region, SUM(amount) as total, AVG(amount) as avg_sale
    FROM df
    WHERE date >= '2024-01-01'
    GROUP BY region
    ORDER BY total DESC
""").to_df()  # 直接转回Pandas DataFrame

更强大的场景是DuckDB直接读取Parquet文件,完全跳过Pandas中间层:

h
o
n
# DuckDB直接读取Parquet,无需Pandas
con = duckdb.connect()
con.execute("""
    SELECT *
    FROM read_parquet('s3: //bucket/*.parquet')
    WHERE event_type = 'purchase'
    LIMIT 1000000
""")

这种模式下DuckDB利用Parquet的列统计信息直接跳过不相关的行组,实现true正的"零加载"查询。

基准测试:Pandas vs Polars vs DuckDB #

以下数据来自2025年H2O.ai对5GB NYC Taxi数据集的标准化基准测试(8 vCPU, 32GB RAM):

| 操作 | Pandas 2.2 | Polars 1.0 | DuckDB 1.0 | Polars加速比 | |——

|————

|————

|————

|————–

| | 读取5GB CSV | 48.2s | 5.1s | 6.8s | 9.5x | | 筛选+聚合 | 12.5s | 1.2s | 1.5s | 10.4x | | GroupBy(10组) | 18.7s | 2.3s | 2.8s | 8.1x | | Join(1GB+1GB) | 45.3s | 4.8s | 5.5s | 9.4x | | 写入Parquet | 22.1s | 3.4s | 4.2s | 6.5x | | 峰值内存 | 28.4GB | 4.2GB | 5.1GB | 6.8x |

关键发现

  • Polars在执行速度和内存效率上均领先,适合作为Pandas的全面替代
  • DuckDB与Polars性能接近,优势在于SQL接口和即席查询(ad-hoc analysis)
  • Pandas在所有测试项中均为最慢且最耗内存,差距在7-10倍之间
  • 三者读取Parquet的性能差距比读取CSV时缩小(Parquet格式本身已做大量优化)

场景决策矩阵:什么场景用什么工具? #

| 场景 | 推荐工具 | 理由 | |——

|———-

|——

| | 探索性数据分析(EDA) | Polars | 交互速度快,API丰富 | | ETL数据管道 | Polars | Lazy模式优化整条pipeline | | SQL重度用户 | DuckDB | 零学习成本,SQL即战力 | | 即席查询(Ad-hoc) | DuckDB | 快速SQL探查,无需转换 | | 超大数据(>100GB) | Polars Streaming | 流式处理不OOM | | 遗留系统迁移 | DuckDB + Pandas | 渐进式替换,风险最低 | | 生产ML特征工程 | Polars | 稳定、快速、内存可控 | | 跨语言数据交换 | Polars | Arrow格式天然跨语言 |

渐进式迁移策略 #

完全替换Pandas并非必要,推荐分阶段推进:

  1. 阶段一:I/O层替换(1-2周)

    • 所有pd.read_csv()替换为pl.read_csv()或DuckDB直接查询
    • 数据落地格式统一改为Parquet
  2. 阶段二:核心转换层(2-4周)

    • ETL中的filter/groupby/join迁移至Polars
    • 保留Pandas用于下游ML库(scikit-learn等)的接口层
  3. 阶段三:全链路优化(1-2月)

    • ML训练数据生成改用Polars原生
    • 特征存储(Feature Store)直接消费Polars输出
  4. 混用模式(推荐)

    • Polars/DuckDB负责数据加载和转换
    • Pandas负责与ML生态的衔接
    • DuckDB负责SQL即席查询
h
o
n
# 混用模式示例:Polars处理 + Pandas输出
import polars as pl

df_pl = pl.scan_parquet("raw_data.parquet").filter(...).group_by(...).agg(...)
# 转回Pandas给scikit-learn
df_pd = df_pl.collect().to_pandas()

常见问题 #

Polars能完全替代Pandas吗? #

功能覆盖度已达90%以上,但Pandas的某些高级特性(如MultiIndex层次索引、时间序列重采样、与statsmodels的紧密集成)在Polars中语法不同或尚未实现。建议新 projects 直接使用Polars,遗留系统逐步迁移。Polars 1.0(2024年7月发布)标志着API稳定性承诺。

DuckDB和Polars,应该选哪个? #

两者并非竞争关系而是互补。如果你团队SQL能力强、需要大量即席查询、希望最低学习成本 → 选DuckDB。如果你追求极致性能、需要构建复杂ETL管道、团队能接受新API → 选Polars。最佳实践是在同一项目中混用:DuckDB做SQL探查,Polars做pipeline。

Polars和DuckDB可以一起用吗? #

完全可以。Polars可以读取DuckDB查询结果,DuckDB也可以查询Polars DataFrame。一个典型模式:用DuckDB的SQL做快速数据探查,确认逻辑后用Polars的Lazy API构建生产级pipeline。

Polars比Pandas快多少? #

根据2025年多组独立基准测试,Polars在典型数据 workloads 上比Pandas快8-30倍,内存使用降低5-10倍。具体加速比取决于操作类型:groupby和join(10-30x)、filter(8-15x)、简单数学运算(5-10x)。磁盘I/O密集型任务加速比较小,CPU密集型任务加速比最大。

新手该学Pandas还是Polars? #

2026年的建议:如果从零开始学习数据分析,直接学Polars。它的API设计更一致、错误提示更友好、性能优势巨大,且Polars正在获得越来越多的教程和社区支持。如果求职导向强,Pandas仍是大多数公司的实际标准,建议两者都掌握,以Pandas读懂 legacy code,以Polars写新代码。


推荐基础设施 #

要 7×24 稳跑上述工具,服务器选择关键:

  • DigitalOcean — 新用户 $200 试用 60 天,全球 14+ 节点,一键 droplet 适配 AI 工作流。
  • HTStack — 香港 VPS,国内访问低延迟。dibi8.com 自家所在 IDC,生产验证。

推广链接,不增加你的成本,能支持 dibi8.com 运营。

💬 留言讨论