Pandas GroupBy 结果解读:数据分组后的处理方法 – wiki基地

Pandas GroupBy 结果解读:数据分组后的处理方法

Pandas 的 groupby() 方法是数据分析中最为强大和灵活的工具之一。它允许我们将数据框(DataFrame)按一个或多个列的值进行分组,并对每个组执行各种操作,从而提取有意义的见解。 然而,仅仅掌握 groupby() 的基本语法是不够的,理解 groupby() 返回的结果类型,以及分组后如何有效地处理这些数据,才是真正发挥其威力的关键。 本文将深入探讨 groupby() 的结果解读,并详细介绍各种分组后的处理方法,帮助你更有效地利用 Pandas 进行数据分析。

1. GroupBy 对象:理解分组后的数据结构

groupby() 方法返回的不是一个标准的 DataFrame,而是一个 DataFrameGroupBy 对象(如果分组对象是 DataFrame)或 SeriesGroupBy 对象(如果分组对象是 Series)。 这个对象代表的是一个分组的视图,而不是实际的数据结果。 它保存了分组的信息,包括分组的键(用来分组的列的值)和每个组对应的索引。

让我们用一个简单的例子来说明:

“`python
import pandas as pd

data = {‘Category’: [‘A’, ‘A’, ‘B’, ‘B’, ‘C’, ‘C’, ‘A’],
‘Value’: [10, 15, 20, 25, 30, 35, 12],
‘Date’: [‘2023-01-01’, ‘2023-01-02’, ‘2023-01-03’, ‘2023-01-04’, ‘2023-01-05’, ‘2023-01-06’, ‘2023-01-07’]}
df = pd.DataFrame(data)

grouped = df.groupby(‘Category’)

print(type(grouped)) # 输出:
“`

输出表明 grouped 变量是一个 DataFrameGroupBy 对象。 你无法直接打印这个对象来查看分组后的数据,因为它仅仅是分组信息的载体。 你需要使用各种方法来进一步处理这个对象,才能得到实际的数据结果。

2. 访问分组数据:通过迭代和 get_group()

虽然不能直接打印 GroupBy 对象,但我们可以通过以下方法来访问和查看每个组的数据:

  • 迭代 GroupBy 对象: GroupBy 对象是可迭代的,每次迭代返回一个元组,包含组名和对应的数据子集(DataFrame)。

    python
    for name, group in grouped:
    print(f"Group Name: {name}")
    print(group)
    print("-" * 20)

    这段代码会依次打印每个 Category 的名称以及对应的 DataFrame。

  • 使用 get_group() 方法: get_group() 方法允许你通过组名来直接获取对应的数据子集。

    python
    group_a = grouped.get_group('A')
    print(group_a)

    这段代码会打印 Category 为 ‘A’ 的所有行。

3. 聚合函数:对每个组进行统计计算

groupby() 最大的价值在于它能让你方便地对每个组应用聚合函数,从而计算各种统计指标。 Pandas 提供了多种内置的聚合函数,也可以自定义聚合函数。

  • 内置聚合函数: 常用的内置聚合函数包括 sum(), mean(), median(), min(), max(), count(), std(), var(), first(), last(), nunique() 等。

    “`python

    计算每个 Category 的 Value 的总和

    sum_by_category = grouped[‘Value’].sum()
    print(sum_by_category)

    计算每个 Category 的 Value 的平均值和标准差

    mean_std_by_category = grouped[‘Value’].agg([‘mean’, ‘std’])
    print(mean_std_by_category)
    “`

  • agg() 方法: agg() 方法是执行聚合操作的通用方法。 它可以接受单个聚合函数、函数列表或字典,从而实现更灵活的聚合操作。

    • 传递函数列表: 如上面的例子所示,传递函数列表可以同时计算多个聚合指标。

    • 传递字典: 传递字典可以对不同的列应用不同的聚合函数。 字典的键是列名,值是要应用的聚合函数(可以是单个函数或函数列表)。

      python
      aggregated_data = grouped.agg({'Value': ['sum', 'mean'], 'Date': 'first'})
      print(aggregated_data)

      这段代码会计算每个 CategoryValue 的总和和平均值,以及 Date 列的第一个值。

  • 自定义聚合函数: 你可以定义自己的聚合函数,并在 agg() 方法中使用。 自定义函数必须接受一个 Series 作为输入,并返回一个标量值。

    “`python
    def range_func(x):
    return x.max() – x.min()

    range_by_category = grouped[‘Value’].agg(range_func)
    print(range_by_category)
    “`

    这段代码定义了一个计算值域的函数 range_func,并将其应用于 groupby 对象。

4. transform() 方法:对每个组进行广播操作

transform() 方法与 agg() 方法类似,都可以对每个组应用函数。 但不同之处在于,transform() 方法会将函数的结果“广播”回原始 DataFrame,而不是返回一个聚合后的结果。 也就是说,transform() 返回的 Series 的索引与原始 DataFrame 的索引相同,每个值都是对应行的组经过函数计算后的结果。

这使得 transform() 方法非常适合于创建基于组的衍生变量,例如标准化、居中化等。

“`python

计算每个 Value 在其所属 Category 中的平均值

mean_value_by_category = grouped[‘Value’].transform(‘mean’)
print(mean_value_by_category)

计算每个 Value 在其所属 Category 中的 Z-score (标准化)

def zscore(x):
return (x – x.mean()) / x.std()

zscore_by_category = grouped[‘Value’].transform(zscore)
print(zscore_by_category)

df[‘Mean_Value’] = mean_value_by_category # 将计算结果添加到原始 DataFrame 中
df[‘Zscore’] = zscore_by_category
print(df)
“`

5. filter() 方法:根据组的属性过滤数据

filter() 方法允许你根据组的属性来过滤 DataFrame。 你需要定义一个函数,该函数接受一个 DataFrame(代表一个组)作为输入,并返回一个布尔值。 如果函数返回 True,则保留该组的所有行;否则,删除该组的所有行。

“`python

保留 Value 的总和大于 50 的 Category

def filter_func(x):
return x[‘Value’].sum() > 50

filtered_df = df.groupby(‘Category’).filter(filter_func)
print(filtered_df)
“`

这段代码会删除 Category 为 ‘A’ 的所有行,因为 ‘A’ 组的 Value 总和(10 + 15 + 12 = 37)小于 50。

6. apply() 方法:执行更复杂的分组操作

apply() 方法是最通用的分组操作方法。 它可以让你对每个组应用任意的函数,函数的输入是一个 DataFrame(代表一个组),输出可以是 DataFrame、Series 或标量值。

apply() 方法非常灵活,可以实现各种复杂的分组操作,例如:

  • 返回 DataFrame: 如果你的函数返回一个 DataFrame,则 apply() 会将所有组的结果连接起来,形成一个新的 DataFrame。

    “`python
    def add_rank(x):
    x[‘Rank’] = x[‘Value’].rank(ascending=False)
    return x

    ranked_df = df.groupby(‘Category’).apply(add_rank)
    print(ranked_df)
    “`

    这段代码定义了一个函数 add_rank,该函数为每个组的 Value 列添加一个排名(Rank)列。 apply() 方法会将每个组的排名结果连接起来,形成一个新的 DataFrame。

  • 返回 Series: 如果你的函数返回一个 Series,则 apply() 会将所有组的结果连接起来,形成一个新的 Series,其索引是分组键和原始 DataFrame 的索引的组合(MultiIndex)。

    “`python
    def find_top_value(x):
    return x.nlargest(1, ‘Value’)

    top_values = df.groupby(‘Category’).apply(find_top_value)
    print(top_values)
    “`

    这段代码定义了一个函数 find_top_value,该函数返回每个组中 Value 最大的行。 apply() 方法会将每个组的结果连接起来,形成一个新的 Series,其索引是 Category 和原始 DataFrame 的索引的组合。

  • 返回标量值: 如果你的函数返回一个标量值,则 apply() 会将所有组的结果连接起来,形成一个 Series,其索引是分组键。

    “`python
    def calculate_range(x):
    return x[‘Value’].max() – x[‘Value’].min()

    range_values = df.groupby(‘Category’).apply(calculate_range)
    print(range_values)
    “`

    这段代码定义了一个函数 calculate_range,该函数计算每个组的 Value 的值域。 apply() 方法会将每个组的结果连接起来,形成一个新的 Series,其索引是 Category

7. 多重索引 (MultiIndex):处理多层分组

groupby() 方法可以接受多个列名作为参数,从而实现多层分组。 多层分组会产生一个 MultiIndex DataFrame 或 Series,其索引由多个层级组成,每个层级对应一个分组列。

“`python
data = {‘Region’: [‘North’, ‘North’, ‘South’, ‘South’, ‘North’, ‘South’],
‘Category’: [‘A’, ‘B’, ‘A’, ‘B’, ‘A’, ‘A’],
‘Value’: [10, 15, 20, 25, 30, 35]}
df = pd.DataFrame(data)

grouped = df.groupby([‘Region’, ‘Category’])

计算每个 Region 和 Category 组合的 Value 的总和

sum_by_region_category = grouped[‘Value’].sum()
print(sum_by_region_category)

访问 MultiIndex DataFrame 的数据

print(sum_by_region_category.loc[(‘North’, ‘A’)]) # 访问 Region 为 ‘North’ 且 Category 为 ‘A’ 的 Value 总和
print(sum_by_region_category.loc[‘North’]) # 访问 Region 为 ‘North’ 的所有 Category 的 Value 总和
“`

对于 MultiIndex DataFrame 或 Series,你可以使用 loc[] 方法来访问特定层级的数据。 你也可以使用 unstack() 方法将 MultiIndex 转换为标准的 DataFrame。

“`python

将 MultiIndex Series 转换为 DataFrame

unstacked_data = sum_by_region_category.unstack()
print(unstacked_data)
“`

unstack() 方法默认将最内层的索引转换为列。 你可以使用 level 参数来指定要转换的索引层级。

8. 注意事项和最佳实践

  • 处理缺失值 (NaN): groupby() 默认会忽略包含缺失值的分组键。 如果你需要将缺失值视为一个单独的组,可以使用 dropna=False 参数。

    “`python
    data = {‘Category’: [‘A’, ‘A’, ‘B’, None, ‘C’, ‘C’],
    ‘Value’: [10, 15, 20, 25, 30, 35]}
    df = pd.DataFrame(data)

    grouped = df.groupby(‘Category’, dropna=False)
    print(grouped[‘Value’].sum())
    “`

  • 分组键的数据类型: 分组键可以是任何数据类型,包括数值、字符串、日期等。 但需要注意的是,如果分组键是浮点数,由于浮点数的精度问题,可能会导致分组不准确。

  • 性能优化: 对于大型数据集,groupby() 操作可能会比较耗时。 可以使用 observed=True 参数来提高性能,尤其是在分组键是类别型数据 (Categorical Data) 的情况下。

    python
    df['Category'] = df['Category'].astype('category')
    grouped = df.groupby('Category', observed=True) # 仅使用实际出现的类别进行分组

  • 选择合适的聚合方法: 根据你的分析目标,选择合适的聚合方法。 例如,如果你想计算数据的分散程度,可以使用标准差 (std()) 或方差 (var())。

9. 总结

Pandas 的 groupby() 方法是一个功能强大的数据分析工具,它允许我们将数据按一个或多个列的值进行分组,并对每个组执行各种操作。 理解 groupby() 返回的 GroupBy 对象,掌握各种分组后的处理方法,例如聚合、转换、过滤和应用,可以帮助你更有效地利用 Pandas 进行数据分析,提取有意义的见解。 掌握多重索引的用法,可以处理更复杂的多层分组情况。 此外,还需要注意处理缺失值、分组键的数据类型以及性能优化等方面的问题,以确保分析的准确性和效率。 通过熟练掌握 groupby() 方法,你将能够更深入地理解数据,发现隐藏在数据背后的规律。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部