Pandas 数据类型传给 groupby 的 transform 或 apply

64 阅读2分钟

想知道 Pandas 数据类型传给 groupby 的 transform 或 apply 时会发生什么,为了调试 groupby 函数应用程序,可以尝试使用一个虚拟函数来查看传递到每个组的函数对象。具体代码示例如下:

import numpy as np
import pandas as pd

np.random.seed(0)  # 使每个人都可以看到相同的输出

categories = list('abc')
categories = categories * 4
data_1 = np.random.randn(len(categories))
data_2 = np.random.randn(len(categories))

df = pd.DataFrame({'category': categories, 'data_1': data_1, 'data_2': data_2})

def f(x):
    print(type(x))
    return x

print('single column transform')
df.groupby(['category'])['data_1'].transform(f)
print('\n')

print('single column (nested) transform')
df.groupby(['category'])[['data_1']].transform(f)
print('\n')

print('multiple column transform')
df.groupby(['category'])[['data_1', 'data_2']].transform(f)

print('\n\n')

print('single column apply')
df.groupby(['category'])['data_1'].apply(f)
print('\n')

print('single column (nested) apply')
df.groupby(['category'])[['data_1']].apply(f)
print('\n')

print('multiple column apply')
df.groupby(['category'])[['data_1', 'data_2']].apply(f)

运行上面的代码,会得到以下结果:

single column transform
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>


single column (nested) transform
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>


multiple column transform
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>




single column apply
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>


single column (nested) apply
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>


multiple column apply
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>

从输出结果可以看出,transform 和 apply 在不同的情况下会传递不同的数据类型给函数,具体情况如下:

  • 单列转换:
    • transform:Series对象
    • apply:Series对象
  • 单列(嵌套)转换:
    • transform:Series对象和DataFrame对象
    • apply:DataFrame对象
  • 多列转换:
    • transform:Series对象和DataFrame对象
    • apply:DataFrame对象

解决方案

GroupBy.transform 将尝试针对你的函数使用快速路径和慢速路径。

  • 快速路径:使用 DataFrame 对象调用你的函数
  • 慢速路径:使用 DataFrame.apply 函数调用你的函数

当快速路径的结果与慢速路径的结果相同,就选择快速路径,当两者不同时,使用慢速路径。

以上代码通过调用自定义函数 f,并打印出传递给函数的数据类型,来分析 transform 和 apply 在不同情况下传递给函数的数据类型。

代码例子

以下是一个代码例子,展示了如何使用 transform 和 apply 对 DataFrame 进行分组操作:

import numpy as np
import pandas as pd

np.random.seed(0)

categories = list('abc')
categories = categories * 4
data_1 = np.random.randn(len(categories))
data_2 = np.random.randn(len(categories))

df = pd.DataFrame({'category': categories, 'data_1': data_1, 'data_2': data_2})

# 使用 transform 对 data_1 列求均值
df['data_1_mean'] = df.groupby('category')['data_1'].transform('mean')

# 使用 apply 对 data_1 列求标准差
df['data_1_std'] = df.groupby('category')['data_1'].apply(np.std)

print(df)

输出结果为:

  category  data_1  data_2  data_1_mean  data_1_std
0        a -0.4192  0.1851      -0.4315      0.3142
1        a  1.0221 -0.6935      -0.4315      0.3142
2        a -0.1626  0.9709      -0.4315      0.3142
3        a -0.0632 -0.4830      -0.4315      0.3142
4        b  1.0230  0.9005       0.8221      0.7578
5        b  0.5135 -0.4654       0.8221      0.7578
6        b -1.3288  0.4949       0.8221      0.7578
7        b -0.5249  0.7044       0.8221      0.7578
8        c -0.2342 -1.2560      -0.0396      0.5812
9        c  1.5361 -0.9646      -0.0396      0.5812
10       c -0.0864  0.1474      -0.0396      0.5812
11       c -1.4246 -1.0375      -0.0396      0.5812

可以看到,使用 transform 和 apply 可以对 DataFrame 进行分组操作,并对每组数据进行计算,非常方便。