1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
.. currentmodule:: xarray
.. _complex:
Complex Numbers
===============
.. jupyter-execute::
:hide-code:
import numpy as np
import xarray as xr
.. jupyter-execute::
:hide-code:
# Ensure the file is located in a unique temporary directory
# so that it doesn't conflict with parallel builds of the
# documentation.
import tempfile
import os.path
tempdir = tempfile.TemporaryDirectory()
Xarray leverages NumPy to seamlessly handle complex numbers in :py:class:`~xarray.DataArray` and :py:class:`~xarray.Dataset` objects.
In the examples below, we are using a DataArray named ``da`` with complex elements (of :math:`\mathbb{C}`):
.. jupyter-execute::
data = np.array([[1 + 2j, 3 + 4j], [5 + 6j, 7 + 8j]])
da = xr.DataArray(
data,
dims=["x", "y"],
coords={"x": ["a", "b"], "y": [1, 2]},
name="complex_nums",
)
Operations on Complex Data
--------------------------
You can access real and imaginary components using the ``.real`` and ``.imag`` attributes. Most NumPy universal functions (ufuncs) like :py:doc:`numpy.abs <numpy:reference/generated/numpy.absolute>` or :py:doc:`numpy.angle <numpy:reference/generated/numpy.angle>` work directly.
.. jupyter-execute::
da.real
.. jupyter-execute::
np.abs(da)
.. note::
Like NumPy, ``.real`` and ``.imag`` typically return *views*, not copies, of the original data.
Reading and Writing Complex Data
--------------------------------
Writing complex data to NetCDF files (see :ref:`io.netcdf`) is supported via :py:meth:`~xarray.DataArray.to_netcdf` using specific backend engines that handle complex types:
.. tab:: h5netcdf
This requires the `h5netcdf <https://h5netcdf.org>`_ library to be installed.
.. jupyter-execute::
complex_nums_h5_filename = "complex_nums_h5.nc"
.. jupyter-execute::
:hide-code:
complex_nums_h5_filename = os.path.join(tempdir.name, complex_nums_h5_filename)
.. jupyter-execute::
# write the data to disk
da.to_netcdf(complex_nums_h5_filename, engine="h5netcdf")
# read the file back into memory
ds_h5 = xr.open_dataset(complex_nums_h5_filename, engine="h5netcdf")
# check the dtype
ds_h5[da.name].dtype
.. tab:: netcdf4
Requires the `netcdf4-python (>= 1.7.1) <https://github.com/Unidata/netcdf4-python>`_ library and you have to enable ``auto_complex=True``.
.. jupyter-execute::
complex_nums_nc4_filename = "complex_nums_nc4.nc"
.. jupyter-execute::
:hide-code:
complex_nums_nc4_filename = os.path.join(tempdir.name, complex_nums_nc4_filename)
.. jupyter-execute::
# write the data to disk
da.to_netcdf(complex_nums_nc4_filename, engine="netcdf4", auto_complex=True)
# read the file back into memory
ds_nc4 = xr.open_dataset(
complex_nums_nc4_filename, engine="netcdf4", auto_complex=True
)
# check the dtype
ds_nc4[da.name].dtype
.. warning::
The ``scipy`` engine only supports NetCDF V3 and does *not* support complex arrays; writing with ``engine="scipy"`` raises a ``TypeError``.
Alternative: Manual Handling
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If direct writing is not supported (e.g., targeting NetCDF3), you can manually
split the complex array into separate real and imaginary variables before saving:
.. jupyter-execute::
complex_manual_filename = "complex_manual.nc"
.. jupyter-execute::
:hide-code:
complex_manual_filename = os.path.join(tempdir.name, complex_manual_filename)
.. jupyter-execute::
# Write data to file
ds_manual = xr.Dataset(
{
f"{da.name}_real": da.real,
f"{da.name}_imag": da.imag,
}
)
ds_manual.to_netcdf(complex_manual_filename, engine="scipy") # Example
# Read data from file
ds = xr.open_dataset(complex_manual_filename, engine="scipy")
reconstructed = ds[f"{da.name}_real"] + 1j * ds[f"{da.name}_imag"]
Recommendations
^^^^^^^^^^^^^^^
- Use ``engine="netcdf4"`` with ``auto_complex=True`` for full compliance and ease.
- Use ``h5netcdf`` for HDF5-based storage when interoperability with HDF5 is desired.
- For maximum legacy support (NetCDF3), manually handle real/imaginary components.
.. jupyter-execute::
:hide-code:
# Cleanup
tempdir.cleanup()
See also
--------
- :ref:`io.netcdf` — full NetCDF I/O guide
- `NumPy complex numbers <https://numpy.org/doc/stable/user/basics.types.html#complex>`__
|