7.数据透视与合并

本文最后更新于 2025年7月31日 晚上

数据透视表 (pivot_table)

数据透视表是探索多维数据关系的强大工具,它可以将“长”格式的数据重塑为“宽”格式的、类似电子表格的视图。

1. 加载数据

  • 题目: 读取当前目录下 "某超市销售数据.csv" 并设置千分位符号为 ,
  • 答案:
    1
    2
    3
    4
    import pandas as pd

    # thousands=',' 用于在读取时自动处理带千分位逗号的数字字符串
    df = pd.read_csv("某超市销售数据.csv", thousands=',')
  • 解题过程:
    • 在数据分析中,经常会遇到用逗号作为千分位分隔符的数字(如 “1,234.56”)。如果直接读取,Pandas 会将其识别为字符串 object 类型,无法进行数值计算。
    • pd.read_csv()thousands 参数就是为了解决这个问题。通过设置 thousands=',',Pandas 在读取时会自动移除逗号,并将该列正确地解析为数值类型(如 int64float64)。

2. 数据透视|默认

  • 题目: 制作各省「平均销售额」的数据透视表。
  • 答案:
    1
    2
    3
    # 使用 pivot_table
    pivot_table_mean = pd.pivot_table(df, values = '销售额',index = '省/自治区')
    print(pivot_table_mean)
  • 解题过程:
    • pd.pivot_table() 是创建数据透视表的核心函数。
    • df: 第一个参数是待操作的 DataFrame。
    • index='省/自治区': index 参数指定了新表的行索引,我们想按省份来观察数据,所以将其设置为 '省/自治区'
    • 默认行为: 当我们不指定 values(要聚合的数值)和 aggfunc(聚合函数)时,pivot_table 默认会:
      1. 选取所有数值类型的列作为 values
      2. 使用均值 ('mean') 作为默认的 aggfunc
    • 因此,这个简单的调用就能得到各省所有数值列的平均值,其中就包含了平均销售额。

3. 数据透视|指定方法

  • 题目: 制作各省「销售总额」的数据透视表。
  • 答案:
    1
    2
    pivot_table_sum = pd.pivot_table(df, index='省/自治区', values='销售额', aggfunc='sum')
    print(pivot_table_sum)
  • 解题过程:
    • 这次我们需求更明确,需要用到另外两个核心参数:
    • values='销售额': 指定我们只关心“销售额”这一列的聚合结果。
    • aggfunc='sum': 指定聚合函数为求和 ('sum'),而不是默认的求均值。

4. 数据透视|多方法

  • 题目: 制作各省「销售总额」与「平均销售额」的数据透视表。
  • 答案:
    1
    2
    pivot_table_multi_func = pd.pivot_table(df, index='省/自治区', values='销售额', aggfunc=['sum', 'mean'])
    print(pivot_table_multi_func)
  • 解题过程:
    • aggfunc 参数可以接收一个函数列表。当你想对同一个 values 列应用多种聚合方法时,只需将这些方法的名称字符串放到一个列表中传给它即可。
    • Pandas 会自动为每种聚合方法生成一列。

5. 数据透视|多指标

  • 题目: 制作各省市「销售总额」与「利润总额」的数据透视表。
  • 答案:
    1
    2
    pivot_table_multi_values = pd.pivot_table(df, index='省/自治区', values=['销售额', '利润'], aggfunc='sum')
    print(pivot_table_multi_values)
  • 解题过程:
    • values 参数同样可以接收一个列名列表。当你想同时对多个指标进行聚合时,将它们的列名放到一个列表中传给 values
    • aggfunc='sum' 会分别应用到 values 列表中的每一列。

6. 数据透视|多索引

  • 题目: 制作「各省市」与「不同类别」产品「销售总额」的数据透视表。
  • 答案:
    1
    2
    pivot_table_multi_index = pd.pivot_table(df, index=['省/自治区', '类别'], values='销售额', aggfunc='sum')
    print(pivot_table_multi_index)
  • 解题过程:
    • index 参数也可以接收一个列名列表。这会创建一个**多级行索引 (MultiIndex)**。
    • 结果表会先按 '省/自治区' 分组,再在每个省内按 '类别' 分组,清晰地展示了更细粒度的交叉数据。

7. 数据透视|多层

  • 题目: 制作各省市「不同类别」产品的「销售总额」透视表。
  • 答案:
    1
    2
    pivot_table_multi_level = pd.pivot_table(df, index='省/自治区', columns='类别', values='销售额', aggfunc='sum')
    print(pivot_table_multi_level)
  • 解题过程:
    • columns='类别': 这是透视表的另一个核心参数。columns 参数可以将指定列中的唯一值“提升”为新表的列标签
    • 这个操作将原本在“类别”列中的“办公用品”、“家具”、“技术”变成了表的列,使得数据更加扁平化,一目了然。这就是“透视”的核心所在。

8. 数据透视|综合

  • 题目: 制作「各省市」、「不同类别」产品「销售量与销售额」的「均值与总和」的数据透视表,并在最后追加一行『合计』。
  • 答案:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    comprehensive_pivot = pd.pivot_table(
    df,
    index=['省/自治区', '类别'], # 多级行索引
    values=['数量', '销售额'], # 多个聚合值
    aggfunc=['mean', 'sum'], # 多个聚合函数
    margins=True, # 添加合计行/列
    margins_name='合计' # 自定义合计名称
    )
    print(comprehensive_pivot)
  • 解题过程:
    • 这是一个展示 pivot_table 强大功能的综合案例,它组合了多个参数:
    • index, values, aggfunc 都使用了列表,创建了一个多维交叉表。
    • margins=True: 这个参数会自动在结果表的最后添加一行和一列,计算所有数据的总计(All)。
    • margins_name='合计': 用于自定义这个总计行/列的名称。

9. 数据透视|筛选

  • 题目: 在上一题的基础上,查询 「类别」 等于 「办公用品」 的详情。
  • 答案:
    1
    2
    3
    4
    5
    # 先生成上一题的透视表
    comprehensive_pivot = pd.pivot_table(df, index=['省/自治区', '类别'], values=['数量', '销售额'], aggfunc=['mean', 'sum'], margins=True, margins_name='合计')
    # 使用 query 方法筛选
    office_supplies_detail = comprehensive_pivot.query("类别 == '办公用品'")
    print(office_supplies_detail)
  • 解题过程:
    • pivot_table 的结果是一个标准的 DataFrame,因此所有 DataFrame 的筛选方法都适用。
    • query(): 由于透视表的结果是多级索引,使用 query() 方法进行筛选非常方便。你可以直接在查询字符串中指定索引层级的名称(如 '类别')和条件。

10. 数据透视|逆透视

  • 题目: 将第 5 题的透视表进行逆透视,其中不需要转换的列为『数量』列。
  • 答案:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 先生成第五题的表 (多指标)
    pivot_multi_values = pd.pivot_table(df, index='省/自治区', values=['销售额', '利润','数量'], aggfunc='sum').reset_index()

    # 进行逆透视 (melt)
    melted_table = pd.melt(
    pivot_multi_values,
    id_vars=['省/自治区', '数量'], # 保持不变的标识列
    value_vars=['销售额', '利润'], # 需要被“融化”到行的列
    var_name='指标类型', # 新的变量列名
    value_name='金额' # 新的值列名
    )
    print(melted_table.head())
  • 解题过程:
    • pd.melt(): meltpivot 的逆操作,它将“宽”格式的数据转换回“长”格式,这在某些数据处理或可视化场景中非常有用。
    • id_vars=['省/自治区', '数量']: 标识变量,这些列在转换过程中保持不变,会为每个“融化”的行重复出现。
    • value_vars=['销售额', '利润']: 值变量,这些列的列名会被“融化”到一个新的列中(由 var_name 指定),它们对应的会被“融化”到另一个新列中(由 value_name 指定)。

数据合并 (concat)

concat 用于沿着一个轴(行或列)将多个 DataFrame 对象拼接在一起。

准备数据

首先,我们执行代码生成本节所需的 df1, df2, df3, df4

1
2
3
4
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']}, index=[0, 1, 2, 3])
df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'], 'B': ['B4', 'B5', 'B6', 'B7'], 'C': ['C4', 'C5', 'C6', 'C7'], 'D': ['D4', 'D5', 'D6', 'D7']}, index=[4, 5, 6, 7])
df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'], 'B': ['B8', 'B9', 'B10', 'B11'], 'C': ['C8', 'C9', 'C10', 'C11'], 'D': ['D8', 'D9', 'D10', 'D11']}, index=[8, 9, 10, 11])
df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'], 'D': ['D2', 'D3', 'D6', 'D7'], 'F': ['F2', 'F3', 'F6', 'F7']}, index=[2, 3, 6, 7])

11. concat|默认拼接

  • 题目: 拼接 df1 和 df2。
  • 答案:
    1
    2
    concatenated_df = pd.concat([df1, df2])
    print(concatenated_df)
  • 解题过程:
    • pd.concat() 的基本用法是接收一个 DataFrame 对象的列表 [df1, df2, ...]
    • 默认情况下,concat 沿着 axis=0(即行)进行拼接,将第二个 DataFrame 的行追加到第一个的末尾。

12. concat|拼接多个

  • 题目: 垂直拼接 df1、df2、df3
  • 答案:
    1
    2
    concatenated_multi = pd.concat([df1, df2, df3])
    print(concatenated_multi)
  • 解题过程:
    • concat 可以拼接任意多个 DataFrame,只需将它们都放到列表中即可。

13. concat|重置索引

  • 题目: 垂直拼接 df1 和 df4,并按顺序重新生成索引。
  • 答案:
    1
    2
    concat_ignore_index = pd.concat([df1, df4], ignore_index=True)
    print(concat_ignore_index)
  • 解题过程:
    • ignore_index=True: 当拼接的 DataFrame 索引不连续或有重叠时,这个参数非常有用。它会忽略原始索引,并为拼接后的新 DataFrame 生成一个从 0 开始的连续索引。
    • 注意,当 df1df4 拼接时,df1 中有而 df4 中没有的列(C列)和 df4 中有而 df1 中没有的列(F列),其缺失的部分会被 NaN 填充。

14. concat|横向拼接

  • 题目: 横向拼接 df1、df4
  • 答案:
    1
    2
    concat_axis1 = pd.concat([df1, df4], axis=1)
    print(concat_axis1)
  • 解题过程:
    • axis=1: 将 axis 参数设置为 1,可以实现按列(横向)拼接
    • Pandas 会根据行索引来对齐数据。对于那些只有一个 DataFrame 中存在的索引行,另一个 DataFrame 对应的列值会用 NaN 填充。

15. concat|横向拼接(取交集)

  • 题目: 在上一题的基础上,只取结果的交集。
  • 答案:
    1
    2
    concat_axis1_inner = pd.concat([df1, df4], axis=1, join='inner')
    print(concat_axis1_inner)
  • 解题过程:
    • join='inner': join 参数控制了在另一个轴上的对齐方式。
      • join='outer' (默认): 取并集,保留所有索引。
      • join='inner': 取交集。在横向拼接时,这意味着只保留那些在两个 DataFrame 中都存在的行索引。

16. concat|横向拼接(取指定)

  • 题目: 在 14 题基础上,只取包含 df1 索引的部分。
  • 答案:
    1
    2
    3
    # 在pandas的新版本中,reindex 更为推荐和清晰
    concat_reindex = pd.concat([df1, df4], axis=1).reindex(df1.index)
    print(concat_reindex)
  • 解题过程:
    • 旧版本的 join_axes 参数已被弃用。现代 Pandas 的推荐做法是:
    1. 先正常进行外连接(join='outer')横向拼接。
    2. 然后使用 .reindex(df1.index) 方法,根据 df1 的索引来重新索引拼接后的结果。这会精确地只保留 df1 中存在的那些行,效果与旧的 join_axes 一致。

17. concat|新增索引

  • 题目: 拼接 df1、df2、df3,同时新增一个索引(x、y、z)来区分不同的表数据来源。
  • 答案:
    1
    2
    concat_with_keys = pd.concat([df1, df2, df3], keys=['x', 'y', 'z'])
    print(concat_with_keys)
  • 解题过程:
    • keys=['x', 'y', 'z']: keys 参数非常有用,它可以在拼接的同时,在最外层添加一个新的索引层级
    • 列表 ['x', 'y', 'z'] 中的元素会分别对应 [df1, df2, df3],作为它们各自数据块的标识。这在需要追踪数据来源时尤其方便。

数据合并 (merge)

merge 实现了数据库风格的连接(join)操作,它根据一个或多个键(key)将不同 DataFrame 的行连接起来。

18. merge|按单键

  • 题目: 根据 key 连接 leftright
  • 答案:
    1
    2
    3
    4
    5
    left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'], 'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3']})
    right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'], 'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']})

    merged_on_key = pd.merge(left, right, on='key')
    print(merged_on_key)
  • 解题过程:
    • pd.merge(left, right, ...): merge 是一个顶级函数。
    • on='key': on 参数指定了用作连接依据的公共列名merge 会查找 leftright 表中 key 列值相同的行,并将它们合并。

19. merge|按多键

  • 题目: 根据 key1key2 连接 leftright
  • 答案:
    1
    2
    3
    4
    5
    left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'], 'key2': ['K0', 'K1', 'K0', 'K1'], 'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3']})
    right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'], 'key2': ['K0', 'K0', 'K0', 'K0'], 'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']})

    merged_on_multi_keys = pd.merge(left, right, on=['key1', 'key2'])
    print(merged_on_multi_keys)
  • 解题过程:
    • 当连接需要基于多个键时,on 参数可以接收一个键名列表。只有当列表中所有键的值都相同时,行才会被合并。

20. merge|左外连接

  • 题目: 保留左表全部键。
  • 答案:
    1
    2
    merged_left_join = pd.merge(left, right, how='left', on=['key1', 'key2'])
    print(merged_left_join)
  • 解题过程:
    • how='left': how 参数定义了连接的方式。
      • 'left': 左外连接。以 left 表的键为基准,保留所有 left 表的行。如果在 right 表中找不到匹配的键,则 right 表的列值会用 NaN 填充。

21. merge|右外连接

  • 题目: 保留右表全部键。
  • 答案:
    1
    2
    merged_right_join = pd.merge(left, right, how='right', on=['key1', 'key2'])
    print(merged_right_join)
  • 解题过程:
    • how='right': 右外连接。与左连接相反,以 right 表的键为基准,保留所有 right 表的行。

22. merge|全外连接

  • 题目: 保留全部键。
  • 答案:
    1
    2
    merged_outer_join = pd.merge(left, right, how='outer', on=['key1', 'key2'])
    print(merged_outer_join)
  • 解题过程:
    • how='outer': 全外连接。取 leftright 表键的并集。对于只在一张表中存在的键,另一张表的列值会用 NaN 填充。

23. merge|内连接

  • 题目: 保留交集。
  • 答案:
    1
    2
    merged_inner_join = pd.merge(left, right, how='inner', on=['key1', 'key2'])
    print(merged_inner_join)
  • 解题过程:
    • how='inner': 内连接(这是 merge 的默认行为)。只保留那些在 leftright 表中都存在的键。

24. merge|重复索引

  • 题目: 重新产生数据并按下图所示进行连接。
  • 答案:
    1
    2
    3
    4
    5
    6
    left = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'v': [1, 2, 3]})
    right = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'v': [4, 5, 6]})

    # suffixes 参数用于为重叠的列名添加后缀以区分
    merged_suffixes = pd.merge(left, right, on='k', suffixes=('_left', '_right'))
    print(merged_suffixes)
  • 解题过程:
    • 当两个待合并的 DataFrame 中有除了连接键之外的同名列时(如此处的 'v'),直接合并会报错。
    • suffixes=('_left', '_right'): 这个参数可以解决该问题。它会为来自左表的重名列添加 _left 后缀,为来自右表的重名列添加 _right 后缀。

数据合并 (join)

join 是一个 DataFrame 的实例方法(即 left.join(right)),它主要用于基于索引来合并数据,可以看作是 merge 的一种便捷方式。

25. join|左对齐

  • 题目: 合并 left 和 right,并按照 left 的索引进行对齐。
  • 答案:
    1
    2
    3
    4
    5
    left = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2'])
    right = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3'])

    joined_df = left.join(right)
    print(joined_df)
  • 解题过程:
    • left.join(right) 默认执行的是左连接 (how='left')。它会遍历 left 表的每一个索引,去 right 表中查找相同的索引,如果找到就合并,找不到则用 NaN 填充。

26. join|左对齐(外连接)

  • 题目: 按下图所示进行连接。
  • 答案:
    1
    2
    joined_outer = left.join(right, how='outer')
    print(joined_outer)
  • 解题过程:
    • join 方法同样有 how 参数。how='outer' 表示进行全外连接,取两个表索引的并集。

27. join|左对齐(内连接)

  • 题目: 按下图所示进行连接。
  • 答案:
    1
    2
    joined_inner = left.join(right, how='inner')
    print(joined_inner)
  • 解题过程:
    • how='inner' 表示进行内连接,只保留两个表共有的索引。

28. join|按索引

  • 题目: 重新产生数据并按下图所示进行连接(根据 key)。
  • 答案:
    1
    2
    3
    4
    5
    left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'key': ['K0', 'K1', 'K0', 'K1']})
    right = pd.DataFrame({'C': ['C0', 'C1'], 'D': ['D0', 'D1']}, index=['K0', 'K1'])

    joined_on_key = left.join(right, on='key')
    print(joined_on_key)
  • 解题过程:
    • join 方法除了能基于索引连接,也可以使用 on 参数指定 left 表中的某一列去和 right 表的索引进行匹配连接。

29. join|按索引(多个)

  • 题目: 重新产生数据并按下图所示进行连接(根据 key1key2)。
  • 答案:
    1
    2
    3
    4
    5
    6
    left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'key1': ['K0', 'K0', 'K1', 'K2'], 'key2': ['K0', 'K1', 'K0', 'K1']})
    index = pd.MultiIndex.from_tuples([('K0', 'K0'), ('K1', 'K0'), ('K2', 'K0'), ('K2', 'K1')])
    right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']}, index=index)

    joined_on_multi_keys = left.join(right, on=['key1', 'key2'])
    print(joined_on_multi_keys)
  • 解题过程:
    • on 参数同样可以接收一个列名列表。这表示 left 表需要用 key1key2 两列组合起来的值,去匹配 right 表的多级索引

7.数据透视与合并
https://blog.wyyy.dpdns.org/2025/7-数据透视与合并/
作者
lwy
发布于
2025年7月27日
许可协议