关于对groupby分组对象使用apply函数和transform函数的区别

import numpy as np
import pandas as pd
df_1=pd.DataFrame({'A':np.random.randint(100,10000,100),'B':np.random.rand(100)})
df_1['C']=pd.cut(df_1['A'],bins=5,labels=['S','M','L','XL','XXL'])
print(df_1.groupby('C').apply(lambda x:x.rank(ascending=True)))
print(df_1.groupby('C').transform(lambda x:x.rank(ascending=True)))

返回结果如下:

       A     B     C
0   16.0   7.0  10.5
1    5.0  14.0  10.5
2   18.0  11.0  11.5
3   19.0   7.0  10.5
4   11.0   1.0  12.0
..   ...   ...   ...
95  14.0   1.0   8.0
96   8.0  15.0  12.0
97   4.0   8.0  12.0
98  21.0   5.0  11.5
99  12.0   1.0  10.5

[100 rows x 3 columns]
       A     B
0   16.0   7.0
1    5.0  14.0
2   18.0  11.0
3   19.0   7.0
4   11.0   1.0
..   ...   ...
95  14.0   1.0
96   8.0  15.0
97   4.0   8.0
98  21.0   5.0
99  12.0   1.0

[100 rows x 2 columns]

问题一:这里使用apply函数和transform函数返回的结果不同之处在于apply函数会有C列的排名结果,但是transform函数的结果中没有,原因是什么呢?

问题二:apply函数只在传入的是一个内建聚合函数时返回的是每个分组的聚合结果,也就是一个分组下只产生一条记录,但是如果使用自定义函数,比如这里对分组对象进行排名,返回的是对每个分组中各条记录的排名,而不是整个分组在数组中的排名(一个标量值)对嘛?这个能稍微扩展一下帮我解释一下原因嘛?

cut,apply和transform的原理我都懂,但是你的代码我是完全看不懂。
groupby就根本不能和rank这种排序的搭配使用,会重新拆散grouped。类似于apply(lambda x:x),无意义。

transform 函数:
1.只允许在同一时间在一个Series上进行一次转换,如果定义列‘a’ 减去列‘b’, 则会出现异常;
2.必须返回与 group相同的单个维度的序列(行)
3. 返回单个标量对象也可以使用,如 . transform(sum)

apply函数:
1. 不同于transform只允许在Series上进行一次转换, apply对整个DataFrame 作用
2.apply隐式地将group 上所有的列作为自定义函数

栗子:

#coding=gbk
import numpy as np
import pandas as pd
data  = pd.DataFrame({'state':['Florida','Florida','Texas','Texas'],
                      'a':[4,5,1,3],
                      'b':[6,10,3,11]
                      })
print(data)
#    a   b    state
# 0  4   6  Florida
# 1  5  10  Florida
# 2  1   3    Texas
# 3  3  11    Texas
def sub_two(X):
    return X['a'] - X['b']
data1 = data.groupby(data['state']).apply(sub_two) # 此处使用transform 则会出现错误
print(data1)
# state     
# Florida  0   -2
#          1   -5
# Texas    2   -2
#          3   -8
# dtype: int64

返回单个标量可以使用transform:

:我们可以看到使用transform 和apply 的输出结果形式是不一样的,transform返回与数据同样长度的行,而apply则进行了聚合

此时,使用apply说明的信息更明确



def group_sum(x):
    return x.sum()
data3 = data.groupby(data['state']).transform(group_sum)    #返回与数据一样的 行
print(data3)
#    a   b
# 0  9  16
# 1  9  16
# 2  4  14
# 3  4  14
#但是使用apply时
data4 = data.groupby(data['state']).apply(group_sum)
print(data4)
#          a   b           state
# state                         
# Florida  9  16  FloridaFlorida
# Texas    4  14      TexasTexas

The other difference is that transform must return a single dimensional sequence the same size as the group. In this particular instance, each group has two rows, so transform must return a sequence of two rows. If it does not then an error is raised:

栗子2:

np.random.seed(666)
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three',
                         'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8), 'D' : np.random.randn(8)})
print(df)
#      A      B         C         D
# 0  foo    one  0.824188  0.640573
# 1  bar    one  0.479966 -0.786443
# 2  foo    two  1.173468  0.608870
# 3  bar  three  0.909048 -0.931012
# 4  foo    two -0.571721  0.978222
# 5  bar    two -0.109497 -0.736918
# 6  foo    one  0.019028 -0.298733
# 7  foo  three -0.943761 -0.460587
def zscore(x):
    return (x - x.mean())/ x.var()  
print(df.groupby('A').transform(zscore))  #自动识别CD列
print(df.groupby('A')['C','D'].apply(zscore))   #此种形式则两种输出数据是一样的
# df.groupby('A').apply(zscore)  此种情况则会报错,apply对整个dataframe作用

df['sum_c'] = df.groupby('A')['C'].transform(sum)   #先对A列进行分组, 计算C列的和
df = df.sort_values('A')
print(df)
#      A      B         C         D     sum_c
# 1  bar    one  0.479966 -0.786443  1.279517
# 3  bar  three  0.909048 -0.931012  1.279517
# 5  bar    two -0.109497 -0.736918  1.279517
# 0  foo    one  0.824188  0.640573  0.501202
# 2  foo    two  1.173468  0.608870  0.501202
# 4  foo    two -0.571721  0.978222  0.501202
# 6  foo    one  0.019028 -0.298733  0.501202
# 7  foo  three -0.943761 -0.460587  0.501202
print(df.groupby('A')['C'].apply(sum))
# A
# bar    1.279517
# foo    0.501202
# Name: C, dtype: float64

The function passed to transform must return a number, a row, or the same shape as the argument. if it's a number then the number will be set to all the elements in the group, if it's a row, it will be broadcasted to all the rows in the group.

函数传递给transform必须返回一个数字,一行,或者与参数相同的形状。 如果是一个数字,那么数字将被设置为组中的所有元素,如果是一行,它将会被广播到组中的所有行。

groupby里有两个比较重要的参数,as_index、group_keys:都是用于设置apply()应用于group之后所得DataFrame的索引,具体可以看看这篇博客https://blog.csdn.net/Dr_maker/article/details/112987020?spm=1001.2014.3001.5502。transform的话没用过,不咋了解。