数据文件读取(CSV/Excel/JSON/Parquet)

数据分析专题 · 多格式数据导入

专题:Python数据分析系统学习

关键词:数据分析, Pandas, CSV, Excel, JSON, Parquet, read_csv, 文件读取, 大文件处理

一、CSV文件读取详解

CSV(Comma-Separated Values)是最常见的数据交换格式之一。Pandas 的 read_csv() 函数提供了极为丰富的参数体系,能够应对各种格式变体和大数据场景。理解每个核心参数的用途,是高效数据处理的第一步。

1.1 基础读取

最简单的用法只需传入文件路径,Pandas 会自动推断分隔符、列名和数据类型。

import pandas as pd # 基础读取,自动推断 df = pd.read_csv("sales_data.csv") print(df.head()) print(df.info())

1.2 分隔符设置

实际数据中分隔符不一定是逗号,可能是制表符(\t)、分号、管道符等。sep 指定分隔符,delimitersep 的别名。对于正则表达式级别的分隔规则,可以传入正则字符串。

# 制表符分隔的TSV文件 df = pd.read_csv("data.tsv", sep="\t") # 分号分隔(欧洲CSV常见格式) df = pd.read_csv("europe_data.csv", sep=";") # 分隔符简写:s% 表示 sep="\t" 等同于 sep="\t" df = pd.read_csv("data.tsv", delim_whitespace=True)

1.3 列名与表头控制

header 参数指定哪一行作为列名,默认为 0(第一行)。如果文件没有列名,设置 header=None 并用 names 手动指定列名。skiprows 可跳过文件前 N 行或指定行号的多行。

# 文件无表头,手动指定列名 df = pd.read_csv("no_header.csv", header=None, names=["编号", "姓名", "年龄", "城市"]) # 跳过文件前2行和注释行 df = pd.read_csv("messy_data.csv", skiprows=[0, 1, 3]) # 跳过第0、1、3行

1.4 行索引设置

index_col 指定哪一列作为行索引,可以是列名(字符串)或列位置(整数),也支持多级索引(传入列表)。

# 使用列名设置索引 df = pd.read_csv("employees.csv", index_col="员工ID") # 使用列位置(第0列) df = pd.read_csv("employees.csv", index_col=0) # 多级索引:将多列组合成MultiIndex df = pd.read_csv("multi_level.csv", index_col=["年份", "季度"])

1.5 数据类型控制

默认情况下 Pandas 会尝试推断每列的数据类型,但自动推断有时会出错(如带前导零的 ID 列被读成整数)。dtype 参数以字典形式指定各列的类型,parse_dates 自动解析日期列。

# 指定列的数据类型 df = pd.read_csv("orders.csv", dtype={ "订单号": str, # 保留前导零 "会员ID": "category", # 转为分类类型,节省内存 "金额": float }) # 指定日期列(自动解析多种日期格式) df = pd.read_csv("orders.csv", parse_dates=["下单时间", "发货时间"]) # 合并多列为日期 df = pd.read_csv("dates.csv", parse_dates={"日期": ["年", "月", "日"]})

1.6 编码与缺失值处理

中文CSV文件常见编码问题,encoding 参数指定文件编码。对于 GBK/GB2312 编码的文件,需要相应设置。na_values 指定哪些值应被视为缺失值,keep_default_na 控制是否保留默认的 NA 标记。

# 处理中文编码 df = pd.read_csv("中文数据.csv", encoding="gbk") # Windows简体中文 df = pd.read_csv("data.csv", encoding="utf-8-sig") # 处理BOM头 # 自定义缺失值标记 df = pd.read_csv("survey.csv", na_values=["", "NA", "N/A", "NULL", "不详", "-"], keep_default_na=False)

1.7 列筛选与读取限制

usecols 仅读取指定列,极大减少内存占用;nrows 限制读取行数,适合预览或抽样。

# 只读取需要的列 df = pd.read_csv("wide_data.csv", usecols=["姓名", "年龄", "收入"]) # 按列位置读取(第0、2、4列) df = pd.read_csv("wide_data.csv", usecols=[0, 2, 4]) # 只读取前100行用于快速预览 df_sample = pd.read_csv("huge_file.csv", nrows=100)

技巧:善用 nrows + usecols 组合可以快速预览大型文件的结构和数据类型,确认无误后再进行全量读取。

二、Excel文件读取

Excel 是企业和非技术团队最常用的数据格式。Pandas 的 read_excel() 支持 .xls 和 .xlsx 格式,需要安装 openpyxl(.xlsx)或 xlrd(旧版 .xls)引擎。

2.1 基础读取与工作表选择

sheet_name 参数可以选择要读取的工作表,支持传入工作表名称(字符串)、位置索引(整数,从0开始)、或者列表批量读取多个表。

# 读取默认第一个工作表 df = pd.read_excel("report.xlsx") # 按名称指定工作表 df_sales = pd.read_excel("report.xlsx", sheet_name="销售数据") # 按索引指定工作表(第2个工作表) df_second = pd.read_excel("report.xlsx", sheet_name=1) # 批量读取多个工作表,返回OrderedDict sheets = pd.read_excel("multi_sheet.xlsx", sheet_name=["Sheet1", "Sheet2", "汇总"]) df_s1 = sheets["Sheet1"] # 读取所有工作表 all_sheets = pd.read_excel("report.xlsx", sheet_name=None) # 返回 {sheet_name: DataFrame} 字典

2.2 表头与区域控制

Excel 文件经常包含合并单元格、多级表头或表头不在第一行的情况。header 指定哪一行(或哪几行)作为列名,skiprows 跳过前面的非数据行。

# 表头在第2行(0-indexed),跳过第1行的标题 df = pd.read_excel("formatted_report.xlsx", header=1) # 多级表头(前2行合并为MultiIndex列名) df = pd.read_excel("merged_header.xlsx", header=[0, 1]) # 跳过前3行无用的说明 df = pd.read_excel("report.xlsx", skiprows=3)

2.3 引擎选择

Pandas 读取 Excel 时依赖后端引擎。不同引擎的特性对比如下:

引擎格式依赖包特点
openpyxl.xlsxopenpyxl支持读写,功能全面,支持样式读取
xlrd.xlsxlrd >= 1.2.0仅读取旧版 .xls,新版 xlrd 已放弃 .xlsx 支持
pyxlsb.xlsbpyxlsb读取二进制 Excel 文件,速度较快
calamine.xlsx/.xlspython-calamine纯 Rust 实现,速度极快

可以通过 engine 参数显式指定引擎。不指定时 Pandas 会根据文件扩展名自动选择。

# 显式指定引擎 df = pd.read_excel("report.xls", engine="xlrd") # 使用calamine引擎(更快) df = pd.read_excel("large_report.xlsx", engine="calamine")

注意:读取 Excel 文件前请确保已安装所需引擎包:pip install openpyxl xlrd。对于大型 Excel 文件,推荐使用 calamine 引擎以获得更快的读取速度。

三、JSON数据读取与处理

JSON 是 Web API 和 NoSQL 数据库中最常用的数据交换格式。Pandas 的 read_json() 函数可以直读标准 JSON,而 json_normalize() 则用于展开嵌套 JSON 结构为扁平表格。

3.1 read_json 基础与 orient 参数

read_json()orient 参数指定 JSON 数据的结构格式,不同 API 返回的 JSON 结构差异很大,必须选择正确的 orient。

# JSON按列存储(最常见,类似DataFrame的列式表示) # {"col1": {"idx0": val, "idx1": val}, "col2": {...}} df = pd.read_json("column_oriented.json", orient="columns") # JSON按行存储(每行一个记录,API常用) # [{"col1": val, "col2": val}, {"col1": val, "col2": val}] df = pd.read_json("records.json", orient="records") # 索引格式(以索引为键的对象) # {"idx0": {"col1": val}, "idx1": {"col1": val}} df = pd.read_json("index_oriented.json", orient="index") # 值格式(二维数组) # [[val, val], [val, val]] df = pd.read_json("values.json", orient="values")

3.2 嵌套JSON展开

现实中的 JSON 数据往往包含嵌套对象(如用户信息中的地址字段),json_normalize()(从 Pandas 1.3 起推荐使用 pd.json_normalize())可以自动展开嵌套层级。

from pandas import json_normalize # 嵌套JSON示例 data = [ { "name": "张三", "orders": [ {"id": 1001, "amount": 250.0}, {"id": 1002, "amount": 180.0} ], "address": {"city": "北京", "district": "朝阳"} }, { "name": "李四", "orders": [ {"id": 1003, "amount": 320.0} ], "address": {"city": "上海", "district": "浦东"} } ] # 展开嵌套:指定记录路径和元数据前缀 df = json_normalize( data, record_path="orders", # 要展开的嵌套列表 meta=["name", # 保留的外层字段 ["address", "city"], # 深层嵌套字段展开 ["address", "district"]], record_prefix="order_" # 展开字段前缀 )

3.3 API数据实时读取

结合 requests 库可以实时从 Web API 拉取 JSON 数据并直接转为 DataFrame,这是数据采集的常见模式。

import requests # 从API获取JSON数据 url = "https://api.example.com/v1/sales?date=2026-01-01" response = requests.get(url, headers={"Authorization": "Bearer token"}) data = response.json() # 直接转为DataFrame df = pd.json_normalize(data, sep="_")

技巧:使用 json_normalizemax_level 参数可以控制嵌套展开的最大层数,避免过度展开导致列数过多。设置 max_level=0 只展开第一层。

四、Parquet与Feather格式

Parquet 和 Feather 是两种现代化的列式存储格式,在性能上远超 CSV 和 Excel,特别适合大数据场景和数据分析流水线。它们的核心优势在于列式存储、高效压缩和快速 IO。

4.1 Parquet格式读取

Parquet 是 Apache 生态中的标准列式存储格式,支持复杂的嵌套数据结构、高效的压缩算法(Snappy、ZSTD、Gzip等),以及谓词下推(只读取需要的行和列)。

# 基础读取Parquet文件 df = pd.read_parquet("sales_data.parquet") # 使用pyarrow引擎(推荐,更快) df = pd.read_parquet("sales_data.parquet", engine="pyarrow") # 使用fastparquet引擎 df = pd.read_parquet("sales_data.parquet", engine="fastparquet") # 只读取指定列(谓词下推,减少IO) df = pd.read_parquet("wide_table.parquet", columns=["id", "name", "amount"])

4.2 Parquet的压缩与分区

Parquet 支持多种压缩算法,可以在文件大小和读取速度之间取得平衡。分区存储(Partitioning)是 Parquet 在大数据场景下的重要特性。

# 写入时指定压缩算法 df.to_parquet("compressed.parquet", compression="zstd") # ZSTD: 高压缩比,速度较快 df.to_parquet("snappy.parquet", compression="snappy") # Snappy: 平衡压缩比和速度 df.to_parquet("gzip.parquet", compression="gzip") # Gzip: 高压缩比,速度较慢 # 按分区写入(目录分区结构) df.to_parquet("partitioned_data/", partition_cols=["year", "month"]) # 直接读取分区数据 df = pd.read_parquet("partitioned_data/")

4.3 Feather格式

Feather 是专为 Python 和 R 语言之间高速数据交换设计的二进制格式,由 Arrow 项目提供支持。它的读写速度比 Parquet 更快,但文件压缩率略低。

# 读取Feather文件 df = pd.read_feather("data.feather") # 写入Feather文件 df.to_feather("data.feather", compression="zstd") # Feather也支持压缩

4.4 格式对比

特性CSVExcelParquetFeather
读取速度较慢极快
压缩率
列式存储
Schema丰富
跨语言通用通用多语言Python/R
谓词下推不支持不支持支持不支持
适用场景小型数据交换报表展示大数据处理进程间通信

最佳实践:在数据分析项目中,建议使用 Parquet 作为中间存储格式(兼顾压缩率和性能),CSV 用于外部数据交换,Feather 用于同一台机器上 Python 和 R 之间的快速数据传递。

五、HTML表格与剪贴板读取

除了常规数据文件,Pandas 还提供了从 HTML 网页和系统剪贴板直接读取数据的能力,这在快速数据采集和日常办公中非常实用。

5.1 HTML表格读取

read_html() 可以从网页 URL 或 HTML 字符串中提取所有 <table> 标签,返回 DataFrame 列表。这个函数依赖 lxmlhtml5lib 解析器。

# 从网页URL读取所有表格 tables = pd.read_html("https://example.com/statistics.html") df_table1 = tables[0] # 第一个表格 df_table2 = tables[1] # 第二个表格 # 从HTML字符串中读取 html_str = """ <table> <tr><th>姓名</th><th>分数</th></tr> <tr><td>小明</td><td>95</td></tr> <tr><td>小红</td><td>88</td></tr> </table> """ df = pd.read_html(html_str)[0] # 指定匹配条件(只读取包含"成绩"的表格) df = pd.read_html("grades.html", match="成绩", encoding="utf-8")[0] # 指定解析器(lxml速度更快) df = pd.read_html("page.html", flavor="lxml")[0]

5.2 剪贴板读取

read_clipboard() 是日常工作中极其高效的工具:复制 Excel 或网页中的表格数据后,直接调用此函数即可获得 DataFrame,无需先保存为文件。

# 复制数据后直接读取(Windows/Linux/macOS均支持) # 操作步骤:选中Excel表格数据 → Ctrl+C → 运行以下代码 df = pd.read_clipboard() # 指定分隔符(复制的是制表符分隔的数据) df = pd.read_clipboard(sep="\t") # 与Excel联动分析 # 1. 在Excel中筛选需要的数据 # 2. Ctrl+C 复制 # 3. 在Python中执行: raw = pd.read_clipboard() print(raw.describe())

技巧:read_clipboard() 本质上是读取系统剪贴板文本后调用 read_csv() 处理,因此大多数 read_csv 的参数(如 headernamesindex_coldtype)同样适用于 read_clipboard()

六、大文件分块读取策略

当文件无法一次性装入内存时,分块读取是必不可少的技巧。Pandas 提供了 chunksizeiterator 两种分块机制,配合逐块处理可以实现 TB 级数据的流式分析。

6.1 chunksize 参数

chunksize 指定每个块的行数,此时 read_csv() 返回一个 TextFileReader 迭代器对象,可以用 for 循环逐块处理。

# 按每10000行分块读取 chunk_iter = pd.read_csv("very_large_file.csv", chunksize=10000) # 逐块处理(例如:分批计算统计量) total_sum = 0 total_count = 0 for chunk in chunk_iter: chunk_sum = chunk["amount"].sum() chunk_count = len(chunk) total_sum += chunk_sum total_count += chunk_count print(f"已处理 {total_count} 行,当前累计金额: {total_sum:.2f}") average = total_sum / total_count print(f"全量数据平均金额: {average:.2f}")

6.2 iterator + get_chunk

使用 iterator=True 开启迭代器模式,配合 get_chunk() 方法可以更灵活地控制每次读取的行数,适合动态调整分块大小。

# 开启迭代器模式 reader = pd.read_csv("huge_log.csv", iterator=True) # 先读取5000行预览 first_chunk = reader.get_chunk(5000) print(first_chunk.info()) # 再读取20000行 second_chunk = reader.get_chunk(20000) # 逐块读取直到耗尽 batch_size = 10000 all_results = [] while True: try: chunk = reader.get_chunk(batch_size) # 处理逻辑 result = chunk.groupby("category")["value"].mean() all_results.append(result) except StopIteration: break # 合并所有块的结果 final_result = pd.concat(all_results).groupby(level=0).mean()

6.3 分块处理的进阶模式

在实际项目中,分块读取通常与数据清洗、特征工程和增量写入结合使用,形成一个完整的 ETL 流水线。

# 完整的ETL分块处理流水线 def process_chunk(chunk): """单块数据清洗函数""" # 清洗:去除空值行 chunk = chunk.dropna(subset=["id", "value"]) # 转换:统一日期格式 chunk["date"] = pd.to_datetime(chunk["date"], errors="coerce") # 特征:提取时间特征 chunk["year"] = chunk["date"].dt.year chunk["month"] = chunk["date"].dt.month # 过滤:只保留有效记录 chunk = chunk[chunk["value"] > 0] return chunk # 分块读取并增量写入Parquet reader = pd.read_csv("source_data.csv", chunksize=50000, dtype={"id": str}, parse_dates=["date"]) first_chunk = True for i, chunk in enumerate(reader): clean_chunk = process_chunk(chunk) if first_chunk: clean_chunk.to_parquet("clean_data.parquet", engine="pyarrow", compression="zstd") first_chunk = False else: clean_chunk.to_parquet("clean_data.parquet", engine="pyarrow", compression="zstd", mode="append") print(f"第 {i+1} 块处理完成,{len(clean_chunk)} 条有效记录")

注意:分块处理时需要特别注意数据类型的一致性。推荐在 read_csv 时通过 dtype 参数显式指定每列的数据类型,避免不同块中类型推断不一致导致的合并错误。此外,chunksize 的选择需要平衡内存占用和 IO 频率:块越大,处理速度越快但内存占用越高。

七、核心要点总结

1. 格式选择策略:小型数据(<100MB)使用 CSV 或 JSON 便于交换;中型数据使用 Parquet 获得压缩与性能平衡;大型数据必须使用 Parquet 的分区存储 + 谓词下推。Feather 适合同一机器上的跨语言快速传递。

2. 参数记忆锚点:记住 read_csv() 的核心参数口诀——"分(sep)头(header)名(names)索(index_col)类(dtype)期(parse_dates)编(encoding)缺(na_values)选(usecols)块(chunksize)",十个参数覆盖 90% 的日常场景。

3. JSON 处理关键:orient 参数决定数据解析成败,json_normalize() 是展开嵌套 JSON 的必备工具。API 返回的 JSON 多用 records 格式。

4. 大文件分块铁律:内存不够时永远使用 chunksizeiterator + get_chunk()。先预览(nrows=1000)确认结构,再分块处理。处理过程中推荐将中间结果写入 Parquet,避免内存溢出。

5. 日常操作效率:利用 read_clipboard() 实现 Excel 数据快速导入分析流程;利用 read_html() 从网页直接抓取表格数据,无需编写爬虫。