
本文详解如何在 pandas 中对指定列(如 id、hrz、tenor)进行分组,并同时对多个数值列(如 1–4)批量应用多个自定义统计函数(如 ks 检验、cvm 检验),最终生成结构清晰、可直接分析的宽格式结果表。
本文详解如何在 pandas 中对指定列(如 id、hrz、tenor)进行分组,并同时对多个数值列(如 1–4)批量应用多个自定义统计函数(如 ks 检验、cvm 检验),最终生成结构清晰、可直接分析的宽格式结果表。
在金融、风控或统计建模场景中,常需对时间序列数据按资产标识(id)、期限类型(tenor)和参数维度(hrz)分组,并对各列指标独立执行分布检验(如 Kolmogorov-Smirnov 或 Cramér–von Mises)。pandas 提供了灵活高效的 groupby().agg() 接口,配合多级索引与堆叠操作,可优雅实现「多分组键 × 多目标列 × 多自定义函数」的组合聚合。
以下为完整实现流程(假设已导入必要库):
import pandas as pd
import numpy as np
from scipy import stats
# 示例自定义函数(注意:ks_test 返回统计量,非 p-value)
def ks_test(x):
return stats.kstest(np.sort(x), 'uniform')[0]
def cvm_test(x):
n = len(x)
if n < 2:
return np.nan
i = np.arange(1, n + 1)
x_sorted = np.sort(x)
w2 = (1 / (12 * n)) + np.sum((x_sorted - ((2 * i - 1) / (2 * n))) ** 2)
return w2✅ 推荐方案一:使用 pd.concat 构建命名字典(语义清晰、控制力强)
# 定义分组键与目标数值列
group_cols = ['id', 'hrz', 'tenor']
value_cols = df.columns.difference(group_cols + ['date']).tolist() # ['1','2','3','4']
# 分组后对所有 value_cols 同时应用各函数
g = df.groupby(group_cols)[value_cols]
result = pd.concat({
'ks_test': g.agg(ks_test),
'cvm_test': g.agg(cvm_test)
}, names=['test']).reset_index()该方法将每个函数结果作为独立 DataFrame 拼接,自动添加 test 层级索引,并通过 reset_index() 展平为标准宽表。输出列顺序为:test, id, hrz, tenor, 1, 2, 3, 4 —— 完全匹配需求格式。
✅ 推荐方案二:使用 .agg([func1, func2]) + stack()(代码更紧凑)
result = (
df.groupby(group_cols)[value_cols]
.agg([ks_test, cvm_test])
.rename_axis([None, 'test'], axis=1) # 重命名列多级索引第二层为 'test'
.stack(level='test') # 将函数名压入行索引
.reset_index() # 转为常规 DataFrame
)此方式利用 agg() 的列表语法批量注册函数,stack() 自动将函数名转为新列,无需手动拼接,适合函数数量较多时快速扩展。
⚠️ 关键注意事项:
- 自定义函数必须接受
Series或ndarray输入,并返回标量(如float);若返回pd.Series,结果将被展开为多列,需谨慎处理; -
kstest在样本量过小时可能不稳定,建议添加if len(x) 等健壮性检查; - 若原始数据含缺失值,
groupby().agg()默认跳过NaN,但ks_test/cvm_test需确保输入无NaN,推荐前置df[value_cols] = df[value_cols].dropna()或在函数内过滤; - 输出顺序默认按分组键字典序排列,如需保持原始时间顺序,请先对
df按date排序再分组。
两种方案均生成结构一致、列名规范的结果,可直接用于后续可视化(如 sns.catplot(..., hue='test'))或导出分析报告。掌握此模式后,可轻松扩展至任意数量的分组维度、数值列与统计函数组合。

















