如何在熊猫中按组获得类别的百分比

时间:2021-02-26 21:03:48

标签: python pandas

如果之前有人问过类似的问题,我很抱歉,我四处搜索但找不到解决方案。

我的数据集看起来像这样

data1 = {'Group':['Winner','Winner','Winner','Loser','Loser','Loser'],
        'MathStudy': ['Read','Read','Notes','Cheat','Cheat','Read'],
        'ScienceStudy': ['Notes','Read','Cheat','Cheat','Read','Notes']}
df1 = pd.DataFrame(data=data1)

enter image description here

我想获得每个组的每个类别的总数百分比,如下所示。在我的数据集中,赢家和输家的数量会发生变化,因此非常感谢灵活的解决方案。 enter image description here

先谢谢你!

3 个答案:

答案 0 :(得分:4)

DataFrame.meltcrosstabnormalize 参数一起使用:

df1 = df1.melt('Group', var_name='Type')

df2 = pd.crosstab([df1['Group'], df1['Type']], df1['value'], normalize=0)
print (df2)
value                   Cheat     Notes      Read
Group  Type                                      
Loser  MathStudy     0.666667  0.000000  0.333333
       ScienceStudy  0.333333  0.333333  0.333333
Winner MathStudy     0.000000  0.333333  0.666667
       ScienceStudy  0.333333  0.333333  0.333333
 

最后如果需要 MultiIndex 到带有删除 value 列名的列添加 DataFrame.rename_axisDataFrame.reset_index

df2 = df2.rename_axis(columns=None).reset_index()
print (df2)
    Group          Type     Cheat     Notes      Read
0   Loser     MathStudy  0.666667  0.000000  0.333333
1   Loser  ScienceStudy  0.333333  0.333333  0.333333
2  Winner     MathStudy  0.000000  0.333333  0.666667
3  Winner  ScienceStudy  0.333333  0.333333  0.333333

答案 1 :(得分:4)

@jezrael 的解决方案很直观,而且我会直接做。但是,我最近了解到 melt 通常表现不佳。如果性能很重要,这是一个替代方案,例如在重复使用的代码中:

g = df1.groupby('Group')
cols = ['MathStudy', 'ScienceStudy']
out = (pd.concat({col:g[col].value_counts(normalize=True) for col in cols})
   .unstack(level=-1, fill_value=0)
)

带运行时间:

2.9 ms ± 96.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

melt 方法相比:

9.44 ms ± 261 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

输出:

                        Cheat     Notes      Read
MathStudy    Loser   0.666667  0.000000  0.333333
             Winner  0.000000  0.333333  0.666667
ScienceStudy Loser   0.333333  0.333333  0.333333
             Winner  0.333333  0.333333  0.333333

注意pd.crosstab 本质上是 groupby(),带有一些额外的簿记。两列上的 groupby 通常要慢得多。

答案 2 :(得分:2)

这是另一种选择:

g = df.set_index('Group').stack().str.get_dummies().groupby(level=[0,1]).sum()
g.div(g.sum(axis=1),axis=0).round(2)