Pandas groupby应用vs转换具有特定功能

jpp*_*jpp 11 python dataframe pandas pandas-groupby

我不明白groupby+ transform操作可接受哪些功能.通常情况下,我最终只是猜测,测试,还原直到某些工作正常,但我觉得应该有一种系统的方法来确定解决方案是否有效.

这是一个最小的例子.首先,让我们用groupby+ applyset:

df = pd.DataFrame({'a': [1,2,3,1,2,3,3], 'b':[1,2,3,1,2,3,3], 'type':[1,0,1,0,1,0,1]})

g = df.groupby(['a', 'b'])['type'].apply(set)

print(g)

a  b
1  1    {0, 1}
2  2    {0, 1}
3  3    {0, 1}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我希望set在原始数据帧的新列中进行组计算.所以我尝试使用transform:

df['g'] = df.groupby(['a', 'b'])['type'].transform(set)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
---> 23 df['g'] = df.groupby(['a', 'b'])['type'].transform(set)

TypeError: int() argument must be a string, a bytes-like object or a number, not 'set'
Run Code Online (Sandbox Code Playgroud)

这是我在Pandas v0.19.0中看到的错误.在v0.23.0中,我看到了TypeError: 'set' type is unordered.当然,我可以映射一个特定的索引来实现我的结果:

g = df.groupby(['a', 'b'])['type'].apply(set)
df['g'] = df.set_index(['a', 'b']).index.map(g.get)

print(df)

   a  b  type       g
0  1  1     1  {0, 1}
1  2  2     0  {0, 1}
2  3  3     1  {0, 1}
3  1  1     0  {0, 1}
4  2  2     1  {0, 1}
5  3  3     0  {0, 1}
6  3  3     1  {0, 1}
Run Code Online (Sandbox Code Playgroud)

但我认为好处transform是避免这种明确的映射.我哪里做错了?

raf*_*elc 13

我相信,首先,使用这些功能有一定的直觉空间,因为它们非常有意义.

在您的第一个结果中,您实际上并没有尝试转换您的值,而是聚合它们(这将按照您的预期方式工作).

但是进入代码,transform文档在说这些内容时非常具有启发性

返回与组块大小相同或可广播到组块大小的结果.

当你这样做

df.groupby(['a', 'b'])['type'].transform(some_func)
Run Code Online (Sandbox Code Playgroud)

实际上,您正在使用函数将每个组中的每个pd.Series对象转换为新对象some_func.但事实是,这个新对象应该具有与组相同的大小,或者可以广播到块的大小.

因此,如果使用tuple或转换系列list,则基本上将转换对象

0    1
1    2
2    3
dtype: int64
Run Code Online (Sandbox Code Playgroud)

[1,2,3]
Run Code Online (Sandbox Code Playgroud)

但请注意,这些值现在已分配回各自的索引,这就是您在transform操作中看不到差异的原因.具有.iloc[0]来自pd.Serieswill 的值的行现在具有[1,2,3][0]变换列表中的值(同样适用于元组)等.请注意,这里的排序大小很重要,因为否则你可能搞乱你的组并且转换不起作用(这正是为什么set在这种情况下不能使用的正确功能).


引用文本的第二部分说"可播放到组块的大小".

这意味着您还可以将您转换pd.Series为可在所有行中使用的对象.例如

df.groupby(['a', 'b'])['type'].transform(lambda k: 50)
Run Code Online (Sandbox Code Playgroud)

会工作.为什么?即使50不可迭代,也可以通过在初始位置的所有位置重复使用该值进行广播pd.Series.


你为什么apply要用套装?

因为该apply方法在结果中没有这种大小约束.它实际上有三种不同的结果类型,它可以推断您是要扩展,缩小还是广播结果.请注意,您无法减少转换*

默认情况下(result_type=None),最终返回类型是从应用函数的返回类型推断出来的.result_type:{'expand','reduce','broadcast',None},默认无这些只在axis=1(列)时起作用:

  1. 'expand':类似列表的结果将变为列.

  2. 'reduce':尽可能返回系列,而不是扩展类似列表的结果.这与'expand'相反.

  3. 'broadcast':结果将广播到DataFrame的原始形状,原始索引和列将被保留.

  • 谢谢.我忘了`transform`作为一种方法可以单独使用(没有`groupby`)并且有特定的要求.话虽如此,在我经验丰富的情况下,它主要用于*with groupby,这就是为什么我错误地认为任何与`groupby` +`apply`一起使用的东西都适用于`transform`. (3认同)