【问题标题】:Compression of arrays in netcdf file压缩 netcdf 文件中的数组
【发布时间】:2019-12-02 10:25:57
【问题描述】:

我希望能够通过操纵比例因子来压缩存储在 netcdf 文件中的数组,并通过转换数组数据类型(即 float32 到 int16)添加要应用于数组的偏移量

我想将通常太大而无法在 python 中使用的栅格数据制作成更小更易于管理的栅格。我知道可以针对 netcdf 数据应用比例因子和偏移量,不仅可以使文件大小更小,而且在加载数组以便于内存管理时,同样的逻辑是否适用。我已经有另一种方法可以使用不同的 numpy 来管理大型数组,但我想用 netcdfs 来实现这一点。

我已经有了以下基于多个链接http://james.hiebert.name/blog/work/2015/04/18/NetCDF-Scale-Factors.html的代码

我正在使用的测试文件是我为自己生成的一个 netcdf 文件,其中包含一个 float32 numpy 数组,并通过 gdal translate 从 geotiff 文件转换为 netcdf

import netCDF4
from math import floor
import numpy as np


def compute_scale_and_offset(min, max, n):
    # stretch/compress data to the available packed range
    scale_factor = (max - min) / (2 ** n - 1)
    # translate the range to be symmetric about zero
    add_offset = min + 2 ** (n - 1) * scale_factor
    return scale_factor, add_offset

def pack_value(unpacked_value, scale_factor, add_offset):
    return unpacked_value - add_offset / scale_factor

def unpack_value(packed_value, scale_factor, add_offset):
    return packed_value * scale_factor + add_offset

netcdf_path = r"path/to/netcdf"

nc = netCDF4.Dataset(netcdf_path,"a")
data = nc.variables['Band1'][:]

scale_factor,offset = compute_scale_and_offset(np.min(data),np.max(data),16)
data = pack_value(data,scale_factor,offset)
data_b = data.astype(np.int16,copy=False)
nc.variables['Band1'][:] = data_b

nc.close()

现在,当我运行上述代码时,我正在使用的文件的大小没有改变,但核心数据数组的输出值确实发生了变化。我的预期结果将是对上述代码的更改,该代码将使用任何通用 netcdf 文件来转换数据数组并允许应用偏移量并将其存储在文件中,以便在从 netcdf4 读取时加载它们。

【问题讨论】:

  • 不是你的问题的答案,但我认为应该是return (unpacked_value - add_offset) / scale_factor...
  • 另外,nc.variables['Band1'][:] = data_b 只是将data_b 转换为Band1 的任何数据类型。我不认为 N​​etCDF4 允许您更改 NetCDF 数据类型,并且删除变量(并使用另一种数据类型重新创建它们)也很困难/不可能,因此您可能必须创建一个具有正确数据类型的全新 NetCDF 文件,对于这样一个简单的任务,这听起来很麻烦:-(

标签: python arrays memory raster netcdf4


【解决方案1】:

Xarray 可以使用 to_netcdf 处理这个问题

import xarray as xr


def compute_scale_and_offset(da, n=16):
    """Calculate offset and scale factor for int conversion

    Based on Krios101's code above.
    """

    vmin = np.min(da).item()
    vmax = np.max(da).item()

    # stretch/compress data to the available packed range
    scale_factor = (vmax - vmin) / (2 ** n - 1)

    # translate the range to be symmetric about zero
    add_offset = vmin + 2 ** (n - 1) * scale_factor

    return scale_factor, add_offset


ds = xr.open_dataset("infile.nc")

scale_factor, add_offset = compute_scale_and_offset(ds['my_var'])

ds.to_netcdf(outfile, encoding={"my_var": {
    "dtype": 'int16',
    "scale_factor": scale_factor,
    "add_offset": add_offset,
    "_FillValue": -32767,
}})

【讨论】:

  • compute_scale_and_offset(da['my_var']) 应该是 compute_scale_and_offset(ds['my_var'])
  • 感谢@Florian,已修复。
  • 因为你需要读取整个数据数组来计算最小值/最大值,如果你的数据集很大,使用 Dask 很有用。 Dask 不喜欢.item()。如果您将 vmin = np.min(da).item()vmax=np.max(da.item() 更改为 vmin = float(da.min().values)vmax = float(da.max().values),Dask 确实有效
猜你喜欢
  • 2018-07-23
  • 2021-12-28
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 2013-08-06
  • 2011-11-18
  • 2010-09-05
  • 2019-02-24
相关资源
最近更新 更多