Как сохранить xarray.DataArray с данными complex128 в netcdf

У меня есть сложные данные (numpy dtype complex128) в наборе данных xarray, которые я хочу сохранить с помощью 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, который не поддерживается. Я также нашел https://unidata.github.io/netcdf4-python/ на составном типы данных с помощью netcdf4. Но, к сожалению, я не вижу, как я могу применить это к своей проблеме, так как я не работаю напрямую с библиотекой 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')

person Anna Krause    schedule 07.11.2017    source источник


Ответы (2)


NetCDF как файловый формат не поддерживает сложные данные. По-видимому, у геолого-геофизических пользователей не было сильной потребности в сохранении комплексных значений.

Тем не менее, вы действительно можете записывать сложные128 данные в файл netCDF, используя какое-то специальное соглашение, например, с помощью пользовательского составного типа данных. Это похоже на подход, используемый h5py. Это действительно должно быть реализовано в самом xarray: запрос на вытягивание будет приветствоваться.

В текущей версии xarray у вас есть два варианта сериализации сложных значений:

  1. Используйте 1_. Это использует соглашение h5py для записи сложных данных. К сожалению, это приводит к созданию недопустимого файла netCDF, который не читается netCDF-C. . Вы должны увидеть предупреждающее сообщение, указывающее на это, если вы попытаетесь это сделать. В будущей версии xarray нам, вероятно, потребуется использовать специальный метод, такой как to_hdf5(), а не to_netcdf(), для создания таких недопустимых файлов.

  2. Преобразуйте данные в действительную и мнимую части и сохраните их как отдельные переменные. Объедините их обратно в комплексные значения при считывании данных с диска. Выберите любую специальную конвенцию, которая кажется вам наиболее подходящей.

e.g.,

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
person shoyer    schedule 08.11.2017

Помимо двух совершенно хороших вариантов, предоставляемых Shoyer, есть еще одно решение, которое я нашел полезным на практике:

Добавьте в набор данных еще одно измерение длины 2, которое представляет действительную и мнимую части данных. Это похоже на хранение реальной и мнимой частей в отдельных переменных, но, по моему опыту, в некоторых случаях может быть удобнее работать.

Например, сохранение, скажем, переменной float с измерениями (x, ReIm), где ReIm — действительно-мнимое, а x — произвольное измерение, дает структуру памяти, эквивалентную массиву 1d по измерению x float _Complex в C или, что то же самое, std::complex<float> в C++.

Чтение и письмо работают следующим образом:

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)

Как показывает пример, этот подход легко реализовать для наборов данных (не только массивов данных).

person arccos    schedule 11.08.2018
comment
хотя я предпочитаю ваш подход к дополнительным измерениям для реального/сложного, чем добавление второй переменной, как в ответе @shoyer, вашему решению, похоже, требуется гораздо больше памяти (?). Хотя решения Shoyer хорошо работают с тестовым набором данных (~ 2 ГБ), у меня закончилась память с вашим решением. Хотя за идею спасибо! :) - person nicrie; 08.11.2020