使用pandas groupby + apply后恢复标准的单索引数据框

问题描述 投票:0回答:2

我想在Python数据帧中为每个组应用自定义缩减功能。该函数通过执行组合该组的多个列的操作将组简化为单行。

我已经实现了这样:

import pandas as pd
import numpy as np

df = pd.DataFrame(data={
  "afac": np.random.random(size=1000),
  "bfac": np.random.random(size=1000),
  "class":np.random.randint(low=0,high=5,size=1000)
})

def f(group):
  total_area = group['afac'].sum()
  per_area   = (group['afac']/total_area).values
  per_pop    = group['bfac'].values
  return pd.DataFrame(data={'per_apop': [np.sum(per_area*per_pop)]})

aggdf = df.groupby('class').apply(f)

我的输入数据框df看起来像:

>>> df
         afac      bfac  class
0    0.689969  0.992403      0
1    0.688756  0.728763      1
2    0.086045  0.499061      1
3    0.078453  0.198435      2
4    0.621589  0.812233      4

但是我的代码给出了这个多索引数据框:

>>> aggdf
         per_apop
class            
0     0  0.553292
1     0  0.503112
2     0  0.444281
3     0  0.517646
4     0  0.503290

我已经尝试了各种方法来回到“正常”的数据框架,但似乎都没有。

>>> aggdf.reset_index()
   class  level_1  per_apop
0      0        0  0.553292
1      1        0  0.503112
2      2        0  0.444281
3      3        0  0.517646
4      4        0  0.503290

>>> aggdf.unstack().reset_index()
  class  per_apop
                0
0     0  0.553292
1     1  0.503112
2     2  0.444281
3     3  0.517646
4     4  0.503290

如何执行此操作并在之后获得正常的数据框?

更新:输出数据框应包含classper_apop的列。理想情况下,函数f可以返回多列,也可能返回多行。也许用

return pd.DataFrame(data={'per_apop': [np.sum(per_area*per_pop),2], 'sue':[1,3]})
python pandas apply pandas-groupby multi-index
2个回答
2
投票

您可以选择要重置的级别以及是否要使用reset_index保留索引。在您的情况下,您最终得到了一个具有两个级别的多索引:class和一个未命名的索引。 reset_index允许您重置整个索引(默认)或仅重置您想要的级别。在以下示例中,最后一级(-1)正从索引中拉出。通过使用drop=True,它被丢弃而不是作为数据框中的列附加。

aggdf.reset_index(level=-1, drop=True)

       per_apop
class
0      0.476184
1      0.476254
2      0.509735
3      0.502444
4      0.525287

EDIT:

要将索引的class级别推回到数据框,您只需再次调用.reset_index()即可。丑陋,但它的工作。

aggdf.reset_index(level=-1, drop=True).reset_index()

   class  per_apop
0      0  0.515733
1      1  0.497349
2      2  0.527063
3      3  0.515476
4      4  0.494530

或者,您也可以重置索引,然后删除额外的列。

aggdf.reset_index().drop('level_1', axis=1)


   class  per_apop
0      0  0.515733
1      1  0.497349
2      2  0.527063
3      3  0.515476
4      4  0.494530

1
投票

让你的自我反省功能返回Series

def f(group):
  total_area = group['afac'].sum()
  per_area   = (group['afac']/total_area).values
  per_pop    = group['bfac'].values
  return pd.Series(data={'per_apop': np.sum(per_area*per_pop)})
df.groupby('class').apply(f).reset_index()

   class  per_apop
0      0  0.508332
1      1  0.505593
2      2  0.488117
3      3  0.481572
4      4  0.500401
© www.soinside.com 2019 - 2024. All rights reserved.