【问题标题】:How to save xarray.DataArray with complex128 data to netcdf如何将带有 complex128 数据的 xarray.DataArray 保存到 netcdf
【发布时间】:2018-04-20 03:40:21
【问题描述】:

我在 xarray 数据集中有一些复杂数据(numpy dtype complex128),我想用 to_netcdf 保存。我收到以下错误:

TypeError: illegal primitive data type, must be one of dict_keys(['S1', 'i1', 'u1', 'i2', 'u2', 'i4', 'u4', 'i8', 'u8', 'f4', 'f8']), got complex128

我明白我正在将数据类型传递给不受支持的底层 netCDF4。我还在 netcdf4 的复合数据类型上找到了https://unidata.github.io/netcdf4-python/。但不幸的是,我不知道如何将其应用于我的问题,因为我没有直接使用 netcdf4 库。

是否可以在保留数据类型的同时将complex128数据类型的数据保存到netcdf(使用xarray.DataArray.to_netcdf)?

MWE:

import numpy as np
import xarray as xr
complex = [np.complex(1.0, 1.0), np.complex(2.0, 1.0), np.complex(3.0, 1.0), np.complex(4.0, 1.0)]
data = xr.DataArray(complex)
data.to_netcdf(r'test.nc')

【问题讨论】:

    标签: python netcdf4 xarray


    【解决方案1】:

    NetCDF 作为一种文件格式不支持复杂数据。显然地球科学用户对保存复杂值的需求并不强烈。

    也就是说,您确实可以使用某种特殊约定(例如使用自定义复合数据类型)将 complex128 数据写入 netCDF 文件。这类似于 h5py 使用的方法。这确实需要在 xarray 本身中实现:欢迎提出拉取请求。

    使用当前版本的 xarray,您有两个用于序列化复数值的选项:

    1. 使用engine='h5netcdf'。这使用 h5py 的约定来编写复杂的数据。不幸的是,这会导致无效的 netCDF 文件 unreadable by netCDF-C。 如果您尝试,您应该会看到一条警告消息,指出这一点。在未来版本的 xarray 中,我们可能需要使用专用方法(例如 to_hdf5() 而不是 to_netcdf())来创建此类无效文件。

    2. 将数据转换为实部和虚部,并将它们保存为单独的变量。从磁盘读回数据时,将它们组合回复杂的值。选择最适合您的临时约定。

    例如,

    def save_complex(data_array, *args, **kwargs):
        ds = xarray.Dataset({'real': data_array.real, 'imag': data_array.imag})
        return ds.to_netcdf(*args, **kwargs)
    
    def read_complex(*args, **kwargs):
        ds = xarray.open_dataset(*args, **kwargs)
        return ds['real'] + ds['imag'] * 1j
    

    【讨论】:

      【解决方案2】:

      除了 shoyer 提供的两个非常好的选项之外,我发现还有另一种在实践中很有用的解决方案:

      向数据集添加另一个长度为 2 的维度,表示数据的实部和虚部。这类似于将实部和虚部存储在单独的变量中,但根据我的经验,在某些情况下使用起来会更好。

      例如,存储一个维度为(x, ReIm)float 变量,其中ReIm 是实部虚数,x 是任意维度,给出的内存布局相当于沿维度@ 的一维数组C 中的 float _Complex 中的 987654326@ 或 C++ 中的 std::complex<float>

      读写是这样的:

      def save_complex(dataset, *args, **kwargs):
          ds = dataset.expand_dims('ReIm', axis=-1) # Add ReIm axis at the end
          ds = xarray.concat([ds.real, ds.imag], dim='ReIm')
          return ds.to_netcdf(*args, **kwargs)
      
      def read_complex(*args, **kwargs):
          ds = xarray.open_dataset(*args, **kwargs)
          return ds.isel(ReIm=0) + 1j * ds.isel(ReIm=1)
      

      如示例所示,这种方法很容易用于数据集(不仅仅是数据数组)。

      【讨论】:

      • 虽然我更喜欢您为实数/复数添加额外维度的方法,而不是像@shoyer 的答案那样添加第二个变量,但您的解决方案似乎需要更多内存 (?)。虽然 shoyer 的解决方案适用于测试数据集 (~2GB),但我的解决方案内存不足。不过谢谢你的想法! :)
      猜你喜欢
      • 2021-01-24
      • 1970-01-01
      • 2014-01-29
      • 1970-01-01
      • 2021-03-29
      • 2017-06-24
      • 2016-08-19
      • 2022-01-07
      • 1970-01-01
      相关资源
      最近更新 更多