使用pandas合并两个CSV文件

12 次阅读

处理多个CSV数据文件时,最常见的需求之一就是将两个文件合并成一个完整的数据集。Python中的pandas提供了concat()和merge()等方法,可以根据实际数据结构选择合适的合并方式。对于简单的上下拼接,concat()更加直接;如果两个CSV文件存在共同字段,需要按照某个列进行关联,则更适合使用merge()。

一、准备两个CSV文件

假设有两个文件:

users_1.csv

csv
id,name,age
1,张三,25
2,李四,30
3,王五,28

users_2.csv

csv
id,name,age
4,赵六,26
5,钱七,32
6,孙八,29

两个文件的字段结构一致,希望将它们纵向合并,可以直接使用pandas的concat()方法。

二、使用concat合并两个CSV文件

最简单的实现方式如下:

Python
运行
import pandas as pd

df1 = pd.read_csv("users_1.csv")
df2 = pd.read_csv("users_2.csv")

result = pd.concat([df1, df2], ignore_index=True)

result.to_csv("users_all.csv", index=False, encoding="utf-8-sig")

执行后,users_all.csv中的数据为:

csv
id,name,age
1,张三,25
2,李四,30
3,王五,28
4,赵六,26
5,钱七,32
6,孙八,29

这里的ignore_index=True非常实用。它会重新生成连续的DataFrame索引,避免两个原始文件的索引重复。

index=False则用于保存CSV时不写入pandas默认生成的行索引,否则最终文件可能会多出一列没有实际业务意义的数据。

三、两个CSV字段不完全一致怎么办

实际项目中,两个CSV文件的列结构不一定完全相同。例如:

users_1.csv:

csv
id,name,age
1,张三,25
2,李四,30

users_2.csv:

csv
id,name,city
3,王五,北京
4,赵六,上海

仍然可以使用concat():

Python
运行
import pandas as pd

df1 = pd.read_csv("users_1.csv")
df2 = pd.read_csv("users_2.csv")

result = pd.concat([df1, df2], ignore_index=True)

print(result)

得到的结果类似:

   id name   age city
0   1   张三  25.0  NaN
1   2   李四  30.0  NaN
2   3   王五   NaN   北京
3   4   赵六   NaN   上海

pandas会自动按照列名进行对齐。某个文件不存在的字段会填充为NaN。

这种机制比直接拼接文本文件更加安全,因为它不会简单地按照字符位置连接,而是根据DataFrame的列结构完成数据对齐。

四、按照共同字段合并两个CSV文件

如果两个CSV文件不是简单的上下拼接,而是存在关联关系,就应该考虑merge()。

例如,第一个文件保存用户信息:

csv
id,name
1,张三
2,李四
3,王五

第二个文件保存订单信息:

csv
user_id,product,price
1,笔记本电脑,5999
2,手机,3999
3,显示器,1299

两个文件通过用户ID建立关系,可以这样处理:

Python
运行
import pandas as pd

users = pd.read_csv("users.csv")
orders = pd.read_csv("orders.csv")

result = pd.merge(
    users,
    orders,
    left_on="id",
    right_on="user_id",
    how="inner"
)

print(result)

输出结果:

   id name  user_id product  price
0   1   张三        1    笔记本电脑   5999
1   2   李四        2       手机   3999
2   3   王五        3      显示器   1299

merge()的核心是指定两个数据集之间的关联字段。

如果两个DataFrame使用相同的列名,例如都通过id关联,则代码可以简化:

Python
运行
result = pd.merge(df1, df2, on="id", how="inner")

五、merge中的how参数如何选择

how决定两个CSV文件的连接方式,常用的有四种。

1. inner:内连接

Python
运行
result = pd.merge(df1, df2, on="id", how="inner")

只保留两个文件中都存在的ID。

适合需要筛选出“双方都有对应数据”的场景。

2. left:左连接

Python
运行
result = pd.merge(df1, df2, on="id", how="left")

以第一个DataFrame为基础,第一个文件中的数据全部保留。如果第二个文件没有匹配记录,对应字段会显示为NaN。

3. right:右连接

Python
运行
result = pd.merge(df1, df2, on="id", how="right")

与left相反,以第二个DataFrame的数据为基础。

4. outer:全连接

Python
运行
result = pd.merge(df1, df2, on="id", how="outer")

两个文件中的数据都会保留,没有匹配的数据使用NaN填充。

因此,如果只是把两个结构相同的CSV文件上下合并,应优先考虑concat();如果是根据ID、订单号、商品编号等字段进行数据关联,则通常使用merge()。

六、合并后处理重复数据

多个CSV文件可能包含重复记录。如果业务上不允许重复数据,可以使用drop_duplicates():

Python
运行
result = pd.concat([df1, df2], ignore_index=True)

result = result.drop_duplicates()

result.to_csv("users_all.csv", index=False, encoding="utf-8-sig")

如果只希望按照某个字段判断重复,可以指定subset:

Python
运行
result = result.drop_duplicates(subset=["id"])

如果重复数据中需要保留最后一条记录,可以使用:

Python
运行
result = result.drop_duplicates(
    subset=["id"],
    keep="last"
)

其中keep="first"保留第一条,keep="last"保留最后一条,keep=False则会删除所有重复记录。

七、合并前处理中文CSV编码

中文CSV文件经常会遇到乱码问题。Windows环境下生成的CSV文件可能使用GBK编码,可以在读取时明确指定:

Python
运行
df1 = pd.read_csv("users_1.csv", encoding="gbk")
df2 = pd.read_csv("users_2.csv", encoding="gbk")

如果文件采用UTF-8编码:

Python
运行
df1 = pd.read_csv("users_1.csv", encoding="utf-8")

保存中文CSV时,可以使用:

Python
运行
result.to_csv(
    "users_all.csv",
    index=False,
    encoding="utf-8-sig"
)

utf-8-sig对Windows环境下的Excel兼容性通常比较好,可以减少直接用Excel打开CSV时出现中文乱码的情况。

八、一次合并多个CSV文件

如果实际需求从两个CSV扩展到了几十甚至上百个文件,没有必要逐个编写pd.concat()。

例如当前目录下有:

data/
├── data_01.csv
├── data_02.csv
├── data_03.csv
└── data_04.csv

可以使用glob批量获取CSV文件:

Python
运行
import glob
import pandas as pd

files = glob.glob("data/*.csv")

dataframes = [pd.read_csv(file) for file in files]

result = pd.concat(dataframes, ignore_index=True)

result.to_csv(
    "all_data.csv",
    index=False,
    encoding="utf-8-sig"
)

这种方式适合日志、销售数据、业务报表等按照日期或批次拆分保存的CSV数据。

如果合并后的文件也放在相同目录中,则需要注意避免下一次运行时把已经生成的结果文件再次读取进去。可以单独指定输入目录,或者排除输出文件。

九、合并前检查CSV结构

为了避免不同文件的字段存在明显差异,可以在合并之前检查列名:

Python
运行
import pandas as pd

df1 = pd.read_csv("users_1.csv")
df2 = pd.read_csv("users_2.csv")

print(df1.columns.tolist())
print(df2.columns.tolist())

也可以检查数据规模:

Python
运行
print(df1.shape)
print(df2.shape)

如果两个文件结构应该完全一致,还可以进行判断:

Python
运行
if list(df1.columns) != list(df2.columns):
    raise ValueError("两个CSV文件的列结构不一致")

result = pd.concat([df1, df2], ignore_index=True)

这种检查方式适合自动化数据处理脚本,可以在数据格式发生变化时及时停止,而不是生成一个看似正常、实际存在问题的结果文件。

十、处理不同列顺序的CSV文件

假设两个文件拥有相同字段,但列顺序不同:

文件1:id,name,age
文件2:age,id,name

使用concat()时,pandas会根据列名进行对齐:

Python
运行
result = pd.concat([df1, df2], ignore_index=True)

因此通常不需要手动调整列顺序。

如果希望最终文件严格按照指定顺序输出,可以重新排列:

Python
运行
result = result[["id", "name", "age"]]

这样可以保证最终CSV的字段顺序符合业务要求。

十一、处理两个CSV中的同名字段

使用merge()时,如果两个DataFrame除了关联字段外还存在相同名称的列,pandas通常会自动添加后缀。

例如:

Python
运行
result = pd.merge(
    df1,
    df2,
    on="id",
    how="inner",
    suffixes=("_left", "_right")
)

如果两个文件都有name字段,最终可能出现:

name_left
name_right

通过suffixes可以让这些字段名称更加容易理解。

十二、合并后验证数据是否正确

完成CSV合并后,不建议直接认为任务已经结束。可以简单检查行数、字段和缺失值:

Python
运行
print("数据行数:", len(result))
print("字段:", result.columns.tolist())
print(result.head())

检查缺失值:

Python
运行
print(result.isnull().sum())

如果使用concat()进行两个文件的纵向合并,并且两个文件没有重复记录,理论上的总行数通常应该接近:

Python
运行
expected = len(df1) + len(df2)

print("预期行数:", expected)
print("实际行数:", len(result))

如果中间使用了drop_duplicates(),实际行数小于预期则属于正常情况,但仍然应该进一步确认删除的重复记录是否符合业务规则。

十三、一个完整的CSV合并示例

对于字段一致、需要纵向合并的两个CSV,可以采用下面这种较完整的写法:

Python
运行
import pandas as pd

file1 = "users_1.csv"
file2 = "users_2.csv"
output_file = "users_all.csv"

df1 = pd.read_csv(file1, encoding="utf-8")
df2 = pd.read_csv(file2, encoding="utf-8")

# 检查字段
if set(df1.columns) != set(df2.columns):
    raise ValueError("两个CSV文件的字段不一致")

# 按照第一个文件的字段顺序统一列
df2 = df2[df1.columns]

# 合并数据
result = pd.concat(
    [df1, df2],
    ignore_index=True
)

# 删除完全重复的记录
result = result.drop_duplicates()

# 保存结果
result.to_csv(
    output_file,
    index=False,
    encoding="utf-8-sig"
)

print(f"合并完成,共保存 {len(result)} 条数据")

这个版本增加了字段检查、列顺序统一、重复数据处理以及结果统计,更适合实际项目中的数据处理脚本。

十四、concat和merge应该怎么选

两种方法虽然都可以实现“合并”,但解决的问题并不相同。

方法主要用途常见场景
pd.concat()沿行或列方向拼接多个结构相似的CSV合并
pd.merge()根据字段建立关联用户表与订单表关联
drop_duplicates()删除重复记录合并后数据去重
read_csv()读取CSV将CSV转换为DataFrame
to_csv()输出CSV保存合并后的数据

可以简单理解为:数据是一批一批追加的,用concat();数据需要通过某个字段“对应起来”,用merge()。

对于最常见的“把两个结构相同的CSV文件合成一个CSV文件”需求,核心代码其实只有几行:

Python
运行
import pandas as pd

df1 = pd.read_csv("file1.csv")
df2 = pd.read_csv("file2.csv")

result = pd.concat([df1, df2], ignore_index=True)

result.to_csv("result.csv", index=False, encoding="utf-8-sig")

掌握read_csv()、concat()、merge()和to_csv()之后,就能够覆盖大多数pandas CSV合并场景。实际使用时,应先明确两个文件是“纵向追加”还是“按照字段关联”,再选择对应的方法,这样既能避免数据错位,也便于后续的数据清洗和分析。