悠悠楠杉
利用pandas.assign与矢量化操作高效处理含移位列的行级最大值计算
利用 pandas.assign 与矢量化操作高效处理含移位列的行级最大值计算
在数据清洗和特征工程的实际场景中,我们经常需要对每一行中的多个列进行横向比较,找出最大值、最小值或特定条件下的极值。当这些列存在“移位”关系——例如某列是前一列的时间滞后版本(lag)或滚动窗口统计结果时,传统逐行遍历的方式不仅效率低下,还容易引发性能瓶颈。借助 pandas.assign 方法结合矢量化操作,我们可以优雅且高效地完成这类任务。
假设我们有一份销售数据表,包含某商品在过去五天的日销售额,字段分别为 sales_d1 到 sales_d5,分别代表第1天至第5天的销售额。现在我们需要新增一个字段 max_sales_last3days,表示从当天往前推三天内的最高销售额。这种结构天然带有“移位”特性:每一天的数据都是前一天的“右移”。
若采用传统的 apply(lambda row: ..., axis=1) 方式,虽然逻辑清晰,但随着数据量增长,性能会急剧下降。更优解是利用 pandas 的矢量化能力,将多列拼接为二维数组,再沿行方向应用 np.max 或 DataFrame.max(axis=1)。
具体实现如下:
python
import pandas as pd
import numpy as np
构造示例数据
data = {
'product': ['A', 'B', 'C', 'D'],
'salesd1': [120, 80, 95, 110],
'salesd2': [130, 85, 90, 115],
'salesd3': [140, 90, 85, 120],
'salesd4': [135, 95, 100, 125],
'sales_d5': [150, 100, 105, 130]
}
df = pd.DataFrame(data)
为了计算每行最后三天(即 d3、d4、d5)的最大值,我们可以直接选取相关列并使用 .max(axis=1):
python
df = df.assign(max_sales_last3days=df[['sales_d3', 'sales_d4', 'sales_d5']].max(axis=1))
这里的关键在于 assign 方法返回新的 DataFrame,避免了原地修改,保证了函数式编程的纯净性,同时代码可读性强。更重要的是,.max(axis=1) 是完全矢量化的操作,底层由 C 实现,远快于 Python 层面的循环。
进一步拓展,如果需求变为动态窗口——比如对每个产品,取最近非空的三个数值中的最大值,我们就需要考虑缺失值的影响。此时仍可通过 apply 配合 pd.Series.dropna 实现,但更好的方式是保持矢量化思维:
python
window_cols = ['sales_d3', 'sales_d4', 'sales_d5']
df = df.assign(
recent_max=lambda x: np.nanmax(x[window_cols].values, axis=1)
)
np.nanmax 能自动忽略 NaN 值,在大多数情况下足够胜任。若需更精细控制(如至少有两个有效值才计算),可结合 notnull().sum() 进行掩码判断,依然保持批量处理优势。
此外,当移位列具有规律命名时(如 salesd1 ~ salesd5),可用字符串匹配快速筛选目标列:
python
sales_cols = [col for col in df.columns if col.startswith('sales_d')]
recent_cols = sorted(sales_cols)[-3:] # 最近三天
df = df.assign(rolling_max_3d=df[recent_cols].max(axis=1))
这种方法具备良好的扩展性,即使后续增加 sales_d6,也能自动适配。
