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

如何一次性对多层索引Pandas DataFrame按组执行行除法?

多层索引DataFrame按组归一化(除以组内首行)的高效实现

问题背景

先构造一个多层索引的Pandas DataFrame:

import numpy as np
import pandas as pd

df1 = pd.DataFrame(np.random.rand(3, 3), index=['a', 'b', 'c'], columns=['col1', 'col2', 'col3'])
df2 = pd.DataFrame(np.random.rand(3, 3), index=['a', 'b', 'c'], columns=['col1', 'col2', 'col3'])
df3 = pd.DataFrame(np.random.rand(3, 3), index=['a', 'b', 'c'], columns=['col1', 'col2', 'col3'])
df = pd.concat([df1, df2, df3], axis=0, keys=['A', 'B', 'C'])

生成的DataFrame示例如下(你的随机数值会与示例不同):

col1        col2        col3
A   a   0.893752    0.554021    0.492867
    b   0.319270    0.263366    0.542281
    c   0.082265    0.635637    0.796405
B   a   0.954748    0.684624    0.488293
    b   0.485414    0.966693    0.211348
    c   0.411648    0.989666    0.028412
C   a   0.701327    0.025172    0.320882
    b   0.073527    0.060885    0.111406
    c   0.169269    0.627686    0.438393

需求

  • 将(A, b)和(A, c)行除以(A, a)行
  • 将(B, b)和(B, c)行除以(B, a)行
  • 将(C, b)和(C, c)行除以(C, a)行

个人尝试

我最初通过循环遍历顶层索引,分别做除法后合并结果:

idx = pd.IndexSlice
ratio_list = [df.loc[idx[x,:], :].div(df.loc[idx[x,'a'], :]) for x in ['A', 'B', 'C']]
ratio = pd.concat(ratio_list, axis=0)

生成的结果如下:

col1        col2        col3
A   a   1.000000    1.000000    1.000000
    b   0.357225    0.475371    1.100259
    c   0.092044    1.147315    1.615864
B   a   1.000000    1.000000    1.000000
    b   0.508422    1.412005    0.432830
    c   0.431159    1.445560    0.058186
C   a   1.000000    1.000000    1.000000
    b   0.104840    2.418784    0.347188
    c   0.241355    24.936324   1.366214

高效解决方案

以下是两种更优的实现方案:groupby-transform写法简洁易读,当DataFrame规模较大时,np.repeat方案的性能优势更显著。

方案一:Pandas groupby-transform(@Smordy提供)

按顶层索引分组,通过transform('first')获取每组首行数据,再直接执行除法:

df.div(df.groupby(level=0).transform('first'))

方案二:Numpy repeat(@ouroboros1提供)

提取每组首行数据,用np.repeat将其重复至与原DataFrame行数一致,再做除法:

df.div(np.repeat(df.loc[(slice(None), ['a']), :].to_numpy(), 3, axis=0))

性能测试

构造更大规模的DataFrame测试两种方案的性能:

nrow = 100  # 可调整行数测试
df = pd.DataFrame(np.random.rand(nrow*3, 3), 
                   columns=['col1', 'col2', 'col3'],
                   index=pd.MultiIndex.from_product([[*'ABC'], ['row' + str(ii) for ii in range(0, nrow)]]))

# Pandas groupby-transform 方案
%timeit df.div(df.groupby(level=0).transform('first'))

# Numpy repeat 方案
%timeit df.div(np.repeat(df.loc[(slice(None), ['row0']), :].to_numpy(), nrow, axis=0))

测试结果显示:当nrow=100时,np.repeat方案的运行速度约为groupby-transform的3倍;若增加DataFrame的列数,两者的速度差距还会进一步扩大。


内容的提问来源于stack exchange,提问作者data-monkey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 14:17:04