将多重索引列除以总和以创建百分比

6 投票
2 回答
2178 浏览
提问于 2025-04-18 18:43

我有一个数据框,它是从一个透视表创建的,样子大概是这样的:

import pandas as pd
d = {('company1', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('company1', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('company1', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},
('company2', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('company2', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('company2', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},}

df = pd.DataFrame(d)

                company1    company2
                FN  FP  TP  FN  FP  TP
April- 2012     112 0   0   112 0   0
April- 2013     370 544 140 370 544 140
April- 2014     499 50  24  499 50  24
August- 2012    431 0   0   431 0   0
August- 2013    496 0   0   496 0   0
August- 2014    221 426 77  221 426 77

我想要遍历多重索引列的上层,计算每个公司的总和,然后把每个公司的数值除以这个总和,得到一个百分比:

                company1     company2
                FN  FP  TP   FN    FP   TP
April- 2012     1   0   0    1   0  0
April- 2013     .35 .51 .13  .35   .51  .13
April- 2014     .87 .09 .03  .87   .09  .03
etc.

我事先不知道公司的名字。这是昨天问的一个问题的变种:对多重索引列求和

2 个回答

1

虽然joris提供的解决方案很好用,但我想补充一点,当Multiindex有更多层级时,这个方法就不管用了。以下是我的一些解决方案,部分参考了一个StackOverflow的帖子(按组归一化DataFrame)和Pandas的文档(http://pandas.pydata.org/pandas-docs/stable/groupby.html)。

d = {
('X', 'company1', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('X', 'company1', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('X', 'company1', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},
('X', 'company2', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('X', 'company2', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('X', 'company2', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},
('Y','company1', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('Y','company1', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('Y','company1', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},
('Y','company2', 'False Negative'): {'April- 2012': 112.0, 'April- 2013': 370.0, 'April- 2014': 499.0, 'August- 2012': 431.0, 'August- 2013': 496.0, 'August- 2014': 221.0},
('Y','company2', 'False Positive'): {'April- 2012': 0.0, 'April- 2013': 544.0, 'April- 2014': 50.0, 'August- 2012': 0.0, 'August- 2013': 0.0, 'August- 2014': 426.0},
('Y','company2', 'True Positive'): {'April- 2012': 0.0, 'April- 2013': 140.0, 'April- 2014': 24.0, 'August- 2012': 0.0, 'August- 2013': 0.0,'August- 2014': 77.0},
}

df = pd.DataFrame(d)
# extrapolation of original method: not working!
# df.div(df.sum(axis=1,level=[0,1]), level=[0,1]) #  does not work

# alternative 1: replicating the sums for each company to fit the number of columns using numpy
df.div(np.repeat(df.sum(axis=1,level=[0,1]).values, 3, axis=1), axis=1)

# alternative 2: stacking, grouping, transforming and unstacking
df.columns.names = ['top', 'company', 'result'] # naming column levels for convenience 
df.\
    stack(["top", "company", "result"]).\
    groupby(level=[0,1,2]).\
    transform(lambda x: (x / x.sum(axis=0))).\
    unstack(["top", "company", "result"])
6

你可以使用 div 方法来进行求和后再进行除法运算(这样你可以指定匹配的级别):

df.div(df.sum(axis=1, level=0), level=0)

撰写回答