NetCDF 文件 - 为什么固定记录尺寸后文件大小为 1/3?

NetCDF file - why is file 1/3 size after fixing record dimension?

我正在努力掌握这个。

我创建了一个具有以下维度和变量的 netcdf4 文件(特别注意无限 point 维度):

dimensions:
    point = UNLIMITED ; // (275935 currently)
    realization = 24 ;
variables:
    short mod_hs(realization, point) ;
        mod_hs:scale_factor = 0.01 ;
    short mod_ws(realization, point) ;
        mod_ws:scale_factor = 0.01 ;
    short obs_hs(point) ;
        obs_hs:scale_factor = 0.01 ;
    short obs_ws(point) ;
        obs_ws:scale_factor = 0.01 ;
    short fchr(point) ;
    float obs_lat(point) ;
    float obs_lon(point) ;
    double obs_datetime(point) ;
}

我有一个 Python 程序在循环中用数据填充此文件(因此记录维度不受限制 - 我不知道文件有多大)。

填充文件后,文件大小为 103MB。

我的问题是从该文件读取数据非常慢。我猜想这与分块和无限 point 维度有关?

我在文件上 运行 ncks --fix_rec_dmn 并且(经过大量搅动后)它生成了一个新的 netCDF 文件,该文件的大小仅为 32MB(这大约是它的数据的正确大小包含)。

这是一个巨大的大小差异 - 为什么原始文件如此臃肿?此外 - 访问此文件中的数据要快几个数量级。例如,在Python中,读取hs变量的内容在原始文件上需要2秒,在固定记录维度文件上需要40毫秒。

我遇到的问题是我的一些文件包含很多点,似乎太大 运行 ncks 在(我的机器 运行s 内存不足我有 8GB),所以我无法将所有数据转换为固定记录维度。

任何人都可以解释为什么文件大小如此不同以及我如何才能使原始文件更小并更有效地阅读?

顺便说一句 - 我没有使用 zlib 压缩(我选择将浮点值缩放为整数短)。

克里斯

编辑 我的 Python 代码实际上是在 3 个月内从多个单独的模型预测文件中构建一个包含并置模型和观测数据的时间序列文件。我的预测模型 运行 每天 4 次,我正在汇总 3 个月的数据,所以大约有 120 个文件。

程序会从每个文件中提取预测周期的子集(e.t.T+24h -> T+48h),所以拼接文件并不是一件简单的事情。

这是我的代码所做的粗略近似(它实际上 reads/writes 更多变量,但为了清楚起见,我在这里只显示 2 个):

# Create output file:
dout = nc.Dataset(fn, mode='w', clobber=True, format="NETCDF4")

dout.createDimension('point', size=None)
dout.createDimension('realization', size=24)

for varname in ['mod_hs','mod_ws']:
    v = ncd.createVariable(varname, np.short, 
            dimensions=('point', 'realization'), zlib=False)
    v.scale_factor = 0.01

# Cycle over dates
date = <some start start>
end_dat = <some end date>

# Keeo track if record dimension ('point') size:
n = 0

while date < end_date: 
    din = nc.Dataset("<path to input file>", mode='r')
    fchr = din.variables['fchr'][:]

    # get mask for specific forecast hour range
    m = np.logical_and(fchr >= 24, fchr < 48)
    sz = np.count_nonzero(m)

    if sz == 0:
        continue

    dout.variables['mod_hs'][n:n+sz,:] = din.variables['mod_hs'][:][m,:]
    dout.variables['mod_ws'][n:n+sz,:] = din.variables['mod_wspd'][:][m,:]

    # Increment record dimension count:
    n += sz

    din.close()

    # Goto next file
    date += dt.timedelta(hours=6)

dout.close()

有趣的是,如果我将输出文件格式设置为 NETCDF3_CLASSIC 而不是 NETCDF4 输出大小是我期望的大小。 NETCDF4 输出似乎膨胀了。

如果可能,请尝试提供无需修改即可运行的代码,我必须进行编辑才能使其运行,但这并不难。

import netCDF4 as nc
import numpy as np
dout = nc.Dataset('testdset.nc4', mode='w', clobber=True, format="NETCDF4")
dout.createDimension('point', size=None)
dout.createDimension('realization', size=24)
for varname in ['mod_hs','mod_ws']:
    v = dout.createVariable(varname, np.short, 
            dimensions=('point', 'realization'), zlib=False,chunksizes=[1000,24])
    v.scale_factor = 0.01
date = 1
end_date = 5000
n = 0
while date < end_date: 
    sz=100
    dout.variables['mod_hs'][n:n+sz,:] = np.ones((sz,24))
    dout.variables['mod_ws'][n:n+sz,:] = np.ones((sz,24))
    n += sz
    date += 1
dout.close()

主要区别在于 createVariable 命令。对于文件大小,在创建变量时不提供 "chunksizes" ,与添加时相比,我也得到了两倍大的文件。所以对于文件大小,它应该可以解决问题。 对于从文件中读取变量,实际上我没有注意到任何差异,也许我应该添加更多变量? 无论如何,现在应该清楚如何添加块大小,您可能需要进行一些测试以获得针对您的问题的良好配置。如果它仍然对您不起作用,请随时询问更多信息,如果您想了解更多关于分块的信息,请阅读 hdf5 docs

我认为您的问题是无限维度的默认 块大小为 1,这会创建大量内部 HDF5 结构。通过显式设置块大小(对于无限维度显然没问题),第二个示例在 space 和时间上表现得更好。

无限维度需要在 HDF5/netCDF4 中分块,所以如果你想要无限维度,你必须考虑分块性能,正如你所发现的那样。

更多信息:

https://www.unidata.ucar.edu/software/netcdf/docs/netcdf_perf_chunking.html

我的经验是,记录维度的默认块大小取决于下面的 netCDF 库的版本。对于 4.3.3.1,它是 524288。275935 条记录大约是记录块的一半。 ncks 自动选择(不告诉您)比 netCDF 默认值更合理的块大小,因此输出得到更好的优化。我认为这就是正在发生的事情。参见 http://nco.sf.net/nco.html#cnk