【问题标题】:Using groupby and apply to add column to each group使用 groupby 和 apply 将列添加到每个组
【发布时间】:2016-07-01 12:56:41
【问题描述】:

我有一个以多索引为列的 ​​DataFrame。我想按级别 1 分组并应用一个生成新列的函数。我希望将此计算列添加到每个组中,因此我的数据框可以为每个组添加新列。

我制作了一个小的虚拟脚本和函数来复制我想做的事情。

import pandas as pd
import numpy as np

columns = [('A','julian'),('A','geoffrey'),
       ('B','julian'),('B','geoffrey'),
       ('C','julian'),('C','geoffrey')]

columns = pd.MultiIndex.from_tuples(columns)

dataframe = pd.DataFrame(data=np.random.rand(10,6),columns=columns)

def addColumn(inputDF):
   group = inputDF.columns[0][1]
   inputDF['sum', group] = inputDF.sum(axis=1)
   return inputDF

newColumnsDataframe = dataframe.groupby(level=1, axis=1).apply(addColumn) 

原始数据框如下所示:

      A                   B                   C          
    julian  geoffrey    julian  geoffrey    julian  geoffrey
 0  0.204082  0.073676  0.795725  0.279702  0.258185  0.258112
 1  0.263235  0.096733  0.507324  0.541198  0.525919  0.757652
 2  0.196243  0.028613  0.653408  0.364365  0.174911  0.924733
 3  0.528785  0.831569  0.654160  0.738029  0.940831  0.294473
 4  0.853517  0.263250  0.803087  0.855270  0.701937  0.264698
 5  0.239797  0.069519  0.943544  0.374411  0.189361  0.846647
 6  0.980734  0.290414  0.850097  0.873785  0.903645  0.118713
 7  0.591942  0.088387  0.566298  0.062140  0.568482  0.872064
 8  0.818167  0.061483  0.282050  0.008404  0.449198  0.658370
 9  0.217424  0.427602  0.471933  0.171458  0.390549  0.234426

生成的数据框应如下所示(我分别构建了 sum DataFrame 并将两个数据框连接起来以实现此结果):

      A         B         C       sum         A         B         C  \
   geoffrey  geoffrey  geoffrey  geoffrey    julian    julian    julian   
0  0.073676  0.279702  0.258112  0.611491  0.204082  0.795725  0.258185   
1  0.096733  0.541198  0.757652  1.395584  0.263235  0.507324  0.525919   
2  0.028613  0.364365  0.924733  1.317710  0.196243  0.653408  0.174911   
3  0.831569  0.738029  0.294473  1.864071  0.528785  0.654160  0.940831   
4  0.263250  0.855270  0.264698  1.383219  0.853517  0.803087  0.701937   
5  0.069519  0.374411  0.846647  1.290578  0.239797  0.943544  0.189361   
6  0.290414  0.873785  0.118713  1.282912  0.980734  0.850097  0.903645   
7  0.088387  0.062140  0.872064  1.022590  0.591942  0.566298  0.568482   
8  0.061483  0.008404  0.658370  0.728257  0.818167  0.282050  0.449198   
9  0.427602  0.171458  0.234426  0.833486  0.217424  0.471933  0.390549   

    sum  
   julian  
0  1.257992  
1  1.296478  
2  1.024561  
3  2.123776  
4  2.358542  
5  1.372703  
6  2.734476  
7  1.726721  
8  1.549415  
9  1.079906  

上面脚本中的方法基于对我有意义的内容以及其他人在网上写的关于做这类事情的内容。但是,newColumnsDataframe 仍然只有 6 列,而不是 8 列(每个名称添加一个)。

我确实注意到,当我按级别 = 0(因此按 A、B 或 C)分组并使用转换时(但不是当我在此级别上使用应用时),newColumnsDataframe 确实有 9 列,添加了一个总和列每组。请看下面的代码:

import pandas as pd
import numpy as np

columns = [('A','julian'),('A','geoffrey'),
       ('B','julian'),('B','geoffrey'),
       ('C','julian'),('C','geoffrey')]

columns = pd.MultiIndex.from_tuples(columns)

dataframe = pd.DataFrame(data=np.random.rand(10,6),columns=columns)

def addColumn(inputDF):
    group = inputDF.columns[0][1]
    inputDF[group, 'sum'] = inputDF.sum(axis=1)
    return inputDF

newColumnsDataframe = dataframe.groupby(level=0, axis=1).transform(addColumn)

我一直认为,transform 作用于组内的每一列,而 apply 作用于整个数据框的组。这似乎与此相矛盾。 我还注意到,当我按 level=1 分组并使用变换而不是应用时,它会引发以下错误:

ValueError: Length mismatch: Expected axis has 10 elements, new values have 6 elements

我对发生的事情感到非常困惑。有谁知道为什么当我在 level=0 上使用变换和组时这会起作用。为什么当我做同样的事情但在 level=1 上分组时会出现错误。为什么在任一级别分组并应用该函数不会将列添加到我的最终数据框中?提前致谢!

(PS:这不是我用来添加列的实际DataFrame或函数,只是一个更简单的说明)

【问题讨论】:

  • 你能添加一个想要的输出吗?
  • 值得重复。你预期的最终结果是什么?有趣的问题,良好的路线图,但没有目的地。
  • 我刚刚添加了原始数据框和所需的结果。我希望这会有所帮助!

标签: python pandas transform apply


【解决方案1】:

有点凌乱,但只有一条线:

(df.join(pd.concat({'sum': df.groupby(level=1, axis=1).sum()}, axis=1))
   .sortlevel(level=1, axis=1))

为我制作这个:

          A         B         C       sum         A         B         C  \
   geoffrey  geoffrey  geoffrey  geoffrey    julian    julian    julian   
0  0.073676  0.279702  0.258112  0.611490  0.204082  0.795725  0.258185   
1  0.096733  0.541198  0.757652  1.395583  0.263235  0.507324  0.525919   
2  0.028613  0.364365  0.924733  1.317711  0.196243  0.653408  0.174911   
3  0.831569  0.738029  0.294473  1.864071  0.528785  0.654160  0.940831   
4  0.263250  0.855270  0.264698  1.383218  0.853517  0.803087  0.701937   
5  0.069519  0.374411  0.846647  1.290577  0.239797  0.943544  0.189361   
6  0.290414  0.873785  0.118713  1.282912  0.980734  0.850097  0.903645   
7  0.088387  0.062140  0.872064  1.022591  0.591942  0.566298  0.568482   
8  0.061483  0.008404  0.658370  0.728257  0.818167  0.282050  0.449198   
9  0.427602  0.171458  0.234426  0.833486  0.217424  0.471933  0.390549   

        sum  
     julian  
0  1.257992  
1  1.296478  
2  1.024562  
3  2.123776  
4  2.358541  
5  1.372702  
6  2.734476  
7  1.726722  
8  1.549415  
9  1.079906 

我刚刚说“这是我的df,让我们先按人名分组并求和,然后将这两个求和列加入原始df,然后使用sortlevel 按@987654326 排序@ 和 axis=1。”

所以,“sum”出现在C 列之后的唯一原因只是因为字母s 出现在C 之后。如果您有一个名为x 的列,这将不起作用。不知道这是否重要。

这是我用于娱乐目的的df

df = pd.DataFrame({
      ('C', 'julian'): [0.258185, 0.52591899999999991, 0.17491099999999998, 0.94083099999999997, 0.70193700000000003, 0.189361, 0.90364500000000003, 0.56848199999999993, 0.44919799999999993, 0.39054899999999998],
      ('B', 'geoffrey'): [0.27970200000000001, 0.54119799999999996, 0.36436499999999999, 0.73802900000000005, 0.85527000000000009, 0.37441099999999999, 0.87378500000000003, 0.062140000000000001, 0.008404, 0.171458], 
      ('A', 'julian'): [0.20408199999999999, 0.263235, 0.196243, 0.52878500000000006, 0.85351699999999997, 0.23979699999999998, 0.98073399999999999, 0.59194199999999997, 0.81816699999999998, 0.21742399999999998], 
      ('B', 'julian'): [0.79572500000000002, 0.507324, 0.65340799999999999, 0.65416000000000007, 0.803087, 0.94354400000000005, 0.85009699999999988, 0.56629799999999997, 0.28205000000000002, 0.47193299999999999], 
      ('A', 'geoffrey'): [0.073676000000000005, 0.096733, 0.028613, 0.831569, 0.26324999999999998, 0.069519000000000011, 0.29041400000000001, 0.088387000000000007, 0.061483000000000003, 0.42760200000000004], 
      ('C', 'geoffrey'): [0.25811200000000001, 0.75765199999999999, 0.92473300000000003, 0.29447299999999998, 0.26469799999999999, 0.84664699999999993, 0.11871300000000001, 0.87206399999999995, 0.65837000000000001, 0.23442600000000002]},
      columns=pd.MultiIndex.from_tuples([('A','julian'),('A','geoffrey'), ('B','julian'),('B','geoffrey'), ('C','julian'),('C','geoffrey')]))

已编辑:

这是另一种方法:

sum_columns = [('sum', name) for name in df.columns.levels[1].tolist()]
df[sum_columns] = df.groupby(axis=1, level=1).sum()
df = df.sortlevel(level=1, axis=1)

sum_columns - 看起来像这样[('sum', 'geoffrey'), ('sum', 'julian')]

df[sum_columns] 为级别 1 上的每个名称创建一个新的“总和”列。

如果希望在名称旁边有总和列,请使用sortlevel

【讨论】:

  • 那是一个整洁的!
  • 太棒了!我认为这会起作用(实际的功能和数据框更复杂,但不明白为什么这不会转移)。非常感谢。你知道为什么申请没有工作吗?仍在试图解决这个问题
  • 我的一个想法是在你的函数group = inputDF.columns[0][1] 中返回字符串'julian'。由于您将函数应用于具有多个组('julian'、'geoffrey')的 DataFrameGroupBy 对象,因此它可能不知道为 geoffrey 做什么?我可能错了。 addColumn(dataframe) 确实返回 (sum, julian) 列,但该列中的总和是每行的 julian 和 geoffrey 的总和。我认为您希望按人计算总和,因此您的功能可能需要更改。
  • 我明白了。它返回的列应该只是这个人的总和,对吧?因为我按人分组(级别= 1)然后将功能应用于每个组?或者 .apply 导致函数作为数据帧应用于每个组不是真的吗? (顺便说一句,您的答案在我的实际代码中有效。感谢您的帮助!)
  • 你提出了很好的观点。我不知道(这让我很困扰),因为我查看了您的功能并看到它符合我的预期。如果我弄清楚了,我会告诉你的!
猜你喜欢
  • 2020-09-01
  • 2021-05-03
  • 2020-04-29
  • 2021-08-30
  • 1970-01-01
  • 2020-01-29
  • 2023-01-13
  • 2020-10-26
  • 2014-08-26
相关资源
最近更新 更多