核心内容

性能优化的第一原则:用向量化操作替代 Python 循环,并善用 eval() / query() 加速表达式计算。

一、向量化操作

pandas / NumPy 的底层运算由 C 实现,向量化操作比 Python 循环快数十到数百倍。

import pandas as pd
import numpy as np
 
df = pd.DataFrame({'a': np.random.rand(1_000_000),
                   'b': np.random.rand(1_000_000)})
 
# 推荐:向量化
df['c'] = df['a'] + df['b']
df['d'] = np.where(df['a'] > 0.5, df['b'], 0)
df['e'] = (df['a'] * 2).clip(0, 1)

常用向量化手段

场景向量化写法
条件赋值np.where(cond, x, y)
聚合df.groupby('key')['val'].sum()
窗口df['val'].rolling(7).mean()
字符串df['name'].str.upper()
日期pd.to_datetime(df['date'])
移位df['val'].shift(1)

二、避免循环

应避免的反模式

# 反模式 1:iterrows 逐行访问(最慢)
for idx, row in df.iterrows():
    df.loc[idx, 'new'] = row['a'] + row['b']
 
# 反模式 2:itertuples(略快但仍慢)
for row in df.itertuples():
    pass
 
# 反模式 3:逐 cell 赋值
for i in range(len(df)):
    df.iloc[i, 2] = df.iloc[i, 0] * 2

推荐的替代方案

# 向量化直接计算
df['new'] = df['a'] + df['b']
 
# 复杂逻辑 → apply 按行(仍比 iterrows 快)
df['new'] = df.apply(lambda r: custom_func(r['a'], r['b']), axis=1)
 
# 分组后变换
df['zscore'] = df.groupby('group')['val'].transform(
    lambda x: (x - x.mean()) / x.std()
)
 
# 条件逻辑 → np.select
conditions = [df['a'] > 0.8, df['a'] > 0.5, df['a'] > 0.2]
choices    = ['high', 'mid', 'low']
df['level'] = np.select(conditions, choices, default='very_low')

何时不得不用循环

  • 需要依赖前一行结果的迭代算法(可用 numba 加速)
  • 无法向量化的自定义业务逻辑(可用 apply + axis=1)

三、eval() / query()

eval() 使用 numexpr 库对表达式进行并行加速,query() 基于 eval 实现快速过滤。

pd.eval() — 表达式求值

# 普通方式
df['result'] = (df['a'] + df['b']) / (df['c'] - 1)
 
# eval 加速(适合大规模 DataFrame)
df['result'] = pd.eval("(df['a'] + df['b']) / (df['c'] - 1)")
 
# 支持局部变量
mean_a = df['a'].mean()
df['result'] = pd.eval("df['a'] - @mean_a")   # @ 引用 Python 变量

df.eval() — 列间运算

# 直接引用列名
df.eval("result = (a + b) / (c - 1)", inplace=True)
 
# 条件表达式
df.eval("flag = a > 0.5 and b < 0.3", inplace=True)

df.query() — 条件过滤

# 普通过滤
df_filtered = df[(df['a'] > 0.5) & (df['b'] < 0.3)]
 
# query 写法(更简洁、更快)
df_filtered = df.query("a > 0.5 and b < 0.3")
 
# 引用外部变量
threshold = 0.8
df_filtered = df.query("a > @threshold")
 
# 字符串列条件
df.query("name.str.startswith('张')")

适用条件

条件说明
数据量大> 10 万行时收益明显
表达式复杂多列参与的算术 / 比较运算
已安装 numexprpip install numexpr
不适用需聚合、含 Python 对象方法(非 str 访问器)

使用 engine='python' 兜底

pd.eval("df.a + df.b", engine='python')   # 无 numexpr 时

相关笔记