Skip to content

Pandas DataFrame导出CSV:to_csv()完全指南

Updated on

你清理了数据,转换了每一列,运行了分析。现在你需要分享结果——但导出的CSV文件在Excel中打开时出现乱码、多了一个不需要的索引列、或者一个4GB的文件让同事的笔记本电脑卡死了。将Pandas DataFrame导出为CSV听起来很简单,直到编码、分隔符、压缩和大文件性能把一行代码变成了调试会议。

这些问题很常见,因为to_csv()有超过20个参数,默认值并不总是与下游消费者的期望匹配。错误的编码会破坏非ASCII文本。缺少index=False会添加一个神秘的列。没有压缩会使合理大小的数据集变成超大的文件附件。

DataFrame.to_csv()方法在你知道该设置哪些参数后就能处理所有这些情况。本指南涵盖了从基本导出到数百万行DataFrame的分块写入等各种实际场景,让你能够一次就正确导出数据。

📚

基本用法

最简单的调用将DataFrame写入当前工作目录的CSV文件:

import pandas as pd
 
df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [28, 35, 42],
    'salary': [72000, 88000, 95000]
})
 
df.to_csv('employees.csv')

这会创建包含以下内容的employees.csv

,name,age,salary
0,Alice,28,72000
1,Bob,35,88000
2,Charlie,42,95000

注意第一列——那是DataFrame的索引。大多数情况下你不需要它。用index=False修复:

df.to_csv('employees.csv', index=False)

输出:

name,age,salary
Alice,28,72000
Bob,35,88000
Charlie,42,95000

完整方法签名

以下是to_csv()的完整签名,包含最常用的参数:

DataFrame.to_csv(
    path_or_buf=None,   # 文件路径或缓冲区
    sep=',',            # 分隔符
    na_rep='',          # 缺失值的字符串
    float_format=None,  # 浮点数格式字符串
    columns=None,       # 要写入的列子集
    header=True,        # 写入列名
    index=True,         # 写入行索引
    index_label=None,   # 索引的列标签
    mode='w',           # 写入模式 ('w', 'a', 'x')
    encoding=None,      # 文件编码(默认utf-8)
    compression='infer',# 压缩类型
    quoting=None,       # csv.QUOTE_*常量
    lineterminator=None,# 换行符
    chunksize=None,     # 每块行数
    date_format=None,   # 日期时间格式字符串
    errors='strict',    # 编码错误处理
)

关键参数详解

sep——自定义分隔符

并非所有系统都读取逗号。使用sep更改分隔符:

import pandas as pd
 
df = pd.DataFrame({
    'product': ['Widget A', 'Widget B'],
    'price': [19.99, 29.99]
})
 
# 制表符分隔
df.to_csv('products.tsv', sep='\t', index=False)
 
# 分号分隔(在欧洲地区设置中常见)
df.to_csv('products_eu.csv', sep=';', index=False)
 
# 管道符分隔
df.to_csv('products.txt', sep='|', index=False)

columns——导出子集

只写入特定列而非整个DataFrame:

import pandas as pd
 
df = pd.DataFrame({
    'id': [1, 2, 3],
    'name': ['Alice', 'Bob', 'Charlie'],
    'email': ['a@x.com', 'b@x.com', 'c@x.com'],
    'internal_score': [0.87, 0.92, 0.78]
})
 
# 只导出客户需要的列
df.to_csv('export.csv', columns=['id', 'name', 'email'], index=False)

header——控制列名

import pandas as pd
 
df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'score': [85, 92]
})
 
# 没有表头行
df.to_csv('no_header.csv', header=False, index=False)
 
# 自定义表头名称
df.to_csv('custom_header.csv', header=['姓名', '考试成绩'], index=False)

na_rep——表示缺失值

默认情况下,NaN值变为空字符串。使用na_rep使其显式化:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'sensor': ['A', 'B', 'C'],
    'reading': [23.5, np.nan, 18.2]
})
 
# 默认:NaN为空字符串
df.to_csv('readings_default.csv', index=False)
# sensor,reading
# A,23.5
# B,
# C,18.2
 
# 显式缺失值标记
df.to_csv('readings_marked.csv', na_rep='NULL', index=False)
# sensor,reading
# A,23.5
# B,NULL
# C,18.2

float_format——控制小数精度

import pandas as pd
 
df = pd.DataFrame({
    'item': ['Widget', 'Gadget'],
    'price': [19.999999, 42.123456]
})
 
# 两位小数
df.to_csv('prices.csv', float_format='%.2f', index=False)
# item,price
# Widget,20.00
# Gadget,42.12

date_format——格式化日期时间列

import pandas as pd
 
df = pd.DataFrame({
    'event': ['Launch', 'Review'],
    'date': pd.to_datetime(['2026-03-15 14:30:00', '2026-04-01 09:00:00'])
})
 
# 仅ISO日期(无时间)
df.to_csv('events.csv', date_format='%Y-%m-%d', index=False)
# event,date
# Launch,2026-03-15
# Review,2026-04-01

处理编码和特殊字符

编码是CSV文件出现乱码的首要原因。to_csv()的默认编码是utf-8,适用于大多数系统。但Windows上的Excel需要BOM(字节顺序标记)才能正确显示非ASCII字符。

UTF-8(默认)

import pandas as pd
 
df = pd.DataFrame({
    'city': ['苏黎世', '慕尼黑', '东京'],
    'greeting': ['你好', '你好', 'こんにちは']
})
 
# 标准UTF-8
df.to_csv('cities.csv', index=False, encoding='utf-8')

带BOM的UTF-8(用于Excel)

如果你的CSV包含重音字符、CJK文本或特殊符号,需要在Microsoft Excel中正确打开:

import pandas as pd
 
df = pd.DataFrame({
    'city': ['苏黎世', '慕尼黑', '圣保罗', '北京'],
    'population': [434000, 1472000, 12330000, 21540000]
})
 
# utf-8-sig添加Excel能识别的BOM
df.to_csv('cities_excel.csv', index=False, encoding='utf-8-sig')

其他编码

import pandas as pd
 
df = pd.DataFrame({'col': ['data']})
 
# Latin-1用于旧西欧系统
df.to_csv('legacy.csv', index=False, encoding='latin-1')
 
# Shift-JIS用于旧日本系统
df.to_csv('japanese.csv', index=False, encoding='shift_jis')
编码何时使用Excel兼容?
utf-8大多数系统、API、数据库的默认选择部分(无BOM)
utf-8-sigWindows上带非ASCII文本的Excel
latin-1 / iso-8859-1旧西欧系统
shift_jis旧日本系统
cp1252Windows西欧

大型DataFrame:压缩和分块

压缩

压缩可以在不丢失数据的情况下将文件大小减少60-90%。Pandas支持多种格式:

import pandas as pd
import numpy as np
 
# 创建大型DataFrame
df = pd.DataFrame({
    'id': range(1_000_000),
    'value': np.random.randn(1_000_000),
    'category': np.random.choice(['A', 'B', 'C', 'D'], 1_000_000)
})
 
# gzip压缩(最常见)
df.to_csv('large_data.csv.gz', index=False, compression='gzip')
 
# zip压缩
df.to_csv('large_data.zip', index=False, compression='zip')
 
# bz2压缩(更小但更慢)
df.to_csv('large_data.csv.bz2', index=False, compression='bz2')
 
# Zstandard(快速、压缩比好——需要zstandard包)
df.to_csv('large_data.csv.zst', index=False, compression='zstd')
压缩文件扩展名速度大小减少回读
.csv最快写入0%pd.read_csv('f.csv')
gzip.csv.gz中等70-85%pd.read_csv('f.csv.gz')
zip.zip中等70-85%pd.read_csv('f.zip')
bz2.csv.bz275-90%pd.read_csv('f.csv.bz2')
zstd.csv.zst70-85%pd.read_csv('f.csv.zst')

Pandas会自动从文件扩展名推断压缩格式,所以compression='infer'(默认值)通常可以正常工作。

Chunksize——批量写入

对于内存压力很大的超大DataFrame,分块写入:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'id': range(5_000_000),
    'value': np.random.randn(5_000_000)
})
 
# 每次写入500,000行
df.to_csv('huge_data.csv', index=False, chunksize=500_000)

chunksize参数不会减少最终文件大小——它控制一次刷新到磁盘的行数。这有助于减少写入操作期间的峰值内存使用。

写入字符串或缓冲区

你并不总是写入文件。有时你需要CSV作为字符串用于API调用、数据库插入或内存处理:

import pandas as pd
 
df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'score': [85, 92]
})
 
# 获取CSV作为字符串
csv_string = df.to_csv(index=False)
print(csv_string)
# name,score
# Alice,85
# Bob,92

写入StringIO或BytesIO

import pandas as pd
from io import StringIO, BytesIO
 
df = pd.DataFrame({
    'x': [1, 2, 3],
    'y': [4, 5, 6]
})
 
# StringIO缓冲区
buffer = StringIO()
df.to_csv(buffer, index=False)
csv_text = buffer.getvalue()
 
# BytesIO缓冲区(适用于HTTP响应、S3上传)
byte_buffer = BytesIO()
df.to_csv(byte_buffer, index=False, encoding='utf-8')
csv_bytes = byte_buffer.getvalue()

当上传CSV数据到云存储(S3、GCS)而不创建本地文件时,这种模式很常见。

追加到现有CSV

要在不覆盖现有文件的情况下添加行,使用mode='a'header=False

import pandas as pd
 
# 第一批——带表头写入
df1 = pd.DataFrame({'name': ['Alice'], 'score': [85]})
df1.to_csv('results.csv', index=False)
 
# 第二批——追加不重复表头
df2 = pd.DataFrame({'name': ['Bob', 'Charlie'], 'score': [92, 78]})
df2.to_csv('results.csv', mode='a', header=False, index=False)

生成的文件有一个表头行,后面是所有三行数据。省略header=False会在数据中间插入第二个表头行。

引号和转义

包含分隔符、换行符或引号的字段需要正确的引号处理。Pandas自动处理这些,但你可以控制行为:

import pandas as pd
import csv
 
df = pd.DataFrame({
    'name': ['O\'Brien', 'Smith, Jr.', 'Alice "Ace" Wong'],
    'notes': ['包含撇号', '包含逗号', '包含引号']
})
 
# 默认:仅在必要时加引号 (QUOTE_MINIMAL)
df.to_csv('minimal.csv', index=False)
 
# 所有字段加引号
df.to_csv('all_quoted.csv', index=False, quoting=csv.QUOTE_ALL)
 
# 仅非数字字段加引号
df.to_csv('nonnumeric.csv', index=False, quoting=csv.QUOTE_NONNUMERIC)

导出格式比较:to_csv() vs to_excel() vs to_parquet()

选择正确的导出格式取决于谁使用文件以及文件有多大:

特性to_csv()to_excel()to_parquet()
文件格式纯文本二进制 (xlsx)二进制(列式)
人类可读通过Excel/Sheets
文件大小(100万行)~50-100 MB~30-60 MB~5-15 MB
写入速度
读取速度中等非常快
类型保留否(全部为字符串)部分完全
需要额外库openpyxlpyarrow / fastparquet
压缩支持gzip, zip, bz2, zstd内置内置 (snappy, gzip)
最适合互操作性、API、快速共享商业用户、Excel用户分析管道、大数据

经验法则:与非技术用户或外部系统共享数据时使用CSV。在速度和类型保真度重要的内部管道中使用Parquet。当接收者需要格式化或多个工作表时使用Excel。

常见陷阱及如何避免

1. 不需要的索引列

最常见的投诉。除非索引包含有意义的数据,否则始终传递index=False

# 错误——回读时添加神秘的"Unnamed: 0"列
df.to_csv('data.csv')
 
# 正确
df.to_csv('data.csv', index=False)

2. 回读时的无名列

如果你带索引导出然后回读文件,会得到一个Unnamed: 0列:

import pandas as pd
 
# 读取带索引导出的CSV
df = pd.read_csv('data.csv', index_col=0)  # 告诉read_csv哪一列是索引

3. 日期时间精度丢失

没有date_format,日期时间列会以包括微秒在内的完整时间戳导出。显式控制:

import pandas as pd
 
df = pd.DataFrame({
    'ts': pd.to_datetime(['2026-01-15 08:30:00.123456'])
})
 
df.to_csv('ts.csv', index=False, date_format='%Y-%m-%d %H:%M:%S')

4. 浮点数舍入问题

浮点数可能产生意外的尾部数字:

import pandas as pd
 
df = pd.DataFrame({'val': [0.1 + 0.2]})
df.to_csv('float.csv', index=False)
# val
# 0.30000000000000004
 
df.to_csv('float_clean.csv', index=False, float_format='%.2f')
# val
# 0.30

5. 列中的混合类型

如果列有混合类型(整数和字符串),to_csv()会将所有内容转换为字符串。导出前验证数据类型:

import pandas as pd
 
df = pd.DataFrame({'id': [1, 2, 'three']})
print(df.dtypes)
# id    object  <-- 混合类型
 
# 导出前清理
df['id'] = pd.to_numeric(df['id'], errors='coerce')
df.to_csv('clean.csv', index=False)

导出前使用PyGWalker可视化数据

在将DataFrame导出为CSV之前,验证数据是否正确很有帮助。PyGWalker (opens in a new tab)可以将任何Pandas DataFrame在Jupyter Notebook中转变为交互式可视界面——拖放列来创建图表,无需编写绑图代码:

import pandas as pd
import pygwalker as pyg
 
df = pd.read_csv('raw_data.csv')
 
# 导出前可视化探索
walker = pyg.walk(df)
 
# 满意后导出
df.to_csv('verified_export.csv', index=False, encoding='utf-8-sig')

这个工作流程可以在问题到达CSV消费者之前捕获意外的空值、异常值或错误的数据类型。

FAQ

如何不带索引将Pandas DataFrame导出为CSV?

to_csv()传递index=Falsedf.to_csv('file.csv', index=False)。这可以防止行索引作为第一列写入。回读文件时,不会看到额外的Unnamed: 0列。

导出CSV时如何处理特殊字符和编码?

如果CSV需要在Microsoft Excel中正确打开非ASCII字符(重音字母、CJK文本),使用encoding='utf-8-sig'。对于标准系统和API,默认的encoding='utf-8'即可。对于旧系统,使用它们要求的特定编码(latin-1shift_jis等)。

从Pandas导出时如何压缩大型CSV文件?

在文件名中添加压缩扩展名,Pandas会处理其余部分:df.to_csv('data.csv.gz', index=False)创建gzip压缩文件。也可以显式设置compression='gzip''zip''bz2''zstd'。压缩文件通常小70-90%,可以直接用pd.read_csv()回读。

可以向现有CSV文件追加数据而不是覆盖吗?

可以。使用mode='a'header=Falsedf.to_csv('file.csv', mode='a', header=False, index=False)mode='a'以追加模式打开文件,header=False防止在文件中间写入重复的表头行。

Pandas中to_csv()和to_parquet()的区别是什么?

to_csv()生成人类可读的文本文件但丢失类型信息(所有内容变为字符串)。to_parquet()创建紧凑的二进制文件,保留数据类型(整数、浮点数、日期时间),读取速度快5-10倍,体积小5-10倍。与非技术用户或外部系统共享时使用CSV。在性能和类型保真度重要的内部数据管道中使用Parquet。

总结

DataFrame.to_csv()是将Pandas数据导出为CSV文件的标准方法。要获得干净的导出,始终传递index=False。当Excel兼容性重要时使用encoding='utf-8-sig'。用gzip或zstd压缩大文件以减少70-90%的大小。用float_format控制小数精度,用na_rep处理缺失值,用mode='a'追加到现有文件。对于类型保留和速度重要的数据管道,考虑使用to_parquet()。导出前,使用PyGWalker等工具可视化检查数据,尽早发现问题。

📚