Pandas 数据合并

在数据分析中,数据往往分散在多个表或文件中。Pandas 提供了强大的数据合并功能——concat()merge()join()——让你能够像 SQL 一样灵活地拼接和连接数据。

📌 准备示例数据

我们将使用几个简单的 DataFrame 来演示合并操作:

import pandas as pd
import numpy as np

# 员工基本信息
df1 = pd.DataFrame({
    '员工ID': [1, 2, 3],
    '姓名': ['张三', '李四', '王五'],
    '部门': ['技术', '销售', '技术']
})

# 员工工资信息
df2 = pd.DataFrame({
    '员工ID': [1, 2, 4],
    '工资': [8000, 12000, 15000],
    '入职年份': [2020, 2018, 2019]
})

# 2023年新员工
df3 = pd.DataFrame({
    '员工ID': [5, 6],
    '姓名': ['赵六', '陈七'],
    '部门': ['管理', '销售']
})

print("df1 (基本信息):")
print(df1)
print("\ndf2 (工资信息):")
print(df2)
print("\ndf3 (新员工):")
print(df3)

🧩 纵向拼接:pd.concat()

concat() 是最简单的合并方式,可以沿着行(axis=0)或列(axis=1)将多个 DataFrame 拼接在一起。

纵向拼接 (axis=0)
# 将df1和df3的行拼接(增加行数)
result = pd.concat([df1, df3], ignore_index=True)
print(result)
横向拼接 (axis=1)
# 将df1和df2的列拼接(增加列数)
result = pd.concat([df1, df2], axis=1)
print(result)

⚙️ 重要参数

  • axis: 0 表示行拼接(默认),1 表示列拼接。
  • join: 'inner' 取交集,'outer' 取并集(默认)。
  • ignore_index: 是否忽略原索引,重新生成 0..n-1 的整数索引。
  • keys: 添加多级索引,用于区分数据来源。
# 使用 keys 区分数据来源
result = pd.concat([df1, df3], keys=['老员工', '新员工'])
print(result)

# 只保留共有列(inner join)
result = pd.concat([df1, df2], axis=1, join='inner')
print(result)

🔗 类似 SQL 的合并:pd.merge()

merge() 根据一个或多个键将两个 DataFrame 的行连接起来,类似于 SQL 中的 JOIN 操作。

参数 说明 常用值
how 连接方式 'inner', 'left', 'right', 'outer'
on 用于连接的列名(两表共有) 列名或列名列表
left_on, right_on 左右表分别指定的连接列 列名
left_index, right_index 是否使用索引作为连接键 True/False
suffixes 当列名冲突时添加的后缀 默认 ('_x', '_y')
# 内连接 (默认 how='inner')
df_inner = pd.merge(df1, df2, on='员工ID')
print(df_inner)

# 左连接
df_left = pd.merge(df1, df2, on='员工ID', how='left')
print(df_left)

# 右连接
df_right = pd.merge(df1, df2, on='员工ID', how='right')
print(df_right)

# 全外连接
df_outer = pd.merge(df1, df2, on='员工ID', how='outer')
print(df_outer)

📌 使用不同列名进行连接

# 如果连接列在左右表中名称不同
df1 = df1.rename(columns={'员工ID': 'ID'})  # 将 df1 的列名改为 ID
df2 = df2.rename(columns={'员工ID': 'EmpID'})

df_merge = pd.merge(df1, df2, left_on='ID', right_on='EmpID')
print(df_merge)

📌 多键连接

# 创建包含复合键的 DataFrame
df_orders = pd.DataFrame({
    '客户ID': [1, 1, 2, 3],
    '产品ID': ['A', 'B', 'A', 'C'],
    '数量': [5, 3, 2, 1]
})

df_products = pd.DataFrame({
    '客户ID': [1, 1, 2, 3],
    '产品ID': ['A', 'B', 'A', 'C'],
    '价格': [10, 20, 10, 30]
})

# 使用多个键连接
df_detail = pd.merge(df_orders, df_products, on=['客户ID', '产品ID'])
print(df_detail)

📎 基于索引合并:join()

join()merge() 的简化版本,默认基于行索引进行连接,非常方便。

# 将 df2 的索引设为员工ID
df2_indexed = df2.set_index('员工ID')
df1_indexed = df1.set_index('员工ID')

# 左连接(默认 how='left')
result = df1_indexed.join(df2_indexed)
print(result)

# 也可以指定 how 和 on 参数
result = df1.join(df2.set_index('员工ID'), on='员工ID')  # 类似 merge

🧪 综合示例

import pandas as pd

# 销售数据
sales_2023 = pd.DataFrame({
    '日期': ['2023-01-01', '2023-01-02', '2023-01-03'],
    '产品': ['A', 'B', 'A'],
    '销售额': [100, 200, 150]
})

sales_2024 = pd.DataFrame({
    '日期': ['2024-01-01', '2024-01-02', '2024-01-03'],
    '产品': ['B', 'C', 'A'],
    '销售额': [250, 180, 220]
})

产品信息 = pd.DataFrame({
    '产品': ['A', 'B', 'C'],
    '类别': ['电子产品', '家居用品', '服饰'],
    '单价': [20, 30, 40]
})

# 1. 纵向合并两年销售数据
all_sales = pd.concat([sales_2023, sales_2024], ignore_index=True)
print("合并销售记录:\n", all_sales)

# 2. 与产品信息左连接,获取产品类别和单价
sales_with_product = pd.merge(all_sales, 产品信息, on='产品', how='left')
print("\n带产品信息的销售数据:\n", sales_with_product)

# 3. 计算总销售额
sales_with_product['总价'] = sales_with_product['销售额'] * sales_with_product['单价']
print("\n计算总价:\n", sales_with_product)

⚠️ 注意事项

重复列名:合并时若存在重复列名,默认会自动添加后缀(如 _x, _y),可通过 suffixes 参数自定义。
索引对齐:使用 concat(axis=1)join() 时,Pandas 会基于索引对齐,如果索引不匹配会产生缺失值。
大数据量性能:对于大型 DataFrame,merge() 可能会消耗较多内存,可以考虑提前对连接键排序或使用 categorical 类型优化。
连接键数据类型:确保连接键的数据类型一致,否则可能匹配失败。例如 int 和 float 可能被视为不同。
最佳实践:
  • 简单拼接用 concat(),类似 SQL 连接用 merge(),基于索引连接用 join()
  • 合并前先用 info() 检查数据类型,确保连接键类型一致。
  • 多表合并时,逐步合并并检查中间结果,避免错误扩散。
  • 如果只需部分列,可以在合并前先用 usecolsdrop() 减少数据量。