4. 数据导入与导出
在金融研究场景中,数据往往来自多个渠道:交易所行情文件、Wind/同花顺导出表、券商柜台 CSV、Python 爬虫抓取的 JSON、Excel 维护的财务底稿。DuckDB 的价值在于,它既能像 SQLite 一样本地零配置运行,又能像分析型数据库一样直接对文件发起 SQL 查询。理解各格式的读写机制,是搭建本地研究流水线的第一步。
核心原理:DuckDB 如何“看见”文件
DuckDB 提供两类文件访问方式。第一类是扫描函数,如 read_csv_auto、read_parquet、read_json_auto,它们把文件当作外部表直接查询,支持谓词下推与列裁剪,不产生中间副本。第二类是导入导出语句,如 COPY ... TO 与 COPY ... FROM,用于把结果落盘或把文件灌入本地表。前者适合探索与即席分析,后者适合固化数据集与交付结果。
需要特别注意的是,DuckDB 对 Parquet 的支持是“原生列式”的:查询 Parquet 时只读取所需列与行组,I/O 成本远低于 CSV。因此,在金融数据量较大时,推荐把 CSV 先转成 Parquet 再反复使用。
方法流程:从文件到可分析表
一个稳健的导入流程通常包含四步:探测结构、声明类型、批量加载、校验结果。下面以 A 股日行情 CSV 为例。
-- 1. 自动探测 CSV 结构
DESCRIBE SELECT * FROM read_csv_auto('data/ashare_daily.csv');
-- 2. 显式声明类型,避免日期被识别为字符串
CREATE TABLE ashare_daily AS
SELECT * FROM read_csv_auto(
'data/ashare_daily.csv',
header = true,
columns = {
'trade_date': 'DATE',
'ts_code': 'VARCHAR',
'open': 'DOUBLE',
'high': 'DOUBLE',
'low': 'DOUBLE',
'close': 'DOUBLE',
'vol': 'BIGINT',
'amount': 'DOUBLE'
}
);
-- 3. 批量加载多个文件(通配符)
CREATE TABLE ashare_all AS
SELECT * FROM read_parquet('data/parquet/*.parquet');
-- 4. 校验行数与日期范围
SELECT COUNT(*) AS rows, MIN(trade_date), MAX(trade_date) FROM ashare_daily;
导出同样简单。若要把研究结果交付给同事,Parquet 是首选;若要给业务方看,CSV 更通用。
-- 导出为 Parquet(推荐,列存压缩)
COPY (SELECT * FROM ashare_daily WHERE trade_date >= '2024-01-01')
TO 'output/ashare_2024.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);
-- 导出为 CSV
COPY (SELECT ts_code, trade_date, close FROM ashare_daily)
TO 'output/close_panel.csv' (HEADER, DELIMITER ',');
spatial 扩展后使用 st_read 读取部分表格格式。对于财务底稿,建议固定“Excel→Parquet”的转换脚本,保证每次研究可复现。
金融数据源对接场景
常见金融数据源与 DuckDB 的对接方式可归纳如下:
| 数据源 | 典型格式 | 推荐接入方式 | 注意事项 |
|---|---|---|---|
| 交易所日行情 | CSV / Parquet | read_csv_auto / read_parquet | 日期与代码类型需显式声明 |
| Wind / 同花顺导出 | Excel / CSV | 先转 Parquet 再查询 | 表头多行、合并单元格需预处理 |
| 券商柜台接口 | JSON / CSV | read_json_auto | 嵌套字段用 -> 提取 |
| Python 爬虫结果 | JSON Lines | read_json_auto(..., format='newline_delimited') | 逐行解析,内存友好 |
| 财务底稿 | Excel | pandas 转换后落 Parquet | 保留原始 sheet 名与版本 |
对于 JSON 嵌套结构,DuckDB 支持用 ->> 提取标量、用 json_extract 提取对象,再配合 UNNEST 展开数组。例如解析某接口返回的行情数组:
SELECT
json_extract_string(item, '$.code') AS code,
json_extract_string(item, '$.date') AS trade_date,
CAST(json_extract(item, '$.close') AS DOUBLE) AS close
FROM (
SELECT UNNEST(json_extract(data, '$.items')) AS item
FROM read_json_auto('data/quotes.json')
) t;
误区与复盘
- 误区一:全部用 read_csv_auto 不声明类型。金融数据中的股票代码常被识别为整数,导致前导零丢失;日期列可能被识别为字符串,后续比较出错。务必用
columns参数显式声明。 - 误区二:反复读取大 CSV。CSV 无压缩、无列裁剪,每次查询都要全量解析。正确做法是首次导入后立即
COPY TO ... (FORMAT PARQUET),后续只查 Parquet。 - 误区三:忽略编码与分隔符。国内数据常为 GBK 编码或使用制表符分隔,需在参数中指定
encoding='gbk'与delim='\t'。 - 误区四:导出时覆盖原始数据。建议输出目录与原始数据目录分离,文件名带日期或版本号,避免不可逆覆盖。
WHERE 过滤分区列,充分利用分区裁剪。
掌握这些读写能力后,你就能把散落在 CSV、Parquet、JSON、Excel 中的金融数据统一纳入 DuckDB,用一条 SQL 完成清洗、聚合与导出,为后续的因子计算与回测打下可复现的数据基础。