在数据分析和处理的过程中,我们经常面临这样一个具体问题:如何统计 DataFrame 中两列甚至多列值组合出现的频率?比如,你可能想知道在销售数据中,每一种“产品”在每一个“州”被购买了多少次。这种基于多列组合的计数操作是数据清洗和特征工程中非常基础且关键的一步。
在这篇文章中,我们将深入探讨如何利用 Pandas 的 INLINECODE36c1cfdc 机制,配合不同的方法(如 INLINECODEbe03f11b、INLINECODE6ad8ec1d、INLINECODE6e9f629c 等)来精准统计每种组合的出现次数。我们将从最基础的概念入手,通过实际案例演示,逐步深入到数据重塑和性能优化,帮助你全面掌握这一技能。
理解 GroupBy 的核心逻辑
在开始写代码之前,让我们先统一一下对“分组”这个操作的理解。Pandas 中的 groupby 操作通常被描述为“分割-应用-合并”的过程。
- 分割:根据某些条件(比如列的值)将数据分成若干组。
- 应用:对每个组独立地应用函数(如求和、计数、平均值)。
- 合并:将结果合并到一个输出结构中。
当我们谈论“统计组合的出现次数”时,本质上我们就是将多列(例如 INLINECODE2ccdb244 和 INLINECODE22586b82)作为一个组合键,分割数据,然后计算落入每个“篮子”里的行数。
准备工作:构建示例 DataFrame
为了演示的连贯性,让我们首先构建一个包含销售记录的 DataFrame。在这个数据集中,我们有产品、销售发生的州以及具体的销售额。
import pandas as pd
import numpy as np
# 初始化数据字典
data = {
‘Products‘: [‘Box‘, ‘Color‘, ‘Pencil‘, ‘Eraser‘, ‘Color‘, ‘Pencil‘, ‘Eraser‘, ‘Color‘,
‘Color‘, ‘Eraser‘, ‘Eraser‘, ‘Pencil‘],
‘States‘: [‘Jammu‘, ‘Kolkata‘, ‘Bihar‘, ‘Gujarat‘, ‘Kolkata‘, ‘Bihar‘, ‘Jammu‘, ‘Bihar‘,
‘Gujarat‘, ‘Jammu‘, ‘Kolkata‘, ‘Bihar‘],
‘Sale‘: [14, 24, 31, 12, 13, 7, 9, 31, 18, 16, 18, 14]
}
# 创建 DataFrame
df = pd.DataFrame(data, columns=[‘Products‘, ‘States‘, ‘Sale‘])
# 查看前几行数据
print("原始 DataFrame:")
print(df.head())
输出预览:
Products States Sale
0 Box Jammu 14
1 Color Kolkata 24
2 Pencil Bihar 31
3 Eraser Gujarat 12
4 Color Kolkata 13
掌握了数据结构后,让我们看看具体如何操作。
—
方法 1:使用 df.size() —— 最直接的计数方式
这是最推荐用于单纯统计“出现次数”的方法。size() 方法会返回一个 Series,其中包含每个组中的元素总数。无论该组数据是否包含空值(NaN),它都会被计入。
代码示例:
# 使用 groupby 和 size() 统计组合出现的次数
# 这将生成一个 MultiIndex (多级索引) 的 Series
count_series = df.groupby([‘States‘, ‘Products‘]).size()
print(count_series)
输出结果:
States Products
Bihar Color 1
Pencil 3
Gujarat Color 1
Eraser 1
Jammu Box 1
Eraser 2
Kolkata Color 3
Eraser 1
dtype: int64
深入解析:
- 为什么这里用
size()?
你可能会想,为什么不用 INLINECODEb29f1cc8?INLINECODE12f765cd 的优势在于它统计的是对象的总数(包括行数),并且它不需要你指定具体的列名。INLINECODE62a57ba2 后直接跟 INLINECODE02a98183,意图非常清晰:我就想知道有多少行符合这个组合。
- 结果格式:
注意输出是一个带有 INLINECODEe94b45b2(多级索引)的 Series。第一级索引是 INLINECODEa15fffcd,第二级是 INLINECODE2066a4fc。这种格式非常适合后续的查找操作,比如 INLINECODE7526dced。
—
方法 2:使用 df.count() —— 关注非空值的计数
虽然 INLINECODE370a3065 统计行数,但在实际业务中,我们有时只关心“有效”数据的数量。例如,如果你的组合中某一列是 INLINECODEd0fd7380(空值),INLINECODE698b8967 会把它算作“1行”,而 INLINECODE5a9c667b 会忽略它。
代码示例:
# 我们对 ‘Sale‘ 列进行 count() 操作
# 这意味着:统计每个组合中,Sale 列不为空的次数
valid_sales_count = df.groupby([‘States‘, ‘Products‘])[‘Sale‘].count()
print(valid_sales_count)
输出结果:
(在这个特定的示例数据集中,因为没有缺失值,所以结果和方法1是一样的,但含义不同)
States Products
Bihar Color 1
Pencil 3
...
dtype: int64
关键区别:
- 适用场景: 当你进行分组统计时,如果你的分组依据列或者目标列包含缺失值,INLINECODEe4f3d4b7 和 INLINECODE1c44d911 的结果就会出现分歧。
- 必须指定列: 使用 INLINECODEade426b7 时,你不能直接写 INLINECODE8f67abcd(除非你想要所有列的计数列表),通常我们会指定具体的列,如
[‘Sale‘].count(),这样更明确且节省内存。
—
方法 3:使用 reset_index() —— 将结果转换为常规 DataFrame
虽然前两种方法返回的带有多级索引的 Series 很适合计算,但它们并不直观,也不方便直接导出到 Excel 或展示给非技术人员阅读。reset_index() 是解决这一问题的利器,它可以将层级索引“拍平”,还原成普通的列。
代码示例:
# 将层级索引重置,使其变为普通的列
# 这里结合了 agg(‘count‘) 来明确我们要进行的聚合操作
df_counts = df.groupby([‘States‘, ‘Products‘])[‘Sale‘].agg(‘count‘).reset_index()
# 为了更清晰,我们可以重命名计数列
df_counts = df_counts.rename(columns={‘Sale‘: ‘Count‘})
print(df_counts)
输出结果:
States Products Count
0 Bihar Color 1
1 Bihar Pencil 3
2 Gujarat Color 1
3 Gujarat Eraser 1
4 Jammu Box 1
5 Jammu Eraser 2
6 Kolkata Color 3
7 Kolkata Eraser 1
深入解析:
- INLINECODE54052b01 的替代方案: 除了在最后调用 INLINECODE7b038822,你也可以在 INLINECODE7127f079 时直接使用参数 INLINECODE68456f50。效果是一样的,但
reset_index()的写法在 Pandas 链式操作中往往更灵活,因为它可以放在管道的任何位置。 - 聚合的灵活性: 使用
.agg(‘count‘)让代码更具语义化。如果未来你需要将“计数”改为“求和”,只需将 ‘count‘ 改为 ‘sum‘ 即可,结构不用大改。
—
方法 4:使用 pivot() —— 数据透视与矩阵化
当你需要将结果制作成报表,也就是希望列名本身也变成数据的一部分(例如:产品名作为列标题,州名作为行标题)时,pivot() 是最好的选择。这就是著名的“透视表”操作。
代码示例:
# 1. 先分组计数,并确保分组列不作为索引 (as_index=False)
# 2. 然后使用 pivot 将 Products 列“旋转”成列头
# 3. fillna(0) 将缺失的组合填充为 0
pivot_df = df.groupby([‘States‘, ‘Products‘], as_index=False).size().pivot(index=‘States‘, columns=‘Products‘, values=‘size‘).fillna(0)
# 这里的 size() 实际上生成了一个名为 ‘size‘ 的列,因为我们在 groupby 时没有指定特定列
print(pivot_df)
输出结果:
`INLINECODE93dbc9e8`INLINECODE0f7cefbedf.groupby(…).size()INLINECODE4144e1cadf.groupby(…)[col].count()INLINECODEc954d1d2.resetindex()INLINECODE5c7a186e.pivot()INLINECODEd284073afillna(0)INLINECODE60ee8adagroupby` 都是你手中最锋利的武器。希望这篇文章能帮助你更自信地处理数据!
> 相关阅读:
> * 深入理解 Pandas 索引与 MultiIndex
> * Pandas 数据重塑指南:Melt, Pivot, Stack
> * 提升 Pandas 性能:数据类型优化指南