首页 / Pandas 入门教程 / 大数据集与性能优化

Pandas 入门教程

大数据集与性能优化

本教程共 54 篇 · 第 53 篇 · 更新于 2026-08-11 · 约 7 分钟阅读

PandasPandas 入门教程性能优化PyArrownumba大数据

本节目标:学会压缩内存占用、分块处理大文件、用 PyArrow 后端和向量化提速,知道什么时候用 numba、query 和 eval。

先看内存:一切优化的起点

大数据集的第一道坎不是速度,是内存。pandas 是内存计算,数据装不下,后面都白搭。先摸清家底:

import pandas as pd

df = pd.DataFrame({
    "id": range(10000),
    "类别": ["甲", "乙", "丙"] * 3333 + ["甲"],
    "数值": [i * 1.1 for i in range(10000)],
})
print(df.memory_usage(deep=True))

memory_usage(deep=True) 逐列报内存字节数,deep=True 才会把字符串内容算进去。哪列最大,就从哪列开刀。df.info() 底部也会汇总总内存。

少加载:能不带就不带

数据还在磁盘上时,先问自己”真的需要全部列吗”。read_csvusecols 只读指定列,read_parquetcolumns 参数。列少一半,内存就少一半:

import pandas as pd

df = pd.DataFrame({"id": range(5), "类别": ["甲"] * 5,
                   "数值": [1.0] * 5, "备注": ["x"] * 5})
df.to_csv("demo.csv", index=False)

# 只读需要的 3 列,跳过"备注"
df2 = pd.read_csv("demo.csv", usecols=["id", "类别", "数值"])
print(df2.head())

换类型:压缩数值与文本

pandas 默认 int64/float64 偏保守,实际数据往往用不了那么宽。用 pd.to_numericdowncast 参数自动降档;低基数文本列转 category:

import pandas as pd

df = pd.DataFrame({"id": [1, 2, 3] * 1000, "城市": ["北京", "上海", "广州"] * 1000})
df["id"] = pd.to_numeric(df["id"], downcast="unsigned")  # int64 降到能装下为止
df["城市"] = df["城市"].astype("category")               # 3 种值,存整数编码
print(df.memory_usage(deep=True))

这两招配合,常见数据集能压到原来的 1/3 甚至更少。注意:唯一值特别多的列不适合转 category(§50 讲过),转之前先 value_counts() 看看。

分块:装不下就分批读

数据比内存大时,别一次性读进来。read_csvchunksize 参数把文件切成一块块,循环里逐块处理,每次只占一块的内存:

import pandas as pd
import io

# 用 StringIO 模拟大文件,实际换成文件路径
data = "id,数值\n" + "\n".join(f"{i},{i * 1.5}" for i in range(1, 1001))

total = 0
count = 0
for chunk in pd.read_csv(io.StringIO(data), chunksize=100):
    total += chunk["数值"].sum()   # 每块算完就丢
    count += len(chunk)
print(total / count)               # 全局平均值

分块适合”块与块之间不需要配合”的统计(求和、计数、value_counts)。多个文件同理:循环里逐个读入、累积结果。要 groupby 这种跨块的复杂操作,手动累积很麻烦,这时考虑 Dask、Polars 这些专门处理超大数据集的库。

PyArrow 后端:类型更丰富更快

pandas 3.0 里,PyArrow 不只是字符串的存储层,还能当所有列的后端。装好 pyarrow 后,指定 dtype="float64[pyarrow]" 这类 Arrow 类型即可:

import pandas as pd

s = pd.Series([1.5, 2.5, None], dtype="float64[pyarrow]")
print(s)
# 0     1.5
# 1     2.5
# 2    <NA>
# dtype: double[pyarrow]

Arrow 后端的优势:所有类型都支持缺失值(NumPy 的 int 列做不到)、数值聚合和字符串运算走 Arrow 的 C++ 实现更快、还能和 Polars、DuckDB 等 Arrow 生态库零拷贝互转。读文件时也能直接指定:pd.read_csv("big.csv", dtype_backend="pyarrow")

Tip

升级 pandas 3.0 后如果发现字符串操作明显变快,多半是因为装过 pyarrow——新 StringDtype 自动用了它(§49)。没装的话 pip install pyarrow 即可,pandas 检测到就自动启用。

向量化:别用循环

Python 循环逐行处理,每一行都要走一遍 Python 解释器,慢。pandas 的向量化操作整列一起算,底层是 C 代码,快几个数量级:

import pandas as pd

df = pd.DataFrame({"a": range(10000), "b": range(10000)})

# 慢:Python 循环
df["c"] = [x + y for x, y in zip(df["a"], df["b"])]

# 快:向量化,一行搞定
df["c"] = df["a"] + df["b"]

连 apply 都要谨慎——它本质还是逐行调 Python 函数。能写成向量化表达式的(加、减、乘、比较、where、clip),一律别用 apply。

query / eval:表达式引擎

df.query 用字符串写筛选条件,df.eval 用字符串写计算式。query 的语法(@ 变量、反引号列名、and/or 组合)在 §21 讲透了,这里只聊性能:它们不只是省代码,大 DataFrame 下可走 numexpr 引擎一次算完多个操作:

import pandas as pd

df = pd.DataFrame({"a": range(10000), "b": range(10000)})

# 表达式里直接创建新列(返回新 DataFrame)
df2 = df.eval("c = a + b")
df2 = df2.eval("d = c * 2")
print(df2.head())

eval 的注意点和 query 一样:局部变量必须加 @ 前缀,否则 pandas 找不到;表达式里的布尔运算用 and / or,不是平时布尔索引的 & / |

Warning

query/eval 不是万能的。官方经验:几万行以下的小 DataFrame 用普通写法反而更快,eval 的解析开销摆在那。超过 10 万行、表达式又长又复杂时,才是它的主场。

numba:给自定义计算加速

循环无法向量化时(比如逐行依赖前面结果),还有一招:numba 即时编译,把 Python 函数编译成机器码,接近 C 的速度。两种用法:

import pandas as pd
import numpy as np

s = pd.Series(range(1000000))

# 用法一:rolling 窗口的 apply 指定 numba 引擎
def f(x):
    return np.sum(x) + 5

print(s.rolling(10).apply(f, engine="numba", raw=True))

# 用法二:自己写 @jit 函数,传入 to_numpy() 的数组
import numba

@numba.jit(nopython=True)
def add_one(x):
    return x + 1

print(add_one(s.to_numpy()))

numba 的代价:第一次调用要编译,慢得吓人,之后就走缓存飞快。数据量小(几万行以内)别用它,收益撑不起编译开销。numba 只擅长数值计算,字符串、日期这类操作不在它的加速范围内。

Note

优化顺序记牢:1. 用 memory_usage(deep=True) 找出内存大户;2. 用 usecols / columns 参数少加载列;3. 换类型压缩——category 收编低基数文本、downcast 压缩数值;4. 用向量化替代循环和 apply;5. 还不够再上 numba、query/eval,或分块处理。前三步通常已经解决了 90% 的问题。另外,3.0 默认开启的 Copy-on-Write(§9)让赋值语义更安全,也少了很多隐性复制开销。

小结

性能优化四板斧:memory_usage 找大户、usecols 少加载、换类型压缩、向量化替代循环。内存不够就分块,函数没法向量化就 numba,超大表达式用 query/eval。优化不是炫技,是让分析跑得完、跑得快。最后一节,看看 pandas 在数据工具江湖里的位置,和接下来往哪学。