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

Python Pandas:按多字段分组并对多列应用自定义统计函数

问题描述

现有如下示例DataFrame:

id  date        hrz tenor   1       2       3       4
AAA 16/03/2010  2   6m      0.54    0.54    0.78    0.19
AAA 30/03/2010  2   6m      0.05    0.67    0.20    0.03
AAA 13/04/2010  2   6m      0.64    0.32    0.13    0.20
AAA 27/04/2010  2   6m      0.99    0.53    0.38    0.97
AAA 11/05/2010  2   6m      0.46    0.90    0.11    0.14
AAA 25/05/2010  2   6m      0.41    0.06    0.96    0.31
AAA 08/06/2010  2   6m      0.19    0.73    0.58    0.80
AAA 22/06/2010  2   6m      0.40    0.95    0.14    0.56
AAA 06/07/2010  2   6m      0.22    0.74    0.85    0.94
AAA 20/07/2010  2   6m      0.34    0.17    0.03    0.77
AAA 03/08/2010  2   6m      0.13    0.32    0.39    0.95
AAA 16/03/2010  2   1y      0.54    0.54    0.78    0.19
AAA 30/03/2010  2   1y      0.05    0.67    0.20    0.03
AAA 13/04/2010  2   1y      0.64    0.32    0.13    0.20
AAA 27/04/2010  2   1y      0.99    0.53    0.38    0.97
AAA 11/05/2010  2   1y      0.46    0.90    0.11    0.14
AAA 25/05/2010  2   1y      0.41    0.06    0.96    0.31
AAA 08/06/2010  2   1y      0.19    0.73    0.58    0.80
AAA 22/06/2010  2   1y      0.40    0.95    0.14    0.56
AAA 06/07/2010  2   1y      0.22    0.74    0.85    0.94
AAA 20/07/2010  2   1y      0.34    0.17    0.03    0.77
AAA 03/08/2010  2   1y      0.13    0.32    0.39    0.95

需要按id、hrz、tenor字段分组,对列1、2、3、4应用以下两个自定义函数:

import scipy.stats
import numpy as np

def ks_test(x):
    return scipy.stats.kstest(np.sort(x), 'uniform')[0]

def cvm_test(x):
    n = len(x)
    i = np.arange(1, n + 1)
    x = np.sort(x)
    w2 = (1 / (12 * n)) + np.sum((x - ((2 * i - 1) / (2 * n))) ** 2)
    return w2

最终希望得到如下格式的输出DataFrame(数值为示例):

id   hrz    tenor   test        1       2       3       4
AAA  2      6m      ks_test     0.04    0.06    0.02    0.03
AAA  2      6m      cvm_test    0.09    0.17    0.03    0.05
AAA  2      1y      ks_test     0.04    0.06    0.02    0.03
AAA  2      1y      cvm_test    0.09    0.17    0.03    0.05
解决方案

可以通过以下步骤实现需求:

  • 导入必要库并定义函数
    确保先导入pandas、numpy和scipy.stats,并定义好两个测试函数:
import pandas as pd
import numpy as np
import scipy.stats

def ks_test(x):
    return scipy.stats.kstest(np.sort(x), 'uniform')[0]

def cvm_test(x):
    n = len(x)
    i = np.arange(1, n + 1)
    x = np.sort(x)
    w2 = (1 / (12 * n)) + np.sum((x - ((2 * i - 1) / (2 * n))) ** 2)
    return w2
  • 分组并应用函数
    使用groupby按指定字段分组,然后对目标列同时应用两个函数:
# 假设原始数据存储在df中
grouped = df.groupby(['id', 'hrz', 'tenor'])[[1,2,3,4]].agg([ks_test, cvm_test])
  • 调整列结构匹配目标格式
    上述操作会生成多层列索引,需要重排结构并添加test列:
# 重排索引,将测试名称转为行维度
grouped = grouped.unstack(level=1).stack(level=0).reset_index()
# 重命名列名
grouped.columns = ['id', 'hrz', 'tenor', 'test', 1, 2, 3, 4]
# 排序确保顺序与示例一致
grouped = grouped.sort_values(['id', 'hrz', 'tenor', 'test']).reset_index(drop=True)

或者用更直观的分两次计算再合并的方式:

# 分别计算两个测试的结果
ks_result = df.groupby(['id', 'hrz', 'tenor'])[[1,2,3,4]].apply(ks_test).reset_index()
ks_result['test'] = 'ks_test'

cvm_result = df.groupby(['id', 'hrz', 'tenor'])[[1,2,3,4]].apply(cvm_test).reset_index()
cvm_result['test'] = 'cvm_test'

# 合并结果并调整列顺序
final_df = pd.concat([ks_result, cvm_result], ignore_index=True)
final_df = final_df[['id', 'hrz', 'tenor', 'test', 1, 2, 3, 4]]
final_df = final_df.sort_values(['id', 'hrz', 'tenor', 'test']).reset_index(drop=True)

执行后,final_df即为目标格式的输出结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 13:14:54