2. 科学数据格式分类与核心特征

面对 NetCDF、HDF5、Zarr、Parquet、CSV、JSON、TIFF 等众多格式,初学者最常见的困惑是"到底该用哪一个"。要回答这个问题,不能靠记忆格式清单,而应建立一套可比较的维度框架。本节从存储模型、元数据能力、压缩与分块、并行 I/O 支持四个核心维度出发,把格式的"性格"拆解清楚,再给出可操作的选型判断路径。

维度一:存储模型决定访问模式

存储模型是格式最本质的特征,它决定了数据在磁盘上的组织方式,也直接决定了什么样的查询是"便宜"的、什么是"昂贵"的。可以归纳为四类:

  • 行式存储:同一记录的所有字段连续存放,如 CSV、JSON Lines。适合逐条读取完整记录的场景,但只取某一列时也必须扫描整行。
  • 列式存储:同一字段的所有值连续存放,如 Parquet、Arrow IPC。适合"取少数列、聚合大量行"的分析型负载,列裁剪和向量化计算效率高。
  • 数组式存储:以多维数组为基本单位,按维度索引寻址,如 NetCDF、HDF5、Zarr、TIFF。适合网格化科学数据,如气候场、遥感影像、仿真结果。
  • 层次式存储:以树状组/数据集结构组织,如 HDF5、NetCDF-4、Zarr 的 group 层级。适合把异构数据(多个变量、多个实验组)收纳进单一容器。

需要注意,数组式与层次式并不互斥:NetCDF-4 底层基于 HDF5,既有多维数组变量,也有 group 层次;Zarr 同样支持层级 group。选型时应先问"我的主要访问模式是整行、整列还是多维切片",再问"是否需要把多组数据装进一个文件"。

维度二:元数据能力决定数据能否自解释

科学数据若缺少单位、坐标轴、缺失值标记、仪器参数等描述,就只是"裸数字"。元数据能力可从三个层次评估:

  1. 结构元数据:字段名、数据类型、维度长度、形状。CSV 只有表头,类型需推断;Parquet 在文件尾写入 schema;NetCDF/HDF5 显式记录维度与类型。
  2. 语义元数据:单位、坐标参照、标定系数、缺失值填充值。NetCDF 的 units、_FillValue、scale_factor 属约定属性,配合 CF 规范可实现自描述;Zarr 通过 .zattrs 承载同类信息。
  3. 溯源元数据:处理历史、软件版本、输入文件清单。HDF5/NetCDF 可挂任意属性组,适合记录 provenance;纯文本格式则需外部约定。

实践判断标准很简单:把文件单独发给同事,他能否不看文档就正确解读数值含义? 若不能,说明元数据能力不足或未被使用。

维度三:压缩与分块决定 I/O 效率

压缩降低存储成本,分块(chunking)则决定随机访问的粒度,两者常需联合设计。以 NetCDF-4/HDF5 为例,一个形状为 (time=8760, lat=180, lon=360) 的逐小时气温场,若按整数组连续存储,读取单个时刻的切片需跨越整个文件;若设置 chunk 为 (24, 180, 360),则读取一天的数据只需命中一个块。

# 以 xarray 写出分块 + 压缩的 NetCDF-4 示例(示例参数,非实测)
ds.to_netcdf(
    "temp_2023.nc",
    engine="netcdf4",
    encoding={
        "t2m": {
            "zlib": True,
            "complevel": 4,
            "chunksizes": (24, 180, 360),
            "dtype": "float32",
        }
    },
)

这里 complevel=4 是压缩强度与速度的折中;chunksizes 应尽量匹配典型访问模式——若主要按时间序列取点,则 chunk 应偏向时间维;若主要取空间场,则 chunk 应偏向空间维。Zarr 把每个 chunk 存为独立对象,天然适合云对象存储的并行读取;Parquet 则以 row group + column chunk 实现列级裁剪与谓词下推。

维度四:并行 I/O 支持决定能否扩展

当数据量超过单机内存或单盘带宽,并行能力成为瓶颈。评估要点包括:是否支持多进程/多节点并发写同一文件(如 HDF5 的 MPI-IO 驱动、NetCDF-4 并行模式);是否支持分片并行读(Zarr 的 chunk 天然可并行,Parquet 可按 row group 切分);是否与分布式计算框架(Dask、Spark)有成熟适配。CSV 与单体 JSON 在这方面的能力最弱,通常需要先切分文件再并行处理。

格式选型四维框架与典型格式定位 存储模型 行 / 列 / 数组 / 层次 决定访问模式 整行?整列?切片? 元数据能力 结构 / 语义 / 溯源 单位、坐标、缺失值 能否自解释 压缩与分块 chunk / row group 压缩比与随机读 匹配访问粒度 并行 I/O 支持 MPI-IO / 分片并发 Dask / Spark 适配 能否横向扩展 综合权衡 → 格式决策 典型格式在四维上的相对定位(●强 ○中 ·弱) 格式 存储模型 元数据 压缩/分块 并行 I/O CSV 行式 · · · Parquet 列式 ○ ● ● NetCDF-4 数组 + 层次 ● ● ○ HDF5 数组 + 层次 ● ● ○ Zarr 数组 + 层次 ○ ● ●

把四个维度合成选型判断

实际选型时,四个维度往往互相牵制,需要按优先级排序。一个可操作的判断顺序是:

  1. 先定访问模式:若数据是规则多维网格(时间×空间×变量),优先数组式(NetCDF/Zarr/HDF5);若是表格型记录且以列聚合为主,优先列式(Parquet);若只是小规模交换与人工查看,CSV/JSON 足够。
  2. 再定部署环境:本地单机大文件用 HDF5/NetCDF-4;云对象存储与分布式读取用 Zarr;数据仓库生态用 Parquet。
  3. 然后定元数据要求:需要 CF 规范自描述与单位语义,选 NetCDF;需要灵活挂载任意属性与溯源,选 HDF5;元数据简单则不必上重格式。
  4. 最后调分块与压缩:依据典型切片形状设置 chunk,压缩级别在 3~5 之间起步,用真实读取模式做基准测试后再调优。
一个常见误区是"用最先进的格式解决所有问题"。Zarr 在云上并行读优秀,但若数据只有几十 MB 且需要人工打开检查,CSV 反而更省事。格式选型的本质是让存储结构与访问模式匹配,而不是追求格式本身的"高级"。
分块参数一旦写入文件便难以更改,重新分块意味着整体重写。因此在数据生产阶段就应预估下游访问模式;若访问模式不确定,可先用中等粒度 chunk(如单块 10~50 MB)保留调整余地。