【问题标题】:Pandas - Groupby and aggregate over multiple columnsPandas - Groupby 并聚合多列
【发布时间】:2019-10-12 02:53:19
【问题描述】:

我正在尝试将 groupby 中的值聚合到多个列上。我来自 R/dplyr 世界,我想要的通常可以使用 group_by/summarize 在一行中实现。我试图找到一种使用熊猫来实现这一目标的同样优雅的方法。

考虑下面的输入数据集。我想按状态聚合并将列 v1 计算为 v1 = sum(n1)/sum(d1) by state

使用 dplyr 的 r-code 如下:

input %>% group_by(state) %>% 
  summarise(v1=sum(n1)/sum(d1),
            v2=sum(n2)/sum(d2))

在 Python 中是否有一种优雅的方式来执行此操作?我在堆栈溢出答案here 中找到了一种稍微冗长的方法来获取我想要的内容。 从链接中复制修改后的 python-code

In [14]: s = mn.groupby('state', as_index=False).sum()

In [15]: s['v1'] = s['n1'] / s['d1']

In [16]: s['v2'] = s['n2'] / s['d2']
In [17]: s[['state', 'v1', 'v2']]

输入数据集

state n1    n2     d1  d2
CA   100   1000    1   2
FL   200   2000    2   4
CA   300   3000    3   6
AL   400   4000    4   8
FL   500   5000    5   2
NY   600   6000    6   4
CA   700   7000    7   6

输出

state   v1           v2
AL      100   500.000000
CA      100   500.000000
NY      100  1500.000000
CA      100  1166.666667
FL      100  1166.666667

【问题讨论】:

    标签: python pandas pandas-groupby


    【解决方案1】:

    DataFrame.assignDataFrame.reindex 的一种可能解决方案:

    df = (mn.groupby('state', as_index=False)
            .sum()
            .assign(v1 = lambda x: x['n1'] / x['d1'], v2 = lambda x: x['n2'] / x['d2'])
            .reindex(['state', 'v1', 'v2'], axis=1))
    
    print (df)
      state     v1           v2
    0    AL  100.0   500.000000
    1    CA  100.0   785.714286
    2    FL  100.0  1166.666667
    3    NY  100.0  1500.000000
    

    另一个带有GroupBy.apply 和自定义 lambda 函数:

    df = (mn.groupby('state')
            .apply(lambda x: x[['n1','n2']].sum() / x[['d1','d2']].sum().values)
            .reset_index()  
            .rename(columns={'n1':'v1', 'n2':'v2'})
          )
    print (df)
      state     v1           v2
    0    AL  100.0   500.000000
    1    CA  100.0   785.714286
    2    FL  100.0  1166.666667
    3    NY  100.0  1500.000000
    

    【讨论】:

      【解决方案2】:

      另一种解决方案:

      def func(x):
          u = x.sum()
          return pd.Series({'v1':u['n1']/u['d1'],
                            'v2':u['n2']/u['d2']})
      
      df.groupby('state').apply(func)
      

      输出:

               v1     v2
      state       
      AL      100.0   500.000000
      CA      100.0   785.714286
      FL      100.0   1166.666667
      NY      100.0   1500.000000
      

      【讨论】:

        【解决方案3】:

        这是与 R 中相同的方法:

        >>> from datar.all import f, tribble, group_by, summarise, sum
        >>> 
        >>> input = tribble(
        ...     f.state, f.n1,    f.n2,     f.d1,  f.d2,
        ...     "CA",    100,     1000,     1,     2,
        ...     "FL",    200,     2000,     2,     4,
        ...     "CA",    300,     3000,     3,     6,
        ...     "AL",    400,     4000,     4,     8,
        ...     "FL",    500,     5000,     5,     2,
        ...     "NY",    600,     6000,     6,     4,
        ...     "CA",    700,     7000,     7,     6,
        ... )
        >>> 
        >>> input >> group_by(f.state) >> \
        ...   summarise(v1=sum(f.n1)/sum(f.d1),
        ...             v2=sum(f.n2)/sum(f.d2))
             state        v1           v2
          <object> <float64>    <float64>
        0       AL     100.0   500.000000
        1       CA     100.0   785.714286
        2       FL     100.0  1166.666667
        3       NY     100.0  1500.000000
        

        我是datar 包的作者。

        【讨论】:

          【解决方案4】:

          另一个选项是 pipe 函数,其中 groupby 对象是可重复使用的:

          (df.groupby('state')
             .pipe(lambda df: pd.DataFrame({'v1' : df.n1.sum() / df.d1.sum(), 
                                            'v2' : df.n2.sum() / df.d2.sum()})
                  )
          ) 
                    v1           v2
          state                    
          AL     100.0   500.000000
          CA     100.0   785.714286
          FL     100.0  1166.666667
          NY     100.0  1500.000000
          

          另一种选择是在分组之前将列转换为 MultiIndex:

          temp = temp = df.set_index('state')
          temp.columns = temp.columns.str.split('(\d)', expand=True).droplevel(-1)
          
          (temp.groupby('state')
               .sum()
               .pipe(lambda df: df.n /df.d)
               .add_prefix('v')
          )
           
                    v1           v2
          state                    
          AL     100.0   500.000000
          CA     100.0   785.714286
          FL     100.0  1166.666667
          NY     100.0  1500.000000
          

          另一种方式,仍然使用 MultiIndex 选项,同时避免 groupby:

          # keep the index, necessary for unstacking later
          temp = df.set_index('state', append=True) 
          
          # convert the columns to a MultiIndex
          temp.columns = temp.columns.map(tuple)
          
          # this works because the index is unique
          (temp.unstack('state')
               .sum()
               .unstack([0,1])
               .pipe(lambda df: df.n / df.d)
               .add_prefix('v')
          ) 
                    v1           v2
          state                    
          AL     100.0   500.000000
          CA     100.0   785.714286
          FL     100.0  1166.666667
          NY     100.0  1500.000000
          
          

          【讨论】:

            猜你喜欢
            • 2021-11-01
            • 2019-12-22
            • 2020-11-05
            • 2017-07-02
            • 2017-07-20
            • 2023-03-07
            • 1970-01-01
            • 2017-06-07
            • 2014-11-23
            相关资源
            最近更新 更多