下面我将从创建、合并、比较、迭代等多个方面,系统地介绍如何高效地处理多个 DataFrame。

创建多个 DataFrame
我们来看如何创建和存储多个 DataFrame,最直接的方法是使用字典,因为每个 DataFrame 都有一个唯一的名称(键)。
import pandas as pd
import numpy as np
# 创建第一个 DataFrame
df1 = pd.DataFrame({
'id': [1, 2, 3],
'name': ['Alice', 'Bob', 'Charlie'],
'score': [85, 92, 78]
})
# 创建第二个 DataFrame
df2 = pd.DataFrame({
'id': [2, 3, 4],
'name': ['Bob', 'Charlie', 'David'],
'salary': [50000, 60000, 45000]
})
# 创建第三个 DataFrame
df3 = pd.DataFrame({
'id': [1, 3, 5],
'name': ['Alice', 'Charlie', 'Eve'],
'department': ['HR', 'Engineering', 'Marketing']
})
# 使用字典存储多个 DataFrame
dataframes = {
'scores': df1,
'salaries': df2,
'departments': df3
}
# 访问特定的 DataFrame
print("--- 访问 df1 ---")
print(dataframes['scores'])
print("\n--- 访问 df2 ---")
print(dataframes['salaries'])
合并多个 DataFrame
合并是将多个 DataFrame 的数据根据某些规则组合在一起,这是处理多表数据的核心操作。
合并两个 DataFrame (pd.merge)
pd.merge() 类似于 SQL 中的 JOIN 操作,它需要一个“键”(key)来决定如何合并行。
内连接
只保留两个 DataFrame 中键(on 或 left_on/right_on 指定的列)都存在的行。

# 根据 'id' 列进行内连接
merged_inner = pd.merge(df1, df2, on='id', how='inner')
print("--- 内连接 (Inner Join) ---")
print(merged_inner)
# 输出只会包含 id 为 2 和 3 的行,因为它们在 df1 和 df2 中都存在。
左连接
保留左 DataFrame (df1) 的所有行,右 DataFrame (df2) 中匹配的行会被合并,不匹配的则为 NaN。
# 根据 'id' 列进行左连接
merged_left = pd.merge(df1, df2, on='id', how='left')
print("\n--- 左连接 (Left Join) ---")
print(merged_left)
# 输出会包含 df1 的所有行 (id 1, 2, 3)
右连接
与左连接相反,保留右 DataFrame (df2) 的所有行。
# 根据 'id' 列进行右连接
merged_right = pd.merge(df1, df2, on='id', how='right')
print("\n--- 右连接 (Right Join) ---")
print(merged_right)
# 输出会包含 df2 的所有行 (id 2, 3, 4)
外连接 保留两个 DataFrame 的所有行,不匹配的部分填充为 NaN。
# 根据 'id' 列进行外连接
merged_outer = pd.merge(df1, df2, on='id', how='outer')
print("\n--- 外连接 (Outer Join) ---")
print(merged_outer)
# 输出会包含 df1 和 df2 的所有 id (1, 2, 3, 4)
合并多个 DataFrame (pd.concat)
pd.concat() 用于将多个 DataFrame 堆叠在一起,通常是沿着行(axis=0)或列(axis=1)的方向。

垂直堆叠 (增加行)
# 垂直堆叠 df1 和 df3,忽略索引
concat_vertical = pd.concat([df1, df3], ignore_index=True)
print("--- 垂直堆叠 (增加行) ---")
print(concat_vertical)
# 会将 df3 的行追加到 df1 的后面
水平堆叠 (增加列)
# 水平堆叠 df1 和 df2,根据 'id' 对齐
# 这是一种快捷方式,相当于 merge 的左连接,但只保留匹配的键
concat_horizontal = pd.concat([df1, df2], axis=1)
print("\n--- 水平堆叠 (增加列) ---")
print(concat_horizontal)
# 会根据索引对齐,如果索引不匹配,会产生很多 NaN
# 更好的做法是先 merge 再 concat,或者直接使用 merge
合并多个 DataFrame (链式合并)
如果你想将多个 DataFrame 依次合并(A 和 B 合并,结果再和 C 合并),可以使用 reduce 函数。
from functools import reduce
# 将 df1, df2, df3 都根据 'id' 列进行外连接
merged_all = reduce(lambda left, right: pd.merge(left, right, on='id', how='outer'), [df1, df2, df3])
print("\n--- 合并所有 DataFrame ---")
print(merged_all)
比较多个 DataFrame
有时你需要检查多个 DataFrame 是否相同,或者找出它们的差异。
使用 df.equals()
检查两个 DataFrame 是否完全相同(包括列、索引和数据)。
df_copy = df1.copy()
print("\n--- 使用 equals() 比较两个 DataFrame ---")
print(f"df1 和 df_copy 是否相同: {df1.equals(df_copy)}") # True
print(f"df1 和 df2 是否相同: {df1.equals(df2)}") # False
使用 df.compare()
找出两个 DataFrame 之间的差异(Pandas 1.1.0+ 新增功能)。
df1_diff = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2_diff = pd.DataFrame({'A': [1, 2], 'B': [30, 40]})
print("\n--- 使用 compare() 比较差异 ---")
print(df1_diff.compare(df2_diff))
# 会清晰地显示出 B 列的差异
迭代和处理多个 DataFrame
当你需要对字典中的每个 DataFrame 执行相同的操作时,循环迭代非常方便。
使用 for 循环
print("\n--- 遍历字典中的所有 DataFrame ---")
for name, df in dataframes.items():
print(f"\n正在处理 DataFrame: {name}")
print(f"形状: {df.shape}")
print(f"列名: {df.columns.tolist()}")
# 示例操作:计算数值列的平均值
numeric_cols = df.select_dtypes(include=np.number)
if not numeric_cols.empty:
print(f"数值列的平均值:\n{numeric_cols.mean()}")
使用列表推导式
如果你想对每个 DataFrame 执行一个操作并收集结果,列表推导式非常高效。
# 示例:获取每个 DataFrame 的行数
row_counts = {name: len(df) for name, df in dataframes.items()}
print("\n--- 使用字典推导式获取每个 DataFrame 的行数 ---")
print(row_counts)
# 输出: {'scores': 3, 'salaries': 3, 'departments': 3}
高级技巧:处理文件夹中的多个 CSV 文件
一个实际场景是,你有一个文件夹,里面有很多 CSV 文件,你需要将它们全部读取并合并成一个大的 DataFrame。
import os
import glob
# 假设你有一个名为 'data_files' 的文件夹,里面有多个 CSV 文件
# 为了演示,我们先创建几个文件
os.makedirs('data_files', exist_ok=True)
df1.to_csv('data_files/file1.csv', index=False)
df2.to_csv('data_files/file2.csv', index=False)
df3.to_csv('data_files/file3.csv', index=False)
# 获取文件夹中所有 CSV 文件的路径
file_paths = glob.glob('data_files/*.csv')
# 读取所有文件并存储在一个字典中
dfs_from_files = {}
for path in file_paths:
# 从路径中提取文件名作为键
filename = os.path.basename(path)
dfs_from_files[filename] = pd.read_csv(path)
print("\n--- 从文件夹读取的所有 DataFrame ---")
for name, df in dfs_from_files.items():
print(f"\n文件名: {name}")
print(df)
# 合并所有从文件读取的 DataFrame
# 假设每个 CSV 文件的结构都相同,可以直接垂直堆叠
final_merged_df = pd.concat(dfs_from_files.values(), ignore_index=True)
print("\n--- 合并文件夹中所有 CSV 的最终结果 ---")
print(final_merged_df)
# 清理创建的文件夹
# import shutil
# shutil.rmtree('data_files')
| 任务 | 推荐方法 | 描述 |
|---|---|---|
| 存储 | 字典 {'name': df} |
为每个 DataFrame 提供一个有意义的名称,方便访问。 |
| 合并 (基于键) | pd.merge() |
当你有共同的列(如 id)需要将不同表格的信息关联起来时使用。 |
how='inner'/'left'/'right'/'outer' |
指定连接类型,决定保留哪些数据。 | |
| 合并 (堆叠) | pd.concat() |
当你想简单地将多个 DataFrame 上下拼接或左右拼接时使用。 |
axis=0 (垂直) / axis=1 (水平) |
指定拼接的方向。 | |
| 比较 | df.equals() |
检查两个 DataFrame 是否完全相同。 |
df.compare() |
找出两个 DataFrame 之间具体的数据差异。 | |
| 批量处理 | for 循环 / 字典/列表推导式 |
对字典中的每个 DataFrame 执行重复性操作。 |
| 批量读取 | glob + pd.read_csv + pd.concat |
高效地读取和合并一个文件夹中的所有 CSV 文件。 |
选择哪种方法取决于你的具体需求:你是想根据键关联数据,还是想简单地堆叠数据,或者是想批量处理它们,掌握这些技巧将让你在处理复杂的数据结构时更加得心应手。
