Pandas的transform()和apply()的区别

23

我不明白为什么在对同一数据框调用applytransform时,它们返回不同的数据类型。之前我对这两个函数的解释是:apply会将数据合并,而transformapply完全相同,但保留原始索引且不合并。请考虑以下内容。

df = pd.DataFrame({'id': [1,1,1,2,2,2,2,3,3,4],
                   'cat': [1,1,0,0,1,0,0,0,0,1]})

让我们识别那些在 cat 列中有非零条目的 id

>>> df.groupby('id')['cat'].apply(lambda x: (x == 1).any())
id
1     True
2     True
3    False
4     True
Name: cat, dtype: bool

好的。然而,如果我们想创建一个指示列,可以执行以下操作。

>>> df.groupby('id')['cat'].transform(lambda x: (x == 1).any())
0    1
1    1
2    1
3    1
4    1
5    1
6    1
7    0
8    0
9    1
Name: cat, dtype: int64

我不明白为什么现在的数据类型是int64,而不是any()函数返回的布尔类型。

当我改变原始数据帧以包含一些布尔值时(请注意,零仍保留),转换方法会在一个object列中返回布尔值。这对我来说是一个额外的谜团,因为所有的值都是布尔值,但显然为了匹配整数和布尔值混合类型的原始列的dtype,它被列为object

df = pd.DataFrame({'id': [1,1,1,2,2,2,2,3,3,4],
                   'cat': [True,True,0,0,True,0,0,0,0,True]})

>>> df.groupby('id')['cat'].transform(lambda x: (x == 1).any())
0     True
1     True
2     True
3     True
4     True
5     True
6     True
7    False
8    False
9     True
Name: cat, dtype: object

然而,当我使用全部布尔值时,转换函数会返回一个布尔列。

df = pd.DataFrame({'id': [1,1,1,2,2,2,2,3,3,4],
                   'cat': [True,True,False,False,True,False,False,False,False,True]})

>>> df.groupby('id')['cat'].transform(lambda x: (x == 1).any())
0     True
1     True
2     True
3     True
4     True
5     True
6     True
7    False
8    False
9     True
Name: cat, dtype: bool

通过我的敏锐的模式识别技能,似乎结果列的 dtype 与原始列的相同。我会感激任何关于为什么会发生这种情况或在 transform 函数中发生了什么的提示。干杯。


apply不会合并数据。apply很灵活,可以返回任何大小的系列或数据帧。transform始终保留每个组的行数。transform还将每个单独的列作为系列发送到调用函数。apply将整个数据帧发送到调用函数。 - Ted Petrou
2
related - piRSquared
啊哈!谢谢 @piRSquared。我觉得在阅读了那条评论并查看了源代码之后,我对为什么会发生这种情况有了更好的理解。 - 3novak
2个回答

12

看起来 SeriesGroupBy.transform() 尝试将结果数据类型转换为与原始列相同的数据类型,但是 DataFrameGroupBy.transform() 似乎不这样做:

In [139]: df.groupby('id')['cat'].transform(lambda x: (x == 1).any())
Out[139]:
0    1
1    1
2    1
3    1
4    1
5    1
6    1
7    0
8    0
9    1
Name: cat, dtype: int64

#                         v       v
In [140]: df.groupby('id')[['cat']].transform(lambda x: (x == 1).any())
Out[140]:
     cat
0   True
1   True
2   True
3   True
4   True
5   True
6   True
7  False
8  False
9   True

In [141]: df.dtypes
Out[141]:
cat    int64
id     int64
dtype: object

6

我再举一个具体的例子来说明求和,因为我认为它更清晰明了:

df = (
    pd.DataFrame(pd.np.random.rand(10, 3), columns=['a', 'b', 'c'])
        .assign(a=lambda df: df.a > 0.5)
)

Out[70]: 
       a         b         c
0  False  0.126448  0.487302
1  False  0.615451  0.735246
2  False  0.314604  0.585689
3  False  0.442784  0.626908
4  False  0.706729  0.508398
5  False  0.847688  0.300392
6  False  0.596089  0.414652
7  False  0.039695  0.965996
8   True  0.489024  0.161974
9  False  0.928978  0.332414

df.groupby('a').apply(sum)  # drop rows

         a         b         c
a                             
False  0.0  4.618465  4.956997
True   1.0  0.489024  0.161974


df.groupby('a').transform(sum)  # keep dims

          b         c
0  4.618465  4.956997
1  4.618465  4.956997
2  4.618465  4.956997
3  4.618465  4.956997
4  4.618465  4.956997
5  4.618465  4.956997
6  4.618465  4.956997
7  4.618465  4.956997
8  0.489024  0.161974
9  4.618465  4.956997

然而,当应用于 pd.DataFrame 而不是 pd.GroupBy 对象时,我没有看到任何区别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接