【问题标题】:Recover a standard, single-index data frame after using pandas groupby+apply使用 pandas groupby+apply 后恢复一个标准的单索引数据框
【发布时间】:2019-07-23 01:41:52
【问题描述】:

我想对 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


    【解决方案1】:

    您可以使用reset_index 选择要重置的级别以及是否要保留索引。在您的情况下,您最终得到了一个具有 2 个级别的多索引:class 和一个未命名的级别。 reset_index 允许您重置整个索引(默认)或您想要的级别。在以下示例中,last 级别 (-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
    

    【讨论】:

    • 这似乎运作良好。你能解释一下发生了什么吗?
    • 更新了我的答案。
    • 抱歉,我收回了,我可以访问aggdf['per_apop'] 就好了,但是aggdf['class'] 会引发KeyError
    • 哦!您还需要将类返回到数据框中。
    • 是的,我愿意。很抱歉!
    【解决方案2】:

    让你的自定义函数返回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
    

    【讨论】:

    • 这似乎阻止了我每个数据帧返回多于一行,否则两者似乎都运行良好。你能解释一下为什么从DataFrame 切换到Series 会这样吗?
    • @Richard 检查打开的问题github.com/pandas-dev/pandas/issues/9867,groupby.apply 总是有输出不一致的问题
    • 我似乎无法使用此答案为每个组返回多行,我意识到这不是问题的明确部分(自编辑以来),但它是可取的。
    【解决方案3】:

    虽然你有一个很好的答案,一个建议:
    在第一组测试funcdf.groupby(...).apply( 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
    

    【讨论】:

      猜你喜欢
      • 2018-08-22
      • 2023-03-24
      • 2019-08-07
      • 1970-01-01
      • 2017-09-14
      • 2020-12-17
      • 2021-02-28
      • 2016-03-04
      • 2019-09-30
      相关资源
      最近更新 更多