Zarr格式存气象数据?读写速度快不快?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

“Zarr格式存气象数据?读写速度快不快?”这个问题,通常出现在气象栅格数据越来越大、NetCDF 文件越来越多、Python 读取越来越慢的时候。对 GIS 用户来说,Zarr 不是一个“更酷的文件后缀”,而是一种更适合分块、并行、云端对象存储和时间序列气象数据分析的数据组织方式。

本文从 GIS 和气象数据处理的实际场景出发,说明 Zarr 格式适不适合存气象数据、读写速度为什么可能更快、什么时候反而不一定快,以及如何用 Python、xarray 和 Dask 做一个可复现的读写流程。

引言:Zarr格式存气象数据适合解决什么问题

气象数据通常有几个典型特点:时间维度长、空间网格规则、变量多、文件体积大。例如温度、降水、风速、气压等变量,往往按小时、日、月持续累积。如果仍然用大量单独的 NetCDF 文件管理,常见问题包括:

  • 批量读取多个 NetCDF 文件时,文件打开和元数据解析耗时明显。
  • 只想读取某个区域或某个时间段,却不得不扫描很多文件。
  • 在 WebGIS、云服务器或对象存储上访问时,随机读取效率较差。
  • 多人协作时,复制和移动大文件成本高。
  • Python 分析中需要频繁重采样、裁剪、按时间聚合,I/O 成为瓶颈。

Zarr 格式的核心优势是把多维数组拆成许多小块,也就是 chunk。读取时可以只访问需要的块,而不是每次都处理整个大文件。对于气象网格数据,这个特性非常重要。

Zarr格式存气象数据与NetCDF气象数据读写速度对比示意图
Zarr 将气象多维数组按块组织,适合按时间、区域和变量进行选择性读取。

背景:气象数据为什么容易遇到读写瓶颈

在 GIS 工作流中,气象数据常见格式包括 NetCDF、GRIB、GeoTIFF、HDF 和 Zarr。它们都能存储栅格或多维数组,但适用场景不同。

以气象再分析数据为例,一个数据集可能包含如下维度:

  • time:时间,例如逐小时、逐日或逐月。
  • lat:纬度。
  • lon:经度。
  • level:高度层或气压层。
  • variable:变量,例如气温、降水、风速、相对湿度。

如果只分析一个月的数据,传统文件方式问题不大。但如果要分析 10 年逐小时降水,或者在全国范围内做逐日气象指标统计,读取性能就会明显影响效率。

很多 GIS 用户遇到的慢,并不是计算公式慢,而是数据读取慢。尤其是以下操作:

  • 循环打开上千个 NetCDF 文件。
  • 每次只取一个小区域,却反复读取完整文件。
  • 将多年的气象数据合并成一个大数组后内存不足。
  • 在网络盘、对象存储或远程服务器上读取气象数据。
  • 用 Python 脚本反复转换、裁剪、聚合。

这正是 Zarr 格式存气象数据受到关注的原因。

原理:Zarr格式读写速度快不快,取决于分块和访问方式

Zarr 格式不是简单地“天然更快”。它快不快,主要取决于三个因素:分块方式、压缩方式和读取模式。

Zarr 的分块存储是什么

Zarr 会把一个大的多维数组拆成多个小块。例如一个气象变量的维度是 time、lat、lon,可以按如下方式分块:

time: 24
lat: 256
lon: 256

这表示每个 chunk 存储 24 个时间步、256 行纬度、256 列经度的数据。读取某一天、某个区域时,只需要访问相关 chunk。

为什么 Zarr 适合气象数据

  • 气象数据天然是多维数组,和 Zarr 的数据模型匹配。
  • 时间序列很长时,Zarr 可以按时间块读取。
  • 区域分析时,可以按空间块读取。
  • 配合 Dask 可以并行计算,不必一次把全部数据读入内存。
  • 适合对象存储,例如 S3、MinIO、云盘类数据湖场景。

Zarr 什么时候可能不快

Zarr 格式存气象数据并不意味着所有操作都会变快。下面几种情况可能并不理想:

  • chunk 设置不合理,例如按时间读取却把 time chunk 设得过小。
  • 文件数量极多且存储在普通机械硬盘上,元数据和小文件访问开销明显。
  • 每次都全量读取整个数据集,Zarr 的优势不明显。
  • 压缩算法选择过重,CPU 解压成为瓶颈。
  • 网络延迟高,对象存储请求次数过多。

所以,“Zarr格式读写速度快不快”的答案应该是:在按时间、空间、变量进行选择性读取时,通常更适合大规模气象数据;但需要合理设置 chunk,不能只改格式不改数据组织方式。

步骤:用 xarray 将 NetCDF 气象数据转换为 Zarr

下面给出一个常见工作流:把一批 NetCDF 气象数据转换为 Zarr,然后按区域和时间读取。适合 Python GIS、气象数据分析和遥感栅格时序处理场景。

1. 安装必要 Python 包

建议使用 conda 环境管理依赖,避免 NetCDF、HDF5 和压缩库版本冲突。

conda create -n zarr_gis python=3.11 -y
conda activate zarr_gis
conda install -c conda-forge xarray dask netcdf4 h5netcdf zarr numcodecs pandas numpy -y

如果还需要处理矢量边界裁剪,可以再安装 geopandas、rioxarray 和 rasterio:

conda install -c conda-forge geopandas rioxarray rasterio -y

2. 读取多个 NetCDF 文件

假设目录中有逐月 NetCDF 文件:

data/
  temp_2020_01.nc
  temp_2020_02.nc
  temp_2020_03.nc

可以使用 xarray 的 open_mfdataset 合并:

import xarray as xr

ds = xr.open_mfdataset(
    "data/temp_2020_*.nc",
    combine="by_coords",
    chunks={"time": 24, "lat": 256, "lon": 256}
)

print(ds)

这里的 chunks 参数很重要。它不是普通参数,而是决定后续 Zarr 存储结构和 Dask 并行任务粒度的关键。

3. 检查维度和坐标名称

不同气象数据集可能使用 latitude、longitude,也可能使用 lat、lon。转换前建议先检查:

print(ds.dims)
print(ds.coords)
print(ds.data_vars)

如果坐标名称不统一,可以先重命名:

ds = ds.rename({
    "latitude": "lat",
    "longitude": "lon"
})

坐标统一后,后续裁剪、切片和 WebGIS 服务发布会更清晰。

4. 写出为 Zarr 格式

将数据写入本地 Zarr 目录:

ds.to_zarr(
    "output/temperature_2020.zarr",
    mode="w",
    consolidated=True
)

参数 consolidated=True 会将元数据合并,通常有利于读取性能,尤其是远程访问 Zarr 数据时。

5. 重新读取 Zarr 数据

zds = xr.open_zarr(
    "output/temperature_2020.zarr",
    consolidated=True
)

print(zds)

如果需要按时间读取:

subset_time = zds.sel(time=slice("2020-06-01", "2020-06-30"))

如果需要按经纬度范围读取:

subset_area = zds.sel(
    lon=slice(110, 120),
    lat=slice(35, 25)
)

注意:有些数据纬度是从北到南递减,所以 lat 的 slice 顺序可能是 slice(35, 25),而不是 slice(25, 35)。这是气象数据处理中非常常见的坑。

6. 对区域和时间做统计

例如计算一个月内区域平均温度:

monthly_mean = zds["t2m"].sel(
    time=slice("2020-06-01", "2020-06-30"),
    lon=slice(110, 120),
    lat=slice(35, 25)
).mean(dim=["time", "lat", "lon"])

result = monthly_mean.compute()
print(result.values)

compute() 会触发实际计算。在此之前,Dask 通常只是构建任务图,并不会立即把所有数据读入内存。

步骤:如何粗略判断 Zarr 读写速度是否值得使用

实际项目中,不建议只听“Zarr 很快”就全量迁移。更稳妥的做法是用自己的数据做一个小测试。

1. 记录 NetCDF 读取耗时

import time
import xarray as xr

t0 = time.time()

ds_nc = xr.open_mfdataset(
    "data/temp_2020_*.nc",
    combine="by_coords"
)

subset_nc = ds_nc["t2m"].sel(
    time=slice("2020-06-01", "2020-06-30"),
    lon=slice(110, 120),
    lat=slice(35, 25)
).mean().compute()

print("NetCDF cost:", time.time() - t0)

2. 记录 Zarr 读取耗时

import time
import xarray as xr

t0 = time.time()

ds_zarr = xr.open_zarr(
    "output/temperature_2020.zarr",
    consolidated=True
)

subset_zarr = ds_zarr["t2m"].sel(
    time=slice("2020-06-01", "2020-06-30"),
    lon=slice(110, 120),
    lat=slice(35, 25)
).mean().compute()

print("Zarr cost:", time.time() - t0)

这个测试不能代表所有场景,但能回答一个关键问题:你的数据、你的硬盘、你的网络环境、你的读取模式下,Zarr 格式读写速度是否真的有改善。

常见坑:Zarr格式存气象数据时最容易出错的地方

1. chunk 设置和分析方式不匹配

如果经常按时间序列读取某一个站点附近区域,可以让 time chunk 稍大一些;如果经常按某一天做全国空间分析,可以让空间 chunk 更适合整幅栅格扫描。

错误示例是完全不考虑使用方式,直接用默认 chunk。这样可能导致读取一个小区域时也触发大量无关 chunk。

2. 纬度方向导致空间切片为空

很多气象数据的纬度从 90 到 -90 递减。如果使用:

ds.sel(lat=slice(25, 35))

可能返回空数据。此时需要改成:

ds.sel(lat=slice(35, 25))

读取前可以先查看:

print(ds.lat.values[:5])
print(ds.lat.values[-5:])

3. 坐标系理解错误

大多数全球气象数据使用经纬度坐标,通常对应 EPSG:4326 或类似地理坐标系统。但并不是所有气象数据都已经带有完整 GIS 坐标参考信息。

如果后续要和行政区矢量叠加,必须确认:

  • 经度范围是 0 到 360,还是 -180 到 180。
  • 纬度方向是递增还是递减。
  • 变量单位是否需要转换,例如 K 转摄氏度。
  • 时间是否为 UTC,是否需要转本地时间。

4. 小文件过多影响对象存储性能

Zarr 会把数据拆成许多 chunk 文件。chunk 太小会产生大量对象或小文件,在对象存储上可能导致请求次数过多,在本地文件系统上也可能带来目录扫描开销。

因此,Zarr 不是 chunk 越小越好,而是要在读取粒度、压缩效率和文件数量之间平衡。

5. 压缩算法选择不当

压缩可以减少存储体积和磁盘读取量,但解压也需要 CPU。对于频繁读取的数据,如果压缩过重,可能出现磁盘不慢、CPU 解压慢的情况。

在气象数据中,可以根据数据类型和精度需求选择压缩策略。不要在没有验证的情况下,为所有变量套用同一套压缩参数。

方法比较:Zarr、NetCDF、GeoTIFF 存气象数据怎么选

格式 适合场景 优势 限制
Zarr 多维气象数据、时间序列分析、云端对象存储、并行计算 分块读取灵活,适合 xarray 和 Dask,适合大规模数组 chunk 设计要求高,小文件或对象数量可能较多
NetCDF 传统气象、海洋、科研数据交换 生态成熟,元数据表达能力强,科研软件支持广 多文件批量读取和云端随机访问可能不够理想
GeoTIFF 单期栅格、制图、GIS 软件加载、遥感影像交换 GIS 软件兼容性强,适合二维栅格 不适合表达复杂多维气象时间序列
COG 云端发布单期栅格、WebGIS 按需读取 HTTP Range 读取友好,适合地图服务和在线浏览 多时间、多变量管理不如 Zarr 自然

如果你的目标是科研交换和长期归档,NetCDF 仍然很常用。如果目标是 Python 中做大规模气象时序分析,Zarr 更值得考虑。如果目标是把某一天的降水结果发布到 GIS 或 WebGIS,GeoTIFF 或 COG 可能更直接。

检查清单:决定是否使用 Zarr 存气象数据前先看这几项

  • 数据是否是多维数组:如果有 time、lat、lon、level 等维度,Zarr 更有意义。
  • 是否经常按子区域读取:如果经常只读部分区域,Zarr 分块优势明显。
  • 是否经常按时间段读取:如果只分析某几天或某几个月,合理 time chunk 很关键。
  • 是否需要并行计算:如果配合 Dask,Zarr 更适合大规模延迟计算。
  • 是否运行在对象存储上:如果数据在 S3、MinIO 或云端,Zarr 比传统大文件更适合按需访问。
  • chunk 是否经过测试:不要盲目使用默认 chunk,应根据分析模式调整。
  • 元数据是否完整:变量单位、坐标、时间、缺失值和属性都要保留。
  • 下游软件是否支持:如果主要用户只使用 ArcGIS Pro 或传统桌面 GIS,需要评估兼容性和转换流程。

FAQ:Zarr格式存气象数据常见问题

Zarr格式存气象数据一定比 NetCDF 快吗?

不一定。Zarr 的优势主要体现在分块读取、并行计算和云端访问。如果你每次都是本地全量读取一个小 NetCDF 文件,Zarr 不一定更快。真正要比较,应使用自己的数据和实际读取模式测试。

Zarr 适合存逐小时气象数据吗?

适合。逐小时气象数据通常时间维度很长,Zarr 可以按 time、lat、lon 分块,配合 xarray 和 Dask 进行按时间段读取、聚合和统计。但需要根据常用分析方式设置 chunk。

Zarr 可以直接在 QGIS 或 ArcGIS Pro 中打开吗?

桌面 GIS 对 Zarr 的直接支持不如 GeoTIFF、NetCDF 普遍。实际工作中,常见做法是用 Python 读取 Zarr 后,把某个时间片或统计结果导出为 GeoTIFF,再放入 QGIS 或 ArcGIS Pro 制图分析。

Zarr 和 COG 哪个更适合 WebGIS?

如果是单期二维栅格在线浏览,COG 更直接。如果是多变量、多时间、多层级气象数据分析,Zarr 更适合后端计算和数据湖管理。很多项目会用 Zarr 做分析存储,用 COG 或瓦片服务做前端发布。

Zarr 的 chunk 应该怎么设置?

没有固定答案。应根据主要访问模式设置。如果常按月分析区域平均值,可以让 time chunk 覆盖合理时间段,空间 chunk 保持中等大小。如果常做单日全国图,可以让空间读取更连续。建议先用小样本测试几组 chunk,再决定正式方案。

把 NetCDF 转成 Zarr 会丢失元数据吗?

通常 xarray 会保留大量维度、坐标和变量属性,但仍建议转换后检查 attrs、coords、变量单位、缺失值编码和时间解释。对于严肃气象分析,元数据检查和数值一致性验证必须做。

结论:Zarr格式存气象数据值得用,但关键是按场景设计

Zarr格式存气象数据的价值,不在于把 NetCDF 换成另一个格式,而在于把气象多维数组变成更适合分块读取、并行计算和云端访问的数据结构。

如果你的工作是长时间序列气象分析、区域统计、Python 批处理、Dask 并行计算或对象存储访问,Zarr 通常值得尝试。读写速度快不快,取决于 chunk、压缩、硬件、网络和实际查询方式。

对 GIS 用户来说,比较稳妥的路线是:原始数据保留 NetCDF 或 GRIB,分析中间层使用 Zarr,最终制图和发布结果输出 GeoTIFF、COG 或 WebGIS 服务。这样既能兼顾气象数据分析效率,也能保证 GIS 工作流的兼容性。