代码之家  ›  专栏  ›  技术社区  ›  Richard

使用groupby后恢复标准的单索引数据帧

  •  0
  • Richard  · 技术社区  · 6 年前

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

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

    更新: class per_apop . 理想情况下,函数 f 可以返回多列,也可能返回多行。也许用

    return pd.DataFrame(data={'per_apop': [np.sum(per_area*per_pop),2], 'sue':[1,3]})
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   James    6 年前

    您可以选择要重置的级别,也可以选择是否要使用 reset_index . 在您的案例中,您最终得到了一个具有两个级别的多索引: class 还有一个没有名字的。 重置索引 允许您重置整个索引(默认)或仅重置所需的级别。在下面的示例中 最后的 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
    

    编辑:

    推动 将索引级别返回到数据帧,只需调用 .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
    
        2
  •  1
  •   BENY    6 年前

    使您的self-def函数返回 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
    
        3
  •  0
  •   denis    4 年前


    测验 func 对于 df.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