核心内容
借助 NumPy 向量化、bottleneck 加速、numba JIT 编译 与 PyArrow 字符串 四类工具大幅提升 pandas 计算性能。
一、NumPy 加速
pandas 底层基于 NumPy,直接使用 NumPy 函数可避免 pandas 层开销。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(1_000_000, 5), columns=list('abcde'))
# pandas 方法
result = df['a'] * 2 + np.sin(df['b'])
# NumPy 直接操作(当无缺失值时可更高效)
arr = df[['a', 'b']].to_numpy()
result_np = arr[:, 0] * 2 + np.sin(arr[:, 1])
# 使用 NumPy ufunc
df['a'].values.mean() # 比 df['a'].mean() 略快
np.log1p(df['a'].values) # 计算 log(1+x)适用场景
- 纯数值、无缺失值的大数组运算
- 频繁调用的自定义数值函数
- 矩阵运算:
df.to_numpy() @ other
二、bottleneck 加速
bottleneck 是 C 语言实现的 NumPy 加速库,pandas 默认开启。
# 确认是否开启
pd.get_option('compute.use_bottleneck') # True
# 手动开关
pd.set_option('compute.use_bottleneck', True)
pd.set_option('compute.use_bottleneck', False)加速的运算:sum、mean、std、var、min、max、median、rank、rolling 相关聚合等。
安装
pip install bottleneck
三、numba 加速
numba 通过 LLVM 将 Python 函数 JIT 编译为机器码,适合无法向量化的循环逻辑。
import pandas as pd
import numpy as np
from numba import jit
# 普通 Python 函数
def compute_python(x, y):
total = 0.0
for i in range(len(x)):
total += x[i] * y[i]
return total
# numba JIT 编译
@jit(nopython=True)
def compute_numba(x, y):
total = 0.0
for i in range(len(x)):
total += x[i] * y[i]
return total
x = np.random.rand(1_000_000)
y = np.random.rand(1_000_000)
compute_numba(x, y) # 首次调用编译,后续极快与 pandas 结合
# 1. rolling.apply 使用 numba 引擎
df['b'].rolling(10).apply(
lambda x: np.argmax(x), engine='numba', raw=True
)
# 2. groupby.transform / apply 使用 numba
df.groupby('g')['v'].transform(
lambda x: x - x.mean(), engine='numba'
)
# 3. 自定义函数中调用 @jit 函数
@jit(nopython=True)
def rolling_custom(arr):
return arr.mean() + arr.std()
df['c'] = df['b'].rolling(10).apply(rolling_custom, raw=True)numba 限制
- 需要安装
numba:pip install numba- 首次调用有编译开销(可在小数据上预热)
nopython=True模式下不支持 pandas 对象,需传 NumPy 数组
四、PyArrow 字符串性能
PyArrow 字符串后端在内存占用与运算速度上均优于 object 字符串。
import pandas as pd
# 方式 1:全局开启
pd.set_option('future.infer_string', True) # 2.0+ / 2.3 中推荐
# 方式 2:显式 dtype
df['name'] = df['name'].astype('string[pyarrow]')
# 方式 3:读取时指定
df = pd.read_csv('data.csv', dtype_backend='pyarrow')
# 方式 4:ArrowDtype
import pyarrow as pa
df['name'] = df['name'].astype(pd.ArrowDtype(pa.string()))性能对比示例
df = pd.DataFrame({
'name': ['apple', 'banana', 'cherry'] * 300_000,
'value': range(900_000)
})
# object 字符串
df_obg = df.astype({'name': 'object'})
%timeit df_obg['name'].str.contains('a') # 慢
# pyarrow 字符串
df_pa = df.astype({'name': 'string[pyarrow]'})
%timeit df_pa['name'].str.contains('a') # 快 2-5 倍
# 内存对比
df_obg.memory_usage(deep=True)
df_pa.memory_usage(deep=True)PyArrow 字符串支持的更多操作
df['name'].str.upper()
df['name'].str.startswith('a')
df['name'].str.contains('a', regex=True)
df['name'].str.extract(r'(\w+)')与 pandas 字符串访问器差异
PyArrow 字符串的缺失值统一为
pd.NA,行为更一致;str访问器绝大部分方法可用,个别正则高级特性可能受限。
加速策略选择
| 场景 | 推荐工具 |
|---|---|
| 数值向量化运算 | NumPy ufunc / 内置方法 |
| 滚动、聚合大数组 | bottleneck |
| 复杂循环 / 自定义算法 | numba(@jit + nopython=True) |
| 大量字符串处理 | PyArrow 字符串 dtype |
| 表达式过滤 | eval() / query() + numexpr |
相关笔记
- 21.5 向量化操作与避免循环 - 避免循环是前提
- 21.4 数据类型优化 - 配合 dtype 优化
- Pandas-十三-Arrow 与高性能数据类型 - PyArrow 详细说明