Pandas 数据分组利器:groupby() 全面解析
数据分析的核心任务之一就是对数据进行分组和聚合。想象一下,你有一张包含全国所有商店销售记录的表格,如果想知道每个地区或每个品类的总销售额,你不会想一行一行地手动计算。你需要一个工具,能够根据特定的标准(比如地区或品类)将数据“分成”不同的组,然后对每个组内的销售额进行“求和”或其他计算。在 Pandas 中,完成这个任务的“瑞士军刀”就是 groupby()
方法。
groupby()
是 Pandas 中最强大、最灵活的功能之一,它是数据分析和处理的基石。掌握了 groupby()
,你就掌握了 Pandas 数据处理的半壁江山。本文将深入浅出地全面解析 groupby()
方法,从基本概念到高级用法,并通过丰富的代码示例,帮助你彻底理解并熟练运用这一强大的工具。
1. 理解 groupby() 的核心思想:Split-Apply-Combine(分-组-合)
groupby()
的操作可以抽象为“Split-Apply-Combine”(分-组-合)三个阶段:
- Split(分): 根据用户指定的键(一个或多个列名、索引、函数或数组),将原始 DataFrame 分割成多个独立的组。每个组包含原始 DataFrame 中满足分组条件的所有行。
- Apply(组): 对每个独立的组应用一个函数。这个函数可以是聚合(如求和、平均值、计数)、转换(如标准化、填充缺失值)或过滤(如选取满足特定条件的组)。
- Combine(合): 将每个组应用函数后得到的结果合并成一个新的 DataFrame 或 Series。合并的方式取决于 Apply 阶段执行的操作类型。
理解这个流程至关重要。groupby()
返回的实际上是一个 GroupBy
对象,它不是一个最终结果,而是一个“待处理”的数据结构。当你对这个 GroupBy
对象调用聚合、转换或过滤方法时,Split 和 Apply 阶段才会真正执行,并将结果 Combine 起来。
2. groupby()
的基本用法
groupby()
方法通常直接在 DataFrame 上调用,其最简单的形式是传递一个或多个列名作为分组键:
“`python
import pandas as pd
import numpy as np
创建一个示例 DataFrame
data = {
‘地区’: [‘华东’, ‘华北’, ‘华东’, ‘华南’, ‘华北’, ‘华东’, ‘华南’],
‘品类’: [‘电子’, ‘服饰’, ‘家居’, ‘电子’, ‘家居’, ‘服饰’, ‘电子’],
‘销售额’: [1000, 500, 800, 1200, 600, 700, 1500],
‘数量’: [10, 20, 5, 15, 10, 25, 18]
}
df = pd.DataFrame(data)
print(“原始 DataFrame:”)
print(df)
print(“-” * 30)
按 ‘地区’ 列进行分组
grouped_by_region = df.groupby(‘地区’)
此时 grouped_by_region 是一个 GroupBy 对象
print(“按 ‘地区’ 分组后的对象类型:”, type(grouped_by_region))
print(“Groupby 对象本身不显示数据,它是待处理的集合。”)
可以查看分组的信息
print(“分组数量:”, grouped_by_region.ngroups)
print(“分组的键:”, grouped_by_region.groups.keys())
print(“具体分组内容 (字典形式):”, grouped_by_region.groups) # 打印会比较长
print(“-” * 30)
对分组后的对象进行聚合操作 (Apply阶段的核心)
例如,计算每个地区的总销售额
region_sales_sum = grouped_by_region[‘销售额’].sum()
print(“每个地区的总销售额:”)
print(region_sales_sum)
print(“-” * 30)
计算每个品类的平均销售额
category_sales_mean = df.groupby(‘品类’)[‘销售额’].mean()
print(“每个品类的平均销售额:”)
print(category_sales_mean)
print(“-” * 30)
“`
解释:
df.groupby('地区')
:这一步执行了 Split 阶段,它根据 ‘地区’ 列的值将 DataFrame 分成了三个组:’华东’、’华北’、’华南’。grouped_by_region
是一个GroupBy
对象。grouped_by_region['销售额'].sum()
:这是 Apply 和 Combine 阶段。我们选择了每个组中的 ‘销售额’ 列,并对每个组的 ‘销售额’ 应用sum()
函数进行聚合。最后,将每个组的求和结果合并成一个新的 Series,其中索引是分组键(地区),值是对应的总销售额。
3. 应用操作详解:Aggregation, Transformation, Filtration
GroupBy
对象提供了多种方法来执行 Apply 阶段的操作,主要分为三类:
3.1 Aggregation(聚合)
聚合操作是对每个组计算一个汇总统计量,并返回一个结果,其行数通常少于原始 DataFrame 的行数(每组一行)。常用的聚合函数包括:
sum()
:总和mean()
:平均值count()
:非 NaN 值的数量size()
:组的大小(包括 NaN)min()
:最小值max()
:最大值std()
:标准差var()
:方差first()
:组的第一个值last()
:组的最后一个值nth(n)
:组的第 n 个值agg()
或aggregate()
:应用一个或多个自定义或内置聚合函数
单个聚合函数:
“`python
计算每个地区的总销售额和总数量
region_sales_quantity = df.groupby(‘地区’)[[‘销售额’, ‘数量’]].sum()
print(“每个地区的总销售额和总数量:”)
print(region_sales_quantity)
print(“-” * 30)
“`
多个聚合函数:
可以使用 agg()
或 aggregate()
方法同时应用多个聚合函数。传入一个函数名的列表或字典。
“`python
计算每个地区的销售额和数量的多种统计信息
region_stats = df.groupby(‘地区’).agg([‘sum’, ‘mean’, ‘count’, ‘size’])
print(“每个地区的销售额和数量的多种统计信息:”)
print(region_stats)
print(“-” * 30)
可以为不同的列应用不同的聚合函数
region_specific_stats = df.groupby(‘地区’).agg({
‘销售额’: [‘sum’, ‘mean’],
‘数量’: ‘sum’ # 或者 [‘sum’]
})
print(“不同列应用不同聚合函数:”)
print(region_specific_stats)
print(“-” * 30)
“`
命名聚合(Named Aggregation):
当应用多个聚合函数时,列名可能会变得复杂(多级索引)。可以使用命名聚合来为结果列指定更友好的名称。这需要使用 agg()
方法,并传入一个字典,字典的键是结果列的新名称,值是一个元组,元组的第一个元素是要聚合的列名,第二个元素是聚合函数(字符串或函数)。
“`python
使用命名聚合为结果列指定名称
named_region_stats = df.groupby(‘地区’).agg(
总销售额=(‘销售额’, ‘sum’),
平均销售额=(‘销售额’, ‘mean’),
总数量=(‘数量’, ‘sum’),
记录数=(‘品类’, ‘count’) # count 可以计算任意非NaN列
)
print(“使用命名聚合的结果:”)
print(named_region_stats)
print(“-” * 30)
“`
自定义聚合函数:
agg()
方法也可以接受一个自定义函数。这个函数应该接收一个 Series(即组中的某一列)并返回一个单一的值。
“`python
定义一个自定义函数,计算销售额的标准差再除以平均值 (变异系数)
def coefficient_of_variation(x):
if x.mean() == 0:
return 0
return x.std() / x.mean()
custom_agg_result = df.groupby(‘地区’).agg(
变异系数=(‘销售额’, coefficient_of_variation)
)
print(“使用自定义聚合函数的结果:”)
print(custom_agg_result)
print(“-” * 30)
“`
3.2 Transformation(转换)
转换操作会对每个组应用一个函数,并返回一个与原始组具有相同索引的 Series 或 DataFrame。这意味着转换操作不会改变数据的行数,而是对原始数据进行某种形式的修改或计算,结果的结构与原始数据在分组键上对齐。转换函数必须返回一个 Series 或 DataFrame,其索引与输入组的索引相同。
常见的转换操作包括:
- 组内标准化/归一化
- 组内填充缺失值
- 组内计算百分比
- 组内排序排名
“`python
创建一个包含 NaN 值的 DataFrame
data_nan = {
‘地区’: [‘华东’, ‘华北’, ‘华东’, ‘华南’, ‘华北’, ‘华东’, ‘华南’],
‘品类’: [‘电子’, ‘服饰’, ‘家居’, ‘电子’, ‘家居’, ‘服饰’, ‘电子’],
‘销售额’: [1000, 500, np.nan, 1200, 600, 700, np.nan],
‘数量’: [10, 20, 5, 15, 10, 25, 18]
}
df_nan = pd.DataFrame(data_nan)
print(“包含 NaN 的 DataFrame:”)
print(df_nan)
print(“-” * 30)
组内填充 NaN:按地区分组,使用每个地区的销售额平均值填充该地区的 NaN
df_filled = df_nan.groupby(‘地区’)[‘销售额’].transform(lambda x: x.fillna(x.mean()))
df_nan[‘填充后销售额’] = df_filled # 将结果添加到原 DataFrame
print(“按地区填充 NaN 后的 DataFrame (仅显示相关列):”)
print(df_nan[[‘地区’, ‘销售额’, ‘填充后销售额’]])
print(“-” * 30)
组内标准化:计算每个地区的销售额在其组内的 Z 分数 ((x – mean) / std)
注意:transform 传入的函数接收的是一个 Series (即组内的列)
df_standardized = df.groupby(‘地区’)[‘销售额’].transform(lambda x: (x – x.mean()) / x.std())
df[‘销售额Z分数’] = df_standardized
print(“按地区标准化销售额后的 DataFrame (仅显示相关列):”)
print(df[[‘地区’, ‘销售额’, ‘销售额Z分数’]])
print(“-” * 30)
计算每个地区的销售额占该地区总销售额的百分比
注意:这里需要先计算组的总和,然后用 transform 将总和广播到每个元素
df[‘销售额占地区总额比例’] = df.groupby(‘地区’)[‘销售额’].transform(lambda x: x / x.sum())
print(“销售额占地区总额比例:”)
print(df[[‘地区’, ‘销售额’, ‘销售额占地区总额比例’]])
print(“-” * 30)
“`
transform
的关键特性:
- 保持索引: 输出的 Series 或 DataFrame 的索引与原始 DataFrame 在分组后的索引是完全对应的。这使得你可以方便地将转换结果直接作为新列添加到原始 DataFrame 中。
- 函数要求: 传入
transform
的函数必须返回:- 一个标量值(会被广播到整个组的每个元素)。
- 一个与输入组具有相同索引的 Series 或 DataFrame。
3.3 Filtration(过滤)
过滤操作是基于组的属性(如组的大小、组内元素的均值等)来判断是否保留整个组。过滤函数应该返回一个布尔值(True
表示保留该组,False
表示丢弃该组)。filter()
方法接收一个函数作为参数。
“`python
创建一个示例 DataFrame,包含一些小分组
data_filter = {
‘类别’: [‘A’, ‘A’, ‘B’, ‘C’, ‘C’, ‘C’, ‘D’],
‘值’: [10, 20, 30, 40, 50, 60, 70]
}
df_filter = pd.DataFrame(data_filter)
print(“用于过滤的 DataFrame:”)
print(df_filter)
print(“-” * 30)
过滤掉大小小于 3 的组 (即只保留组内行数 >= 3 的组)
lambda x 是一个子 DataFrame (一个组)
filtered_df_size = df_filter.groupby(‘类别’).filter(lambda x: len(x) >= 3)
print(“过滤掉大小小于 3 的组:”)
print(filtered_df_size) # 只保留了 ‘C’ 组
print(“-” * 30)
过滤掉组内平均值小于 40 的组
filtered_df_mean = df_filter.groupby(‘类别’).filter(lambda x: x[‘值’].mean() >= 40)
print(“过滤掉组内平均值小于 40 的组:”)
print(filtered_df_mean) # 保留了 ‘C’ 和 ‘D’ 组 (‘A’平均15, ‘B’平均30)
print(“-” * 30)
“`
filter
的关键特性:
- 返回子集: 输出是一个 DataFrame,它是原始 DataFrame 的一个子集,包含所有被保留的组的所有行。
- 函数要求: 传入
filter
的函数必须接收一个子 DataFrame(一个组),并返回一个单一的布尔值。
4. 按多个列进行分组
groupby()
方法可以接受一个列表作为分组键,这意味着可以同时按多个列进行分组。这将创建更细粒度的组。
“`python
按 ‘地区’ 和 ‘品类’ 两个列进行分组
grouped_multi = df.groupby([‘地区’, ‘品类’])
计算每个地区-品类组合的总销售额
multi_level_sales = grouped_multi[‘销售额’].sum()
print(“按地区和品类分组的总销售额 (多级索引):”)
print(multi_level_sales)
print(“-” * 30)
结果是一个带有 MultiIndex (多级索引) 的 Series。
可以使用 unstack() 方法将内部索引转换为列
multi_level_sales_unstacked = multi_level_sales.unstack()
print(“按地区和品类分组的总销售额 (unstack 后):”)
print(multi_level_sales_unstacked)
print(“-” * 30)
也可以直接在 groupby 后的对象上进行其他聚合、转换、过滤操作
multi_agg = grouped_multi.agg({
‘销售额’: [‘sum’, ‘mean’],
‘数量’: ‘sum’
})
print(“按地区和品类分组的多维度聚合:”)
print(multi_agg)
print(“-” * 30)
“`
5. 按索引进行分组
除了列名,groupby()
还可以按索引进行分组。如果索引是 MultiIndex,可以按索引的级别进行分组。
“`python
创建一个带有 MultiIndex 的 DataFrame
index_multi = pd.MultiIndex.from_tuples([
(‘A’, ‘x’), (‘A’, ‘y’), (‘B’, ‘x’), (‘B’, ‘y’), (‘B’, ‘z’)
], names=[‘Level1’, ‘Level2’])
df_multi_index = pd.DataFrame({
‘Value’: [10, 20, 30, 40, 50]
}, index=index_multi)
print(“带有 MultiIndex 的 DataFrame:”)
print(df_multi_index)
print(“-” * 30)
按 Level1 索引级别进行分组
grouped_by_level1 = df_multi_index.groupby(level=’Level1′)
print(“按 Level1 索引级别分组的总和:”)
print(grouped_by_level1[‘Value’].sum())
print(“-” * 30)
按多个索引级别进行分组
grouped_by_levels = df_multi_index.groupby(level=[‘Level1’, ‘Level2’]) # 这相当于按完整的 MultiIndex 分组,结果通常是每行一组
“`
6. 按自定义规则进行分组
groupby()
可以接受函数、字典或 Series 作为分组键,这提供了极大的灵活性。
- 按函数分组: 函数会作用于 DataFrame 的索引。根据函数返回的值进行分组。
- 按字典或 Series 分组: 字典或 Series 提供了一种映射关系,将索引或列值映射到分组键。
“`python
按索引的第一个字母进行分组 (函数作用于索引)
df_index = pd.DataFrame({‘Value’: [10, 20, 30, 40]}, index=[‘Apple’, ‘Banana’, ‘Cherry’, ‘Date’])
print(“按索引分组前的 DataFrame:”)
print(df_index)
print(“-” * 30)
grouped_by_index_func = df_index.groupby(lambda x: x[0]) # x 是索引值
print(“按索引的第一个字母分组的总和:”)
print(grouped_by_index_func[‘Value’].sum())
print(“-” * 30)
按字典映射进行分组 (字典键是索引值,值是分组键)
mapping = {‘Apple’: ‘Group1’, ‘Banana’: ‘Group1’, ‘Cherry’: ‘Group2’, ‘Date’: ‘Group2’}
grouped_by_map = df_index.groupby(mapping)
print(“按字典映射分组的总和:”)
print(grouped_by_map[‘Value’].sum())
print(“-” * 30)
按 Series 进行分组 (Series 索引与 DataFrame 索引对齐,Series 值是分组键)
group_series = pd.Series([‘Odd’, ‘Even’, ‘Odd’, ‘Even’], index=[1, 2, 3, 4])
df_serial_group = pd.DataFrame({‘Value’: [100, 200, 300, 400]}, index=[1, 2, 3, 4])
print(“按 Series 分组前的 DataFrame:”)
print(df_serial_group)
print(“-” * 30)
grouped_by_series = df_serial_group.groupby(group_series)
print(“按 Series 分组的总和:”)
print(grouped_by_series[‘Value’].sum())
print(“-” * 30)
“`
7. 访问分组
虽然 GroupBy
对象主要用于链式调用 Apply 操作,但也可以直接访问单个组或遍历所有组。
“`python
获取某个特定的组 (返回一个 DataFrame)
hua_dong_group = grouped_by_region.get_group(‘华东’)
print(“获取 ‘华东’ 组的数据:”)
print(hua_dong_group)
print(“-” * 30)
遍历所有组
print(“遍历所有组:”)
for name, group in grouped_by_region:
print(f”— 分组名称: {name} —“)
print(group)
print(“-” * 30)
“`
遍历分组在某些需要对每个组执行复杂或非常规操作的场景下非常有用,但通常情况下,利用 agg()
、transform()
或 filter()
的内置功能会更高效。
8. groupby() 的可选参数
groupby()
方法有一些重要的可选参数可以控制分组的行为:
-
as_index=True/False
:默认情况下 (True
),分组键会成为结果 DataFrame 或 Series 的索引。如果设置为False
,分组键会作为结果 DataFrame 中的普通列,并且结果的索引是默认的整数索引。当按多个列分组时,设置为False
可以避免生成多级索引。“`python
as_index=True (默认)
region_sales_sum_indexed = df.groupby(‘地区’)[‘销售额’].sum()
print(“as_index=True 的结果 (索引是地区):”)
print(region_sales_sum_indexed)
print(“-” * 30)as_index=False
region_sales_sum_flat = df.groupby(‘地区’, as_index=False)[‘销售额’].sum()
print(“as_index=False 的结果 (地区是普通列):”)
print(region_sales_sum_flat)
print(“-” * 30)多列分组时 as_index=False 很有用
multi_level_sales_flat = df.groupby([‘地区’, ‘品类’], as_index=False)[‘销售额’].sum()
print(“多列分组 as_index=False:”)
print(multi_level_sales_flat)
print(“-” * 30)
``
sort=True/False
*:默认情况下 (
True),分组键会按照字母顺序或数值大小进行排序。如果设置为
False`,分组的顺序将取决于它们在原始 DataFrame 中出现的顺序。对于非常大的数据集,关闭排序可以提高性能。“`python
sort=True (默认) – 地区已按字母排序
print(“sort=True (默认):”)
print(df.groupby(‘地区’)[‘销售额’].sum())
print(“-” * 30)sort=False – 地区按原始顺序出现 (华东, 华北, 华南)
print(“sort=False:”)
print(df.groupby(‘地区’, sort=False)[‘销售额’].sum())
print(“-” * 30)
``
dropna=True/False
*:默认情况下 (
True),如果分组键中包含
NaN值,这些行将被从分组中排除。如果设置为
False,包含
NaN的行将单独形成一个组,其键为
NaN`。“`python
创建带有 NaN 分组键的 DataFrame
df_group_nan = pd.DataFrame({
‘Key’: [‘A’, ‘B’, np.nan, ‘A’, ‘B’, np.nan],
‘Value’: [10, 20, 30, 40, 50, 60]
})
print(“带有 NaN 分组键的 DataFrame:”)
print(df_group_nan)
print(“-” * 30)dropna=True (默认) – 忽略 NaN 分组键的行
print(“dropna=True (默认):”)
print(df_group_nan.groupby(‘Key’, dropna=True)[‘Value’].sum())
print(“-” * 30)dropna=False – NaN 形成单独的分组
print(“dropna=False:”)
print(df_group_nan.groupby(‘Key’, dropna=False)[‘Value’].sum())
print(“-” * 30)
“`
9. 实际应用场景示例
结合我们最初的销售数据示例,看看 groupby()
如何解决更复杂的实际问题:
场景一:找出每个地区销售额最高的品类
这需要两步:先按地区和品类分组计算总销售额,然后在每个地区的组内找到销售额最大的一行。
“`python
1. 按地区和品类分组计算总销售额
region_category_sales = df.groupby([‘地区’, ‘品类’])[‘销售额’].sum()
print(“按地区和品类分组的总销售额:”)
print(region_category_sales)
print(“-” * 30)
2. 在每个地区的组内,找到销售额最大的品类
需要按地区再次分组,然后应用一个函数来找到最大值对应的索引
Reset index first to make ‘地区’ a column again for the second groupby
region_category_sales = region_category_sales.reset_index()
print(“重置索引后的总销售额 DataFrame:”)
print(region_category_sales)
print(“-” * 30)
按地区分组,并在每个地区组内找到销售额最大的一行
top_category_per_region = region_category_sales.groupby(‘地区’).apply(lambda x: x.loc[x[‘销售额’].idxmax()])
print(“每个地区销售额最高的品类:”)
print(top_category_per_region)
print(“-” * 30)
另一种更直接的方式是使用 nth(0) 或 first()/last() 配合排序
先按地区和销售额排序 (销售额降序),然后按地区分组取第一个
df_sorted = df.sort_values(by=[‘地区’, ‘销售额’], ascending=[True, False])
top_category_per_region_v2 = df_sorted.groupby(‘地区’).first()
print(“另一种方式:排序后取第一个 (每个地区销售额最高的记录):”)
print(top_category_per_region_v2) # 注意这里是原始记录,不是汇总后的销售额
print(“-” * 30)
“`
场景二:为每笔销售记录添加该地区/品类的平均销售额
这正是 transform
的典型应用场景。
“`python
计算每个地区-品类组合的平均销售额,并添加到原始 DataFrame
df[‘地区_品类平均销售额’] = df.groupby([‘地区’, ‘品类’])[‘销售额’].transform(‘mean’)
print(“添加地区-品类平均销售额列:”)
print(df)
print(“-” * 30)
“`
场景三:只保留那些总销售额超过2000的地区的所有销售记录
这需要使用 filter
。
“`python
创建一个包含更多数据的 DataFrame
data_large = {
‘地区’: [‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘C’, ‘C’, ‘C’, ‘C’, ‘D’, ‘D’, ‘D’, ‘D’, ‘D’],
‘销售额’: [100, 200, 300, 50, 150, 400, 500, 600, 700, 50, 60, 70, 80, 90]
}
df_large = pd.DataFrame(data_large)
print(“用于过滤的更大 DataFrame:”)
print(df_large)
print(“-” * 30)
过滤:只保留总销售额超过 2000 的地区
lambda x 是一个地区的所有销售记录 (子 DataFrame)
x[‘销售额’].sum() 计算该地区的总销售额
filtered_regions = df_large.groupby(‘地区’).filter(lambda x: x[‘销售额’].sum() > 2000)
print(“总销售额超过 2000 的地区的所有记录:”)
print(filtered_regions) # 地区 A 总额 600, B 总额 200, C 总额 2200, D 总额 350。所以只保留 C。
print(“-” * 30)
“`
10. 性能考虑
对于非常大的数据集,groupby()
的性能是一个重要因素。以下是一些建议:
- 避免在
transform
或filter
中使用效率低下的函数: lambda 函数很方便,但如果内部操作复杂且没有向量化,可能会很慢。尽量使用 Pandas 内置或 NumPy 函数,它们通常是高度优化的。 - 考虑
as_index=False
和sort=False
: 如果结果的索引不是必需的,或者分组顺序不重要,将这两个参数设置为False
可以避免索引的创建/管理和排序的开销。 - 处理缺失值:
dropna=False
会创建额外的NaN
分组,如果NaN
很多,可能会影响性能。根据需求决定是否保留或提前处理缺失值。 - 内存使用:
groupby()
会在内存中创建中间分组对象。对于极大的数据集,可能需要考虑分块处理或使用 Dask 等库。
11. 与 SQL 的 GROUP BY 对比
对于熟悉 SQL 的用户,Pandas 的 groupby()
和 SQL 的 GROUP BY
概念非常相似,但功能更强大。
- SQL
GROUP BY
主要用于聚合: 你只能在SELECT
列表中使用聚合函数(SUM
,AVG
,COUNT
,MIN
,MAX
等)或分组键。 - Pandas
groupby()
提供更多操作: 除了聚合 (agg
),还可以进行转换 (transform
) 和过滤 (filter
),并且可以在apply
中应用任意自定义函数。
12. 总结
Pandas 的 groupby()
方法是进行数据分组、聚合、转换和过滤的核心工具。它基于强大的 Split-Apply-Combine 范式,提供了极高的灵活性和效率。
- 通过传递列名、索引、函数或 Series,可以灵活定义分组键。
agg()
用于对每个组进行聚合计算,支持单个、多个函数以及命名聚合。transform()
用于对每个组进行转换,结果与原始 DataFrame 的索引对齐,常用于组内计算并添加为新列。filter()
用于根据组的属性筛选保留哪些完整的组。as_index
和sort
参数可以控制结果的格式和处理过程。
熟练掌握 groupby()
是成为 Pandas 高手的必经之路。通过不断实践,你会发现它几乎可以解决所有涉及到按类别汇总、比较或转换数据的任务。希望本文的全面解析和丰富示例能帮助你充分理解并自信地运用这一强大的数据分析利器!