7.4 读取与保存数据
课前导读
本节目标
你将学会:用 pandas 读取和保存本地数据文件,理解 CSV、Parquet、路径、编码、覆盖风险和数据版本。
适合基础:已经知道 DataFrame 是一张可计算的行情表,准备把表格保存下来并重复使用。
预计用时:约 34 分钟。
本节边界:只使用手写示例数据,不调用真实外部数据服务,不新增依赖。
知识正文
1. 把数据文件想成“投资资料柜”
如果 DataFrame 是摊在桌面上的行情表,数据文件就是放进资料柜里的档案。
你今天研究一只 A 股,手动整理了四天行情。
如果不保存,关掉 Python 后这张表就像临时草稿一样消失。
如果保存成文件,下次可以重新打开,继续检查、补充或复盘。
投资研究最怕“凭记忆”。
量化研究更怕“今天的数据和昨天的数据不是同一份,却没有记录”。
所以读取与保存数据,不只是技术动作。
它也是研究纪律。
你要知道文件从哪里来,保存在哪里,是否覆盖旧版本,编码是否正确,字段是否仍然一致。
2. CSV:最适合新手理解的表格文本
CSV 可以理解为“用逗号分隔的表格文本”。
它很朴素,很多表格软件都能打开。
一行是一条记录。
逗号把不同字段分开。
例如下面这几行就像一个迷你行情文件。
日期,股票代码,开盘价,收盘价,成交量
2026-01-05,600000,10.00,10.20,1200000
2026-01-06,600000,10.20,10.10,980000
2026-01-07,600000,10.10,10.45,1500000CSV 的好处是直观。
你可以用记事本看,也可以用表格软件看。
CSV 的不足是它不擅长保存复杂数据类型,也没有强制字段说明。
例如股票代码 600000 如果被误当成数字,有些软件可能会把前导零处理掉。
A 股代码里有 000001 这样的代码,因此股票代码通常要当作文本处理。
常见误区
不要因为 CSV 能用 Excel 打开,就默认 Excel 展示的一定等于原始数据。编码、日期和股票代码格式都可能被自动改动。
3. 用 pandas 保存一份 CSV
下面的代码会用手写数据创建 DataFrame,并保存到本地 data\a_share_prices.csv。
from pathlib import Path
import pandas as pd
data = {
"日期": ["2026-01-05", "2026-01-06", "2026-01-07"],
"股票代码": ["600000", "600000", "600000"],
"开盘价": [10.00, 10.20, 10.10],
"收盘价": [10.20, 10.10, 10.45],
"成交量": [1200000, 980000, 1500000],
}
df = pd.DataFrame(data)
csv_path = Path(r"data\a_share_prices.csv")
csv_path.parent.mkdir(exist_ok=True)
df.to_csv(csv_path, index=False, encoding="utf-8-sig")Path(r"data\a_share_prices.csv") 表示文件路径。
data 是文件夹名。
a_share_prices.csv 是文件名。
mkdir(exist_ok=True) 表示如果 data 文件夹不存在,就创建它;如果已经存在,也不要报错。
index=False 表示不要把 DataFrame 默认行号也写进 CSV。
encoding="utf-8-sig" 有助于某些表格软件正确识别中文列名。
这段代码不需要外部行情接口,因此适合练习。
4. 用 pandas 读取 CSV
保存之后,可以再把它读回来。
loaded = pd.read_csv(
csv_path,
dtype={"股票代码": "string"},
encoding="utf-8-sig",
)
print(loaded)
print(loaded.dtypes)pd.read_csv() 的意思是“从 CSV 文件读取成 DataFrame”。
dtype={"股票代码": "string"} 是告诉 pandas:股票代码不是普通数字,而是文本。
这对 A 股非常重要。
如果把 000001 当成数字,前面的 0 可能不见。
dtypes 可以查看每一列的数据类型。
你可以把它理解为读取后的“开箱验货单”。
5. 路径:数据到底放在哪里
新手常见报错是“文件找不到”。
这通常不是 pandas 不会读,而是路径写错。
路径可以理解为资料柜里的地址。
例如 data\a_share_prices.csv 表示:在当前项目下的 data 文件夹里,找 a_share_prices.csv 这个文件。
如果你在不同目录运行 Python,同一个相对路径可能指向不同位置。
所以学习阶段建议把脚本、数据文件夹和笔记放在同一个项目目录下。
你还可以打印路径,确认自己保存到哪里。
print(csv_path.resolve())resolve() 会显示更完整的本地路径。
看到真实路径后,你就能去文件夹里检查文件是否存在。
6. 编码:为什么中文列名有时会乱码
编码像是文字的“翻译规则”。
同样的中文列名,如果保存和读取时使用的编码不一致,就可能出现乱码。
在中文环境中,你可能遇到两类常见编码。
| 编码 | 常见场景 | 处理建议 |
|---|---|---|
| utf-8-sig | 自己保存的新文件、希望兼容表格软件 | 新手优先使用 |
| gbk | 一些旧系统或历史导出的中文文件 | 读取失败时再尝试 |
如果 utf-8-sig 读取报错,可以尝试。
loaded_gbk = pd.read_csv(
csv_path,
dtype={"股票代码": "string"},
encoding="gbk",
)不要机械地到处改编码。
正确做法是记录文件来源和导出方式,再选择匹配的读取参数。
7. 覆盖风险:保存文件会不会把旧数据冲掉
to_csv() 默认会覆盖同名文件。
这件事很危险。
假设你昨天保存了一份 a_share_prices.csv,今天又用同名路径保存。
如果没有备份,昨天那份文件可能被直接替换。
投资研究中,覆盖旧文件会带来两个问题。
第一,你无法确认研究时用的是哪一版数据。
第二,如果数据供应方后来修正了历史数据,你可能不知道结果为何变化。
更稳妥的命名方式,是把日期或版本写进文件名。
versioned_path = Path(r"data\a_share_prices_2026-01-08_v1.csv")
df.to_csv(versioned_path, index=False, encoding="utf-8-sig")文件名不需要花哨,但要让未来的自己看得懂。
比如“哪一天保存”“第几版”“覆盖范围是什么”。
关键心得
量化研究不是只要算得快,更要能回到当时那份数据,解释当时为什么得到那个结果。
8. Parquet:更像“带结构的压缩档案”
Parquet 可以先理解为更适合程序使用的数据文件格式。
如果 CSV 是朴素文本表,Parquet 更像带字段类型信息的压缩档案。
它通常更省空间,读取大数据时也更高效。
但 Parquet 的学习门槛比 CSV 高一点。
在 pandas 中,保存 Parquet 通常会写成这样。
parquet_path = Path(r"data\a_share_prices.parquet")
# 如果本地没有安装 pyarrow 或 fastparquet,这一行可能会报错。
# 本课程当前只认识接口,依赖处理放到后续环境整理中。
df.to_parquet(parquet_path, index=False)读取时通常写成这样。
# 同样需要本地 Parquet 引擎支持。
loaded_parquet = pd.read_parquet(parquet_path)
print(loaded_parquet.head())本节你不需要为了 Parquet 立刻安装新依赖。
你只要先知道:CSV 适合新手理解和小数据交换,Parquet 更适合未来更大的本地数据管理。
9. 数据版本:同一只股票,为什么会有多份文件
同一只股票可能有不同版本的数据。
例如未复权价格、前复权价格、后复权价格。
例如今天下载的文件和下周下载的文件。
例如只包含日线的数据和同时包含成交额、换手率的数据。
这些差异都会影响研究结果。
如果你不记录版本,后面复盘时会非常痛苦。
最小的数据版本记录可以包括四项。
| 记录项 | 示例 | 为什么重要 |
|---|---|---|
| 保存时间 | 2026-01-08 | 便于回到当时那份数据 |
| 股票范围 | 600000 单只样例 | 避免误以为覆盖全市场 |
| 字段范围 | 日期、代码、开盘价、收盘价、成交量 | 便于复查计算依据 |
| 处理口径 | 手写未复权示例 | 避免和复权数据混用 |
这张记录表可以写在你的研究笔记里。
等你以后管理更多数据,再学习更规范的元数据管理。
10. A 股案例:路径、代码和编码一起出错会怎样
假设你保存了一份招商银行样例数据,文件名叫 600036.csv。
你后来又保存了一份平安银行样例数据,文件名也叫 000001.csv。
如果读取时没有把股票代码设为文本,000001 可能在某些环境里被显示成 1。
如果中文字段编码错误,“收盘价”可能变成乱码。
如果路径写错,你可能读到旧文件,误以为代码没有更新。
这些问题都不是交易策略本身的问题。
但它们会让研究结果偏离事实。
量化开发的专业性,就体现在这些基础环节不马虎。
11. 一个安全保存流程
graph TD
A["准备 DataFrame"] --> B["检查字段和行数"]
B --> C["确认保存路径"]
C --> D["使用带版本的文件名"]
D --> E["保存 CSV"]
E --> F["重新读取文件"]
F --> G["核对行数、字段和股票代码类型"]
G --> H["在笔记中记录数据版本"]这个流程看起来比直接 to_csv() 多了几步。
但它能减少很多低级错误。
在真实研究中,低级数据错误往往比复杂模型错误更常见。
交互实验室
练习 1:保存并读回同一份数据
请运行本节的 to_csv() 和 read_csv() 示例。
完成后检查三件事。
第一,项目目录下是否出现了 data\a_share_prices.csv。
第二,读回来的行数是否还是 3 行。
第三,股票代码的数据类型是否是文本。
如果三项都正确,说明你完成了最小数据闭环。
练习 2:制造一次编码观察
把 encoding="utf-8-sig" 改成不写 encoding,再读一次文件。
如果你的环境仍然正常,不代表编码不重要。
它只说明当前环境刚好能识别。
请在笔记中写下:当文件来自不同软件或旧系统时,我需要检查编码。
练习 3:创建一个版本化文件名
把保存路径改成下面这样。
safe_path = Path(r"data\a_share_prices_2026-01-08_v2.csv")
df.to_csv(safe_path, index=False, encoding="utf-8-sig")保存后对比文件夹里的两个 CSV。
请回答:哪个文件名更容易让未来的自己理解数据来源?
练习 4:写一张数据来源卡
请在笔记里写下五行。
数据名称:单只 A 股手写日线样例。
保存路径:你本地的实际路径。
字段范围:日期、股票代码、开盘价、收盘价、成交量。
处理口径:教学用手写数据,不代表真实行情。
注意事项:股票代码按文本读取,保存时避免覆盖旧版本。
核心复盘
知识清单:
- [ ] CSV 是新手最容易理解的表格文本格式,但要注意编码和股票代码格式。
- [ ]
to_csv()会保存 DataFrame,默认可能覆盖同名文件。 - [ ]
read_csv()会把 CSV 读回 DataFrame,A 股代码建议按文本读取。 - [ ] 路径决定文件保存和读取的位置,路径错会导致“读错文件”。
- [ ] Parquet 更适合后续管理更大数据,但通常需要额外引擎支持。
- [ ] 数据版本记录能帮助你复现当时的研究结论。
金句总结
会保存数据,不等于会管理数据;能说明数据从哪里来、是哪一版,才是量化研究的起点。
风险提醒
覆盖旧文件、股票代码格式错误、编码乱码和数据版本混乱,都可能让研究结果失真。任何保存和读取动作都应留下可复查记录。