摘要
数据分析的第一步通常是读取数据。CSV、Excel 和数据库是最常见的数据来源,但真正落地时会遇到编码、分隔符、表头、日期、数据类型、缺失值、大文件、SQL 查询和连接管理等问题。这些问题看似琐碎,却往往决定了后续清洗和建模的成败------一个被误读为整数的订单号、一个因编码错误而乱码的中文列名,都可能在分析链路中埋下隐患。
本文围绕 pandas 的 read_csv、read_excel 和 read_sql,介绍常见数据读取场景、参数配置、类型控制、分块读取和读取后的校验流程。我们会从核心概念出发,逐步拆解每个参数的作用与适用场景,再通过 11 个实战示例演示如何应对真实数据中的典型问题,最后给出常见问题的排查思路和进阶实践建议,帮助建立稳定可复用的数据入口。
无论你是刚接触 pandas 的初学者,还是在维护数据管道的工程师,都可以从本文中找到可直接落地的读取方案,让数据入口更可靠、更可控。
一、背景与问题
读取数据看起来简单:
python
import pandas as pd
df = pd.read_csv("data.csv")
但真实数据往往并不理想:
- CSV 编码不统一,中文乱码。
- 分隔符不是逗号,而是制表符或竖线。
- 前几行是说明文字,不是表头。
- 日期字段格式混乱。
- 金额字段包含逗号、空字符串或单位。
- Excel 有多个工作表,表头位置不固定。
- 数据库字段类型和 pandas 推断类型不一致。
- 文件过大,一次性读取导致内存不足。
稳定的数据读取流程应该明确数据来源、字段类型、缺失值规则和读取后的校验步骤。
二、核心概念
1. 数据源类型
常见来源如下:
| 来源 | 特点 |
|---|---|
| CSV | 简单通用,适合导出和交换 |
| Excel | 业务人员常用,格式灵活但不够规范 |
| 数据库 | 适合结构化查询和增量读取 |
| JSON | 适合嵌套结构和接口数据 |
| Parquet | 列式存储,适合大数据分析 |
本文重点覆盖 CSV、Excel 和数据库。
2. 编码
编码决定字节如何解释为字符。常见编码包括:
utf-8utf-8-siggbkgb18030
中文 Windows 环境导出的 CSV 可能使用 gbk,Excel 更容易识别带 BOM 的 utf-8-sig。
3. 分隔符
CSV 并不一定用逗号分隔。常见分隔符包括:
| 分隔符 | 参数 |
|---|---|
| 逗号 | sep="," |
| 制表符 | sep="\t" |
| 竖线 | `sep=" |
| 分号 | sep=";" |
分隔符错误时,pandas 可能把整行读成一列。
4. 类型推断
pandas 会根据数据内容推断类型,但推断不一定符合业务:
- 订单号可能被推断为整数。
- 日期可能被推断为字符串。
- 混合数字和空字符串可能变成浮点。
- 大数字 ID 可能出现精度风险。
重要字段应通过 dtype 和后续转换显式控制。
5. 分块读取
大文件无法一次性读入内存时,可以使用 chunksize 分块读取。分块处理适合统计、过滤、清洗和导入数据库。
三、工作原理
1. 通用读取流程
text
确认数据源
→ 明确编码、分隔符和表头
→ 指定关键字段类型
→ 读取样本
→ 校验列名、行数和缺失值
→ 正式读取
→ 保存读取日志
不要直接对未知文件执行完整读取。先读取少量行,确认结构正确。
2. 读取后的校验
每次读取后至少检查:
- 行数是否符合预期。
- 列名是否完整。
- 主键是否缺失或重复。
- 关键字段类型是否正确。
- 日期和数值转换是否产生新增缺失值。
读取成功不等于数据可用。
3. 数据库读取边界
从数据库读取数据时,应避免:
SELECT *读取不需要的字段。- 一次性读取全表。
- 在 Notebook 中写死生产数据库密码。
- 缺少时间范围或分页条件。
- 对线上库发起高成本查询。
数据分析查询也需要遵守数据库性能和权限边界。
四、实战示例
1. 读取标准 CSV
python
import pandas as pd
df = pd.read_csv(
"data/orders.csv",
encoding="utf-8-sig",
)
print(df.head())
print(df.info())
如果出现乱码,可以尝试 gbk 或 gb18030,但最终应推动数据导出端统一编码。
2. 指定字段类型
python
df = pd.read_csv(
"data/orders.csv",
dtype={
"order_id": "string",
"customer_id": "string",
"region": "string",
},
encoding="utf-8-sig",
)
ID 类字段优先按字符串读取,避免前导零和超长数字问题。
3. 解析日期
python
df = pd.read_csv(
"data/orders.csv",
encoding="utf-8-sig",
)
df["order_date"] = pd.to_datetime(
df["order_date"],
errors="coerce",
)
bad_dates = df.loc[df["order_date"].isna(), ["order_id", "order_date"]]
print(bad_dates)
先读取再转换,通常比一次性依赖自动解析更容易排查异常。
4. 处理非标准分隔符
python
df = pd.read_csv(
"data/orders.tsv",
sep="\t",
encoding="utf-8",
)
读取后先检查列数。如果只读出一列,优先检查分隔符是否正确。
5. 跳过说明行和指定表头
python
df = pd.read_csv(
"data/report.csv",
skiprows=2,
header=0,
encoding="utf-8-sig",
)
业务系统导出的文件常常包含标题、导出时间和说明行,需要跳过后再读取表格主体。
6. 读取部分列
python
df = pd.read_csv(
"data/orders.csv",
usecols=["order_id", "order_date", "region", "amount"],
encoding="utf-8-sig",
)
只读取需要的列,可以降低内存占用和处理成本。
7. 分块读取大文件
python
import pandas as pd
total_amount = 0.0
total_rows = 0
for chunk in pd.read_csv(
"data/large_orders.csv",
chunksize=100_000,
encoding="utf-8-sig",
):
chunk["amount"] = pd.to_numeric(chunk["amount"], errors="coerce")
total_amount += chunk["amount"].sum(skipna=True)
total_rows += len(chunk)
print("rows:", total_rows)
print("total amount:", total_amount)
分块处理适合累计统计和过滤导出。如果后续逻辑需要全量排序或复杂关联,则需要更合适的存储和计算方案。
8. 读取 Excel
python
df = pd.read_excel(
"data/orders.xlsx",
sheet_name="订单明细",
dtype={
"订单号": "string",
"客户ID": "string",
},
engine="openpyxl",
)
print(df.head())
Excel 适合业务人员维护小规模数据,但不适合作为大规模自动化数据管道的主要格式。
9. 读取多个工作表
python
sheets = pd.read_excel(
"data/monthly_report.xlsx",
sheet_name=None,
engine="openpyxl",
)
for sheet_name, frame in sheets.items():
print(sheet_name, frame.shape)
sheet_name=None 会读取所有工作表并返回字典。需要谨慎处理大型 Excel 文件。
10. 从数据库读取
python
import pandas as pd
from sqlalchemy import create_engine, text
engine = create_engine(
"postgresql+psycopg://user:password@localhost:5432/app"
)
query = text(
"""
select order_id, order_date, region, amount
from orders
where order_date >= :start_date
and order_date < :end_date
"""
)
df = pd.read_sql(
query,
con=engine,
params={
"start_date": "2026-09-01",
"end_date": "2026-10-01",
},
)
真实项目中,数据库连接信息应来自环境变量或配置系统,不要写死在代码里。
11. 读取后统一校验
python
def validate_orders(frame: pd.DataFrame) -> None:
required_columns = {"order_id", "order_date", "region", "amount"}
missing_columns = required_columns - set(frame.columns)
if missing_columns:
raise ValueError(f"missing columns: {missing_columns}")
if frame["order_id"].isna().any():
raise ValueError("order_id contains missing values")
if frame["order_id"].duplicated().any():
raise ValueError("order_id contains duplicated values")
if len(frame) == 0:
raise ValueError("orders is empty")
把校验放在读取之后、清洗之前,可以更早发现数据源异常。
五、常见问题与实践建议
1. 中文乱码怎么办?
先确认源文件编码。可以尝试 utf-8-sig、gbk、gb18030。长期方案是统一导出编码,并把编码约定写入数据接口文档。
2. 为什么订单号前面的 0 没了?
因为字段被当成数字读取了。读取时指定:
python
pd.read_csv("orders.csv", dtype={"order_id": "string"})
ID、手机号、证件号和编码字段通常都应该按字符串处理。
3. Excel 公式读取到的是公式还是结果?
取决于文件保存状态和读取引擎。pandas 读取 Excel 时通常读取单元格缓存结果,但如果文件没有被 Excel 重新计算保存,结果可能不是最新。自动化流程中不建议依赖复杂 Excel 公式。
4. 大文件应该怎么读?
优先考虑:
- 只读取需要的列。
- 明确 dtype 降低内存。
- 使用
chunksize分块。 - 转换为 Parquet。
- 将数据放入数据库或数据仓库。
如果每次都要全量扫描超大 CSV,应重新考虑数据存储方式。
5. 数据库读取是否可以直接 SELECT *?
不建议。只选择需要的字段,增加时间范围和过滤条件,并避免在业务高峰期执行重查询。分析任务也可能影响线上数据库。
六、进阶思考
1. 数据读取层抽象
可以把读取逻辑封装为函数:
python
from pathlib import Path
import pandas as pd
def read_orders_csv(path: str | Path) -> pd.DataFrame:
frame = pd.read_csv(
path,
dtype={"order_id": "string", "customer_id": "string"},
encoding="utf-8-sig",
)
frame["order_date"] = pd.to_datetime(
frame["order_date"],
errors="coerce",
)
frame["amount"] = pd.to_numeric(
frame["amount"],
errors="coerce",
)
validate_orders(frame)
return frame
这样 Notebook、脚本和定时任务都可以复用同一套读取规则。
2. 读取日志
每次读取可以记录:
- 文件路径或数据源。
- 文件大小。
- 读取时间。
- 行数和列数。
- 关键字段缺失数量。
- 数据时间范围。
- 读取程序版本。
当数据结果异常时,这些信息有助于定位是源数据变化还是处理逻辑变化。
3. 从 CSV 迁移到 Parquet
Parquet 支持列式存储、类型信息和压缩,适合较大规模的分析任务。稳定数据管道中,可以把外部 CSV 读取后转换为 Parquet,后续分析直接读取 Parquet。
4. 安全与权限
数据库连接字符串、账号密码和访问 Token 不应写进 Notebook。建议使用环境变量、密钥管理工具或只读分析账号,并限制可访问的数据范围。
结论
数据读取是数据分析流程的入口,入口不稳定,后续清洗和统计都会受影响。CSV 要关注编码、分隔符和类型;Excel 要关注工作表、表头和公式;数据库要关注查询范围、连接管理和权限。
下一篇将进入数据清洗,重点处理缺失值、重复值和异常值,并把读取后的原始数据变成更可靠的分析数据集。
参考资料
- pandas
read_csv文档:https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html - pandas
read_excel文档:https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html - pandas
read_sql文档:https://pandas.pydata.org/docs/reference/api/pandas.read_sql.html - SQLAlchemy 官方文档:https://docs.sqlalchemy.org/