使用PANDAS GroupBy 应用后,恢复标准的单个指数数据框架



我想在python dataframe中的每个组应用自定义还原功能。该函数通过执行组合组的几个列的操作将组减少为单行。

我已经实现了这样的实现:

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]})

您可以选择要重置的级别以及是否要使用reset_index保留索引。就您而言,您最终获得了一个具有2个级别的多指数: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

编辑:

要将索引的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

使您的自我函数返回 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

尽管您有一个很好的答案,但建议:
在第一组上测试df.groupby(...).apply( func )func,例如:

agroupby = df.groupby(...)  
for key, groupdf in agroupby:  # an iterator -> (key, groupdf) ... pairs
    break  # get the first pair
print( "n-- first groupdf: len %d  type %s n%s" % (
        len(groupdf), type(groupdf), groupdf ))  # DataFrame
test = myfunc( groupdf )
    # groupdf .col [col] [[col ...]] .set_index .resample ... as usual

最新更新