【问题标题】:How to use groupby transform across multiple columns如何跨多列使用 groupby 转换
【发布时间】:2016-03-10 01:19:56
【问题描述】:

我有一个大数据框,我按一到 n 列分组,并希望在这些组上跨两列(例如 foo 和 bar)应用一个函数。

这是一个示例数据框:

foo_function = lambda x: np.sum(x.a+x.b)

df = pd.DataFrame({'a':[1,2,3,4,5,6],
                   'b':[1,2,3,4,5,6],
                   'c':['q', 'q', 'q', 'q', 'w', 'w'],  
                   'd':['z','z','z','o','o','o']})

# works with apply, but I want transform:
df.groupby(['c', 'd'])[['a','b']].apply(foo_function)
# transform doesn't work!
df.groupby(['c', 'd'])[['a','b']].transform(foo_function)
TypeError: cannot concatenate a non-NDFrame object

transform 显然无法将多个列组合在一起,因为它分别查看每一列(与 apply 不同)。就速度/优雅而言,下一个最佳选择是什么?例如我可以使用apply,然后使用pd.match 创建df['new_col'],但这有时需要匹配多个groupby 列(col1 和col2),这看起来真的很hacky / 需要相当多的代码。

--> 有没有类似 groupby().transform 的函数可以使用在多个列上工作的函数?如果这不存在,最好的 hack 是什么?

【问题讨论】:

  • 我不同意接受的答案,并说最好的方法是使用 apply 执行您想要的计算,从而每组一行;然后使用pd.merge 将此结果合并回原始数据帧,这将为您提供所需的类似索引数据帧。

标签: python pandas


【解决方案1】:

Circa Pandas 0.18 版,原来的答案(如下)似乎不再有效。

相反,如果您需要跨多个列进行 groupby 计算,请先进行多列计算,然后再进行 groupby:

df = pd.DataFrame({'a':[1,2,3,4,5,6],
                   'b':[1,2,3,4,5,6],
                   'c':['q', 'q', 'q', 'q', 'w', 'w'],  
                   'd':['z','z','z','o','o','o']})
df['e'] = df['a'] + df['b']
df['e'] = (df.groupby(['c', 'd'])['e'].transform('sum'))
print(df)

产量

   a  b  c  d   e
0  1  1  q  z  12
1  2  2  q  z  12
2  3  3  q  z  12
3  4  4  q  o   8
4  5  5  w  o  22
5  6  6  w  o  22

原答案:

错误信息:

TypeError: cannot concatenate a non-NDFrame object

建议为了连接,foo_function 应该返回一个 NDFrame(例如 Series 或 DataFrame)。如果你返回一个系列,那么:

In [99]: df.groupby(['c', 'd']).transform(lambda x: pd.Series(np.sum(x['a']+x['b'])))
Out[99]: 
    a   b
0  12  12
1  12  12
2  12  12
3   8   8
4  22  22
5  22  22

【讨论】:

  • 这是在 0.19.1 中引发的关键错误。我的理解是 transform 将一个系列(每列)传递给函数。但非常奇怪的是,当使用上述数据运行以下内容时,它也会传递一次数据帧。 df.groupby(['c', 'd']).transform(lambda x: print(type(x)))。这看起来像一个错误
  • @TedPetrou:感谢您指出这一点。作为一种性能增强,transform——就像apply——可能会尝试将该函数应用于(子)数据帧。在某些情况下(例如向量化函数和大量列),这可能比对每个组和列应用一次函数更快。
  • @TedPetrou:关于KeyError——现在我回顾我原来的答案,我认为我建议的解决方案不是一个好的解决方案。 transform 为每个组调用一次该函数。如果您在较大的输入上调用矢量化函数的次数较少,您将获得更好的性能。因此,在调用transform 之前 计算整个列上的df['a']+df['b'] 更有意义。我已经修改了上面的帖子以说明我的意思。
  • 不确定您的 0.18 修复是否有效。如果我想做的功能不能在 group by 之外完成怎么办?说出我想要的每个组df['a'] + df['b'].shift()
  • @jf328:在进行 groupby 操作之前对整个列进行预计算对性能有好处如果可能。你是对的,这并不总是可能的。在这些情况下,您需要使用更多的flexible apply
【解决方案2】:

按照我阅读问题的方式,您希望能够对两列中的单个值进行任意操作。您只需要确保返回与传入的数据框大小相同的数据框。我认为最好的方法是创建一个新列,如下所示:

df = pd.DataFrame({'a':[1,2,3,4,5,6],
                   'b':[1,2,3,4,5,6],
                   'c':['q', 'q', 'q', 'q', 'w', 'w'],  
                   'd':['z','z','z','o','o','o']})
df['e']=0

def f(x):
    y=(x['a']+x['b'])/sum(x['b'])
    return pd.DataFrame({'e':y,'a':x['a'],'b':x['b']})

df.groupby(['c','d']).transform(f)

    a   b   e
0   1   1   0.333333
1   2   2   0.666667
2   3   3   1.000000
3   4   4   2.000000
4   5   5   0.909091
5   6   6   1.090909

如果你有一个非常复杂的数据框,你可以选择你的列(例如df.groupby(['c'])['a','b','e'].transform(f)

这对我来说确实看起来很不雅,但在大型数据集上它仍然比 apply 快​​得多。

另一种方法是使用set_index 捕获您需要的所有列,然后仅将一列传递给transform

【讨论】:

  • 对于它的价值,这不再有效。 transform 似乎没有发送整个df,所以你会得到一个KeyErrorx['a']。如果您有其他建议,我很想知道。否则,我不得不使用apply,它对整个df 起作用。 f(x) 变为 x['e'] = (x['a']+x['b'])/sum(x['b']),然后简单地变为 return x
  • df['e'] = (df['a'] + df['b']) / df.groupby(['c','d'])['b'].transform('sum')
猜你喜欢
  • 2021-01-30
  • 1970-01-01
  • 1970-01-01
  • 2021-02-26
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 2021-09-29
  • 2021-11-14
相关资源
最近更新 更多