如何在python中获取行基础行合计中每个值的百分比

如何在python中获取行基础行合计中每个值的百分比,python,pandas,Python,Pandas,我有以下数据: id hours class 1 67.91 V 1 65.56 V 1 51.14 V 1 41.51 V 1 33.55 V 1 26.45 G 1 26.09 V 1 25.77 G 1 25.50 P 1 25.13 G 1 24.49 P 1 21.88 B 1

我有以下数据:

id  hours       class
1   67.91       V
1   65.56       V
1   51.14       V
1   41.51       V
1   33.55       V
1   26.45       G
1   26.09       V
1   25.77       G
1   25.50       P
1   25.13       G
1   24.49       P
1   21.88       B
1   18.57       V
1   17.90       B
...

18  92.2        B
18  81.06       V
18  70.48       V
18  67.10       B
18  62.92       B
18  62.88       V
18  54.36       B
18  52.77       V
18  44.55       V
18  40.61       P
18  40.51       P
18  40.06       V
18  37.67       V
18  33.78       B
我基本上需要以pivot格式获取数据,并计算每个班级的总学时占每个家庭总学时的百分比 数据:

预期产出:

id  B       G       P       V       Total
1   8.44%   16.41%  10.60%  64.55%  100.00%
18  39.74%  0.0%    10.39%  49.87%  100.00%
有人能帮我吗?这必须按id/行进行。 数据位于pandas数据框中。

我认为您需要+
sum
+或用于旋转:

df = df.groupby(['id','class'])['hours'].sum().unstack(fill_value=0)

然后除以每行的总和,再乘以
100
,最后添加新列
Total
,如果得到
100
,请检查,谢谢您的想法:

df = df.div(df.sum(1), 0).mul(100).round(2).assign(Total=lambda df: df.sum(axis=1))
print (df)
class      B      G      P      V  Total
id                                      
1       8.44  16.41  10.60  64.55  100.0
18     39.74   0.00  10.39  49.87  100.0
对于百分比转换为
字符串
s并添加
%

df1 = df.astype(str) + '%'
print (df1)
class       B       G       P       V   Total
id                                           
1       8.44%  16.41%   10.6%  64.55%  100.0%
18     39.74%    0.0%  10.39%  49.87%  100.0%
计时

np.random.seed(123)
N = 100000
L = list('BGPV')

df = pd.DataFrame({'class': np.random.choice(L, N),
                   'hours':np.random.rand(N),
                   'id':np.random.randint(20000, size=N)})
print (df)


def dark1(df):
    ndf = df.groupby('id').apply(lambda x : x.groupby('class')['hours'].sum()/x['hours'].sum())\
                          .reset_index().pivot(columns='class',index='id')*100
    return ndf.assign(Total=ndf.sum(1)).fillna(0)

def dark2(df):
    one =  df.groupby('id')['hours'].sum()
    two = df.pivot_table(index='id',values='hours',columns='class',aggfunc=sum)

    ndf = pd.DataFrame(two.values / one.values[:,None]*100,columns=two.columns)
    return ndf.assign(Total=ndf.sum(1)).fillna(0)

def jez1(df):
    df = df.groupby(['id','class'])['hours'].sum().unstack(fill_value=0)
    return df.div(df.sum(1), 0).mul(100).assign(Total=lambda df: df.sum(axis=1))

def jez2(df):
    df = df.pivot_table(index='id', columns='class', values='hours', aggfunc='sum', fill_value=0)
    return df.div(df.sum(1), 0).mul(100).assign(Total=lambda df: df.sum(axis=1))

print (dark1(df))
print (dark2(df))
print (jez1(df))
print (jez2(df))

警告

考虑到组的数量,结果不能解决性能问题,这将影响其中一些解决方案的计时。

我认为您需要+
sum
+或用于旋转:

df = df.groupby(['id','class'])['hours'].sum().unstack(fill_value=0)

然后除以每行的总和,再乘以
100
,最后添加新列
Total
,如果得到
100
,请检查,谢谢您的想法:

df = df.div(df.sum(1), 0).mul(100).round(2).assign(Total=lambda df: df.sum(axis=1))
print (df)
class      B      G      P      V  Total
id                                      
1       8.44  16.41  10.60  64.55  100.0
18     39.74   0.00  10.39  49.87  100.0
对于百分比转换为
字符串
s并添加
%

df1 = df.astype(str) + '%'
print (df1)
class       B       G       P       V   Total
id                                           
1       8.44%  16.41%   10.6%  64.55%  100.0%
18     39.74%    0.0%  10.39%  49.87%  100.0%
计时

np.random.seed(123)
N = 100000
L = list('BGPV')

df = pd.DataFrame({'class': np.random.choice(L, N),
                   'hours':np.random.rand(N),
                   'id':np.random.randint(20000, size=N)})
print (df)


def dark1(df):
    ndf = df.groupby('id').apply(lambda x : x.groupby('class')['hours'].sum()/x['hours'].sum())\
                          .reset_index().pivot(columns='class',index='id')*100
    return ndf.assign(Total=ndf.sum(1)).fillna(0)

def dark2(df):
    one =  df.groupby('id')['hours'].sum()
    two = df.pivot_table(index='id',values='hours',columns='class',aggfunc=sum)

    ndf = pd.DataFrame(two.values / one.values[:,None]*100,columns=two.columns)
    return ndf.assign(Total=ndf.sum(1)).fillna(0)

def jez1(df):
    df = df.groupby(['id','class'])['hours'].sum().unstack(fill_value=0)
    return df.div(df.sum(1), 0).mul(100).assign(Total=lambda df: df.sum(axis=1))

def jez2(df):
    df = df.pivot_table(index='id', columns='class', values='hours', aggfunc='sum', fill_value=0)
    return df.div(df.sum(1), 0).mul(100).assign(Total=lambda df: df.sum(axis=1))

print (dark1(df))
print (dark2(df))
print (jez1(df))
print (jez2(df))

警告


考虑到组的数量,结果不会解决性能问题,这将影响其中一些解决方案的计时。

另一种方法是使用
嵌套的groupby
,即

ndf = df.groupby('id').apply(lambda x : x.groupby('class')['hours'].sum()/x['hours'].sum())\
                      .reset_index().pivot(columns='class',index='id')*100
ndf = ndf.assign(Total=ndf.sum(1)).fillna(0)

           hours                                  Total
class          B         G          P          V       
id                                                     
1       8.437798  16.40683  10.603457  64.551914  100.0
18     39.741341         0  10.387349  49.871311  100.0
或:


另一种方法是使用
nestedgroupby

ndf = df.groupby('id').apply(lambda x : x.groupby('class')['hours'].sum()/x['hours'].sum())\
                      .reset_index().pivot(columns='class',index='id')*100
ndf = ndf.assign(Total=ndf.sum(1)).fillna(0)

           hours                                  Total
class          B         G          P          V       
id                                                     
1       8.437798  16.40683  10.603457  64.551914  100.0
18     39.741341         0  10.387349  49.871311  100.0
或:


因此,它不是一个代码编写服务。告诉我们您尝试了什么,遇到了什么障碍,所以这不是一个代码编写服务。告诉我们您尝试了什么,遇到了什么障碍分配
Total=100
不是一个好办法。最好将其用作其余计算的内置检查:
Total=lambda df:df.sum(axis=1)
assigning
Total=100
不是一个好办法。最好将其用作其余计算的内置检查:
Total=lambda df:df.sum(axis=1)
这不是一个好主意,我认为性能应该不好,我将测试它。每当我们看到apply时,我们可以直接说它对性能不好,这只是解决它的另一种简单方法。不,我认为apply没有问题,但是apply和inside groupby不好。@jezrael我添加了另一个方法,两个检查一次。这不是个好主意,我想,性能应该不好,我要测试它。每当我们看到apply,我们可以直接说它对性能不好,这只是另一种简单的解决方法。不,我认为apply没有问题,但应用和内部groupby并不好。@jezrael我添加了另一个方法,两个人检查一次。