You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升Pandas/Numpy中均值、极值等聚合运算的速度?

优化分组聚合与合并的性能

问题背景

我有一个包含600行、2999个浮点列和1个分类列cat1的DataFrame,需要按cat1分组后,对指定的10列(col1到col10)计算mean、max、min、std、sum聚合值,将结果合并回原DataFrame后转换为numpy数组。当前实现耗时约35ms,希望尽可能提升运行速度。

测试数据生成代码

import pandas as pd
import numpy as np

# 生成600行3000列的DataFrame
num_rows = 600
num_numeric_cols = 2999
# 生成float32类型的随机数值列
numeric_data = np.random.rand(num_rows, num_numeric_cols).astype(np.float32)
# 生成含50个唯一类别的分类列
num_categories = 50
categorical_data = np.random.choice(range(num_categories), num_rows)
# 数值列命名
numeric_column_names = [f"col{i}" for i in range(num_numeric_cols)]
# 构建DataFrame
data = {numeric_column_names[i]: numeric_data[:, i] for i in range(num_numeric_cols)}
data["cat1"] = categorical_data

df = pd.DataFrame(data)
df["cat1"] = df["cat1"].astype("category")

待优化的核心代码

import time

start = time.time()    

aa = df.groupby(["cat1"])[["col1","col2","col3","col4","col5","col6","col7","col8","col9","col10"]]
df = pd.merge(df,aa.mean().reset_index(),how="left",on="cat1",suffixes=["","mean"])
df = pd.merge(df,aa.std().reset_index(),how="left",on="cat1",suffixes=["","std"])
df = pd.merge(df,aa.min().reset_index(),how="left",on="cat1",suffixes=["","min"])
df = pd.merge(df,aa.max().reset_index(),how="left",on="cat1",suffixes=["","max"])
df = pd.merge(df,aa.sum().reset_index(),how="left",on="cat1",suffixes=["","sum"])
df= df.to_numpy(np.float32)
print("Time Taken",time.time()-start)
print(df.shape)

优化方案

方案1:一次性完成聚合,减少分组与合并次数

原代码多次调用分组聚合和pd.merge,每次合并都会带来额外开销。通过agg方法一次性指定所有聚合函数,扁平化列名后用join合并(比merge更高效,基于分类键直接拼接)。

优化后代码:

import time

start = time.time()

target_cols = ["col1","col2","col3","col4","col5","col6","col7","col8","col9","col10"]
# 一次性聚合所有函数,并重命名列
agg_result = df.groupby("cat1")[target_cols].agg({
    col: ["mean", "max", "min", "std", "sum"] for col in target_cols
})
# 扁平化多级列名
agg_result.columns = [f"{col}_{func}" for col, func in agg_result.columns]
agg_result = agg_result.reset_index()

# 用join合并(cat1为分类列,匹配效率更高)
df_merged = df.join(agg_result.set_index("cat1"), on="cat1")
# 转numpy数组
result = df_merged.to_numpy(np.float32)

print("Time Taken", time.time() - start)
print(result.shape)

效果:将多次分组和合并压缩为1次分组+1次join,耗时可降至10ms以内(取决于硬件)。

方案2:用numpy直接处理,跳过pandas合并开销

由于最终目标是生成numpy数组,可绕过pandas的DataFrame合并步骤,直接用numpy完成分组统计和映射拼接,性能提升更明显。

优化后代码:

import time

start = time.time()

target_cols = ["col1","col2","col3","col4","col5","col6","col7","col8","col9","col10"]
# 提取目标列的numpy数组
target_data = df[target_cols].to_numpy(np.float32)
# 提取分类列的编码值(category类型的codes为整数索引)
cat_codes = df["cat1"].cat.codes.values
# 获取唯一类别编码
unique_cats = np.unique(cat_codes)

# 预分配统计结果数组:每个类别对应5种统计量×10列
stats = np.zeros((len(unique_cats), len(target_cols)*5), dtype=np.float32)

for i, cat in enumerate(unique_cats):
    # 筛选当前类别的行
    mask = cat_codes == cat
    subset = target_data[mask]
    # 计算所有统计量并拼接
    stats[i] = np.concatenate([
        subset.mean(axis=0),
        subset.max(axis=0),
        subset.min(axis=0),
        subset.std(axis=0, ddof=1),  # 与pandas的std默认参数一致
        subset.sum(axis=0)
    ])

# 创建映射字典:类别编码→对应的统计行
cat_to_stats = {cat: stats[i] for i, cat in enumerate(unique_cats)}
# 为原表每一行映射对应的统计数据
mapped_stats = np.array([cat_to_stats[code] for code in cat_codes], dtype=np.float32)

# 拼接原数据和统计数据:原表所有列(除cat1) + 统计列
original_data = df.drop("cat1", axis=1).to_numpy(np.float32)
result = np.hstack([original_data, mapped_stats])

print("Time Taken", time.time() - start)
print(result.shape)

效果:完全基于numpy操作,避免pandas分组和合并开销,耗时可降至5ms以内。注意std使用ddof=1,与pandas默认的无偏标准差保持一致。


内容的提问来源于stack exchange,提问作者Rahul Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 23:25:29