一份 GRIB 与 NetCDF 的体积对比测试报告
由于前面写的一篇关于 GRIB 与 NetCDF 的文章 「GRIB 与 NetCDF 数据格式的特点及性能对比」 在 GRIB2 与 NetCDF4 文件体积问题上有一些错误结论,经朋友指正,因此专门做此文予以更正。
摘要
本文利用工具将一个原始 ERA5 的 GRIB1 格式文件转换为不同压缩算法下的 GRIB2、NetCDF3 以及不同压缩等级下的 NetCDF4 格式文件,同时对他们进行 bz2 压缩,最后对比它们的文件体积大小。最终的结论是,不论是否使用 bz2 压缩,体积最小的都是 GRIB2 家族的文件格式。
数据简介
本次测试使用的原始数据为通过ERA5再分析数据下载的 GRIB 文件,该原始数据为 GRIB1 格式,而为了对比不同格式下的体积,我会用一些工具将该数据进行格式转换。
原始数据下载地址:https://doi.org/10.5281/zenodo.6348679
使用工具及方法
本文使用的命令行工具包括:
- ecCodes
- wgrib2
- netCDF4
- bzip2
其中 ecCodes 和 netCDF4 都可以直接使用 conda 进行安装,而 bzip2 和 wgrib2 需要根据自己的系统查询对应的安装方法在这里不再赘述,本文使用 wgrib2 是通过 docker 启动对应镜像的方式进行的: $ docker pull agilesrc/wgrib2
使用wgrib2设置grib2压缩格式的方法
wgirb2可以使用的压缩方法包括:
- ieee : data is ieee format (4 bytes per data point)
- simple : no compression, packed scaled integers
- complex1 : complex packing
- complex1-bitmap : complex and using bitmap for undefined grid points
- complex2 : complex packing, pack increments (deltas)
- complex2-bitmap : complex packing, pack increments (deltas) and using bitmap for undefined
- complex3 : complex packing, pack increments after linear extrapolation
- complex3-bitmap : complex packing, pack increments after linear extrapolation and using bitmap for undefined
- jpeg : jpeg2000 compression
- aec : aec/CCSDS compression
- same : try to keep same packing type as input, if input is in an unsupported output packing, complex1 is used
示例:
$ wgrib2 in.grb -set_grib_type complex3 -grib_out out.grb
相关文档链接:https://www.cpc.ncep.noaa.gov/products/wesley/wgrib2/set_grib_type.html
使用wgrib2将grib2转化为netcdf格式的方法
示例:
$ wgrib2 /assets/img/a-size-comparison-of-grib-and-netcdf/in.grb2 -netcdf out.nc
相关文档链接:https://www.cpc.ncep.noaa.gov/products/wesley/wgrib2/netcdf.html
使用grib_set对grib1和grib2格式进行切换的方法
grib_set
是 ecCodes 命令行工具包中的一个命令行工具,用于修改 grib 文件的属性,也可以对 grib1 和 grib2 格式进行切换,然而该命令行并不能转换所有的 grib 文件,例如 GFS 的 grib2 文件有时就无法转换。
ecCodes的安装方法:
$ conda install -c conda-forge -y eccodes
示例:
# 将girb1转换为grib2
$ grib_set -s edition=2 in.grib out.grib2
# 将grib2转换为grib1
$ grib_set -s edition=1 in.grib2 out.grib1
使用nccopy转换netcdf文件格式及压缩格式的方法
nccopy
是 netCDF4 命令行工具包里的一个命令行工具,它可以实现nc文件的各种格式转换。
netcdf4 的安装方法:
$ conda install -c conda-forge -y netCDF4
根据 nccopy 的文档,与转换格式和压缩相关的参数包括:
[-k kind] specify kind of netCDF format for output file, default same as input kind strings: ‘classic’, ’64-bit offset’, ‘cdf5’, ‘netCDF-4’, ‘netCDF-4 classic model’
[-d n] set output deflation compression level, default same as input (0=none 9=max)
示例:
# 将netcdf3文件转换为`netCDF-4 classic model`模式的netcdf4格式,并使用1级压缩
$ nccopy -k 'netCDF-4 classic model' -d 1 era5-sample.nc3 era5-sample-c0.nc4
GRIB1 与 GRIB2 的体积对比
GRIB1 格式是不支持压缩的,而 GRIB2 格式支持压缩,因此我们对比 GIRB1 和 GRIB2 格式文件的体积,本质上是对比 GRIB1 与各种压缩方式下 GRIB2 文件的体积。
由于我们的原始文件 era5-sample.grib
是 GRIB1 格式,我们先把它转换为 GRIB2,在终端执行:
$ grib_set -s edition=2 era5-sample.grib era5-sample.grib2
查看新的 GRIB2 文件:
$ grib_ls era5-sample.grib2
era5-sample.grib2
edition centre date dataType gridType stepRange typeOfLevel level shortName packingType
2 ecmf 20211028 fc regular_ll 8 heightAboveGround 10 10u grid_simple
2 ecmf 20211028 fc regular_ll 8 heightAboveGround 10 10v grid_simple
2 ecmf 20211028 fc regular_ll 8 heightAboveGround 2 2d grid_simple
2 ecmf 20211028 fc regular_ll 8 heightAboveGround 2 2t grid_simple
2 ecmf 20211028 fc regular_ll 8 surface 0 fal grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 slhf grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 ssr grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 str grid_simple
2 ecmf 20211028 fc regular_ll 8 surface 0 sp grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 sshf grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 ssrd grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 strd grid_simple
2 ecmf 20211028 fc regular_ll 7-8 surface 0 tp grid_simple
13 of 13 messages in era5-sample.grib2
13 of 13 total messages in 1 files
可以看到 GRIB 的 edition 已经变成2了,下面我们要对转换后的 GRIB2 文件进行压缩,在预装了 wgrib2 并支持 bash 命令的环境下,在数据目录运行以下脚本:
#!/bin/sh
ctypes=( "ieee" "simple" "complex1" "complex2" "complex3" "jpeg" "aec" "same" )
for ctype in "${ctypes[@]}"
do
wgrib2 -set_grib_type $ctype era5-sample.grib2 -grib_out era5-sample-$ctype.grib2
done
从而获得以下文件:
$ ls -lh era5-sample-*.grib2
-rw-r--r-- 1 clarmylee staff 36M 3 12 14:02 era5-sample-aec.grib2
-rw-r--r-- 1 clarmylee staff 34M 3 12 14:01 era5-sample-complex1.grib2
-rw-r--r-- 1 clarmylee staff 27M 3 12 14:01 era5-sample-complex2.grib2
-rw-r--r-- 1 clarmylee staff 27M 3 12 14:02 era5-sample-complex3.grib2
-rw-r--r-- 1 clarmylee staff 322M 3 12 14:01 era5-sample-ieee.grib2
-rw-r--r-- 1 clarmylee staff 36M 3 12 14:02 era5-sample-jpeg.grib2
-rw-r--r-- 1 clarmylee staff 65M 3 12 14:02 era5-sample-same.grib2
-rw-r--r-- 1 clarmylee staff 65M 3 12 14:01 era5-sample-simple.grib2
下面我们用代码统计一下这些文件的大小并绘制图形
可以看出来体积最大的压缩格式为 ieee,体积最小的为 complex3,而原始的 GRIB1 格式的文件是除了 ieee 以外体积最大的,使用 grib_set
直接转换后的 GRIB2 文件比原始文件略小,而 complex3 压缩格式的文件大约是原始文件格式的1/3左右。
以上 GRIB 文件是在不丧失直接读取能力的存储格式,下面我们再来测试一下,将他们压缩为 .bz2 格式以后的体积,在终端执行 $ bzip2 -k *grib*
,可以得到以下文件:
$ ls -lh *.bz2
-rw-r--r-- 1 clarmylee staff 25M 3 12 14:02 era5-sample-aec.grib2.bz2
-rw-r--r-- 1 clarmylee staff 33M 3 12 14:01 era5-sample-complex1.grib2.bz2
-rw-r--r-- 1 clarmylee staff 26M 3 12 14:01 era5-sample-complex2.grib2.bz2
-rw-r--r-- 1 clarmylee staff 26M 3 12 14:02 era5-sample-complex3.grib2.bz2
-rw-r--r-- 1 clarmylee staff 55M 3 12 14:01 era5-sample-ieee.grib2.bz2
-rw-r--r-- 1 clarmylee staff 26M 3 12 14:02 era5-sample-jpeg.grib2.bz2
-rw-r--r-- 1 clarmylee staff 31M 3 12 14:02 era5-sample-same.grib2.bz2
-rw-r--r-- 1 clarmylee staff 31M 3 12 14:01 era5-sample-simple.grib2.bz2
-rw-r--r-- 1 clarmylee staff 52M 3 12 13:58 era5-sample.grib.bz2
-rw-r--r-- 1 clarmylee staff 52M 3 12 13:58 era5-sample.grib2.bz2
可以看出来,经过 bz2 压缩以后文件体积最小的是基于 aec 方法压缩的文件,bz2 压缩效果最明显的是 ieee,而原先体积较小的文件经 bz2 算法压缩后的效果甚微。
综上可以看出,在 GRIB 的生态下,单纯从降低文件体积的角度考虑,在不丧失读取能力的情况下,使用 complex3 压缩算法的 GRIB2 格式进行存储是最优的方案,若可以接受读取能力丧失的情况,那么 aec 压缩算法也可以考虑。
NetCDF3 与 NetCDF4 的体积对比
下面我们来讨论一下 NetCDF3 和 NetCDF4 之间的文件体积对比,跟 GRIB 类似的是,旧版本的 NetCDF3 不支持原生压缩,如果要压缩需要借助类似于 bz2 的工具进行,而新版本的 NetCDF4 支持原生压缩,因此对两种格式的对比实际上就是用 NetCDF3 与 NetCDF4 不同压缩等级之间的对比。
grib_to_netcdf 命令支持将 GRIB 转换为下列四种 NetCDF 存储格式:
- netCDF classic file format
- netCDF 64 bit classic file format (Default)
- netCDF-4 file format
- netCDF-4 classic model file format
以上四种数据格式的底层差异我们在这里不做赘述,仅比较它们的体积,我们先分别将 GRIB 文件转为这4种 nc 格式。
$ grib_to_netcdf -k 1 -o era5-sample-class.nc3 era5-sample.grib
$ grib_to_netcdf -k 2 -o era5-sample-64class.nc3 era5-sample.grib
$ grib_to_netcdf -k 3 -o era5-sample.nc4 era5-sample.grib
$ grib_to_netcdf -k 4 -o era5-sample-class.nc4 era5-sample.grib
$ ls -lh *nc*
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:26 era5-sample-64class.nc3
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:26 era5-sample-class.nc3
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:27 era5-sample-class.nc4
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:26 era5-sample.nc4
可以看到,直接使用 grib_to_netcdf 命令转换的各种 NetCDF 格式的体积没有显著差异,下面我们先使用 nccopy 对其中 nc4 进行原生压缩,执行以下脚本:
#!/bin/sh
clevels=( 0 1 2 3 4 5 6 7 8 9 )
for level in "${clevels[@]}"
do
nccopy -k 'netCDF-4' -d $level era5-sample.nc4 era5-sample-c$level.nc4
done
检查结果:
$ ls -lh era5-sample-*nc*
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:26 era5-sample-64class.nc3
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:44 era5-sample-c0.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c1.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c2.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c3.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c4.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c5.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c6.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c7.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c8.nc4
-rw-r--r-- 1 clarmylee staff 44M 3 12 15:44 era5-sample-c9.nc4
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:26 era5-sample-class.nc3
-rw-r--r-- 1 clarmylee staff 161M 3 12 15:27 era5-sample-class.nc4
可以看到,在无压缩的状态下,nc4 的体积为161M,而使用1-9级压缩后的结果都是44M,也就是说 NetCDF4 格式下,压缩与非压缩的结果差异很大,而不同等级之间的压缩差异很小,并且非压缩的 NetCDF4 与 NetCDF3 的体积一样大。
我们再对其进行 bz2 压缩,执行 $ bzip2 -k era5-sample-*nc*
然后画出图来看一下: 从上图可以看出来,按照非 bz2 算法压缩的形式排序,体积最大的是未经任何压缩的 NetCDF4 格式的文件,而体积最小的是9级压缩的 NetCDF4 格式。 若按照 bz2 压缩之后的文件体积来看的话,NetCDF3 经 bz2 压缩以后会比 NetCDF4 的体积更小。
4种格式体积的交叉对比
下面综合上面所有的压缩或不压缩以及不同压缩级别的格式,汇总在一起来看一下它们的体积对比 上图是按照非 bz2 压缩体积从大到小排序的,可以看出来的是,按照不丧失可读性的原始文件排序,依然是 complex3 压缩算法下的 GRIB2 格式的文件体积最小。 而使用 bz2 压缩后的格式排序,体积最小的依然是 aec 压缩算法下的 GRIB2 格式。
结论
从上述的对比中,我们可以得出结论,不论是否使用 bz2 压缩,体积最小的都是 GRIB2 家族的文件格式,在非 bz2 模式下体积最小的是基于 complex3 压缩算法的 GRIB2 文件,在 bz2 模式下体积最小的是基于 aec 压缩算法的 GRIB2 文件,当然这仅仅是从体积的角度来考虑。若要考察读取速度,则还需要额外的实验和测试。
若要引用本文,请使用以下引用格式:
Wentao Li. (2022). 一份GRIB与NetCDF的体积对比报告 (Version v1). Zenodo. https://doi.org/10.5281/zenodo.6348695