Skip to content

Commit d3b484f

Browse files
committed
Merge branch 'main' into concat_default_kwargs
2 parents 90bd629 + 6abb769 commit d3b484f

33 files changed

+1041
-185
lines changed

.github/workflows/ci-additional.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ jobs:
123123
python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report
124124
125125
- name: Upload mypy coverage to Codecov
126-
uses: codecov/[email protected].2
126+
uses: codecov/[email protected].3
127127
with:
128128
file: mypy_report/cobertura.xml
129129
flags: mypy
@@ -174,7 +174,7 @@ jobs:
174174
python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report
175175
176176
- name: Upload mypy coverage to Codecov
177-
uses: codecov/[email protected].2
177+
uses: codecov/[email protected].3
178178
with:
179179
file: mypy_report/cobertura.xml
180180
flags: mypy-min
@@ -230,7 +230,7 @@ jobs:
230230
python -m pyright xarray/
231231
232232
- name: Upload pyright coverage to Codecov
233-
uses: codecov/[email protected].2
233+
uses: codecov/[email protected].3
234234
with:
235235
file: pyright_report/cobertura.xml
236236
flags: pyright
@@ -286,7 +286,7 @@ jobs:
286286
python -m pyright xarray/
287287
288288
- name: Upload pyright coverage to Codecov
289-
uses: codecov/[email protected].2
289+
uses: codecov/[email protected].3
290290
with:
291291
file: pyright_report/cobertura.xml
292292
flags: pyright39

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ jobs:
172172
path: pytest.xml
173173

174174
- name: Upload code coverage to Codecov
175-
uses: codecov/[email protected].2
175+
uses: codecov/[email protected].3
176176
env:
177177
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
178178
with:

.github/workflows/upstream-dev-ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ jobs:
140140
run: |
141141
python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report
142142
- name: Upload mypy coverage to Codecov
143-
uses: codecov/[email protected].2
143+
uses: codecov/[email protected].3
144144
with:
145145
file: mypy_report/cobertura.xml
146146
flags: mypy

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Xarray's contributor guidelines [can be found in our online documentation](https://docs.xarray.dev/en/stable/contributing.html)
1+
Xarray's contributor guidelines [can be found in our online documentation](https://docs.xarray.dev/en/stable/contribute/contributing.html)

asv_bench/benchmarks/indexing.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,30 @@
3939
"2d-1scalar": xr.DataArray(randn(100, frac_nan=0.1), dims=["x"]),
4040
}
4141

42-
vectorized_indexes = {
43-
"1-1d": {"x": xr.DataArray(randint(0, nx, 400), dims="a")},
44-
"2-1d": {
45-
"x": xr.DataArray(randint(0, nx, 400), dims="a"),
46-
"y": xr.DataArray(randint(0, ny, 400), dims="a"),
47-
},
48-
"3-2d": {
49-
"x": xr.DataArray(randint(0, nx, 400).reshape(4, 100), dims=["a", "b"]),
50-
"y": xr.DataArray(randint(0, ny, 400).reshape(4, 100), dims=["a", "b"]),
51-
"t": xr.DataArray(randint(0, nt, 400).reshape(4, 100), dims=["a", "b"]),
52-
},
53-
}
42+
43+
def make_vectorized_indexes(n_index):
44+
return {
45+
"1-1d": {"x": xr.DataArray(randint(0, nx, n_index), dims="a")},
46+
"2-1d": {
47+
"x": xr.DataArray(randint(0, nx, n_index), dims="a"),
48+
"y": xr.DataArray(randint(0, ny, n_index), dims="a"),
49+
},
50+
"3-2d": {
51+
"x": xr.DataArray(
52+
randint(0, nx, n_index).reshape(n_index // 100, 100), dims=["a", "b"]
53+
),
54+
"y": xr.DataArray(
55+
randint(0, ny, n_index).reshape(n_index // 100, 100), dims=["a", "b"]
56+
),
57+
"t": xr.DataArray(
58+
randint(0, nt, n_index).reshape(n_index // 100, 100), dims=["a", "b"]
59+
),
60+
},
61+
}
62+
63+
64+
vectorized_indexes = make_vectorized_indexes(400)
65+
big_vectorized_indexes = make_vectorized_indexes(400_000)
5466

5567
vectorized_assignment_values = {
5668
"1-1d": xr.DataArray(randn((400, ny)), dims=["a", "y"], coords={"a": randn(400)}),
@@ -101,6 +113,20 @@ def time_indexing_basic_ds_large(self, key):
101113
self.ds_large.isel(**basic_indexes[key]).load()
102114

103115

116+
class IndexingOnly(Base):
117+
@parameterized(["key"], [list(basic_indexes.keys())])
118+
def time_indexing_basic(self, key):
119+
self.ds.isel(**basic_indexes[key])
120+
121+
@parameterized(["key"], [list(outer_indexes.keys())])
122+
def time_indexing_outer(self, key):
123+
self.ds.isel(**outer_indexes[key])
124+
125+
@parameterized(["key"], [list(big_vectorized_indexes.keys())])
126+
def time_indexing_big_vectorized(self, key):
127+
self.ds.isel(**big_vectorized_indexes[key])
128+
129+
104130
class Assignment(Base):
105131
@parameterized(["key"], [list(basic_indexes.keys())])
106132
def time_assignment_basic(self, key):

doc/user-guide/complex-numbers.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
.. currentmodule:: xarray
2+
3+
.. _complex:
4+
5+
Complex Numbers
6+
===============
7+
8+
Xarray leverages NumPy to seamlessly handle complex numbers in :py:class:`~xarray.DataArray` and :py:class:`~xarray.Dataset` objects.
9+
10+
In the examples below, we are using a DataArray named ``da`` with complex elements (of :math:`\mathbb{C}`):
11+
12+
.. ipython:: python
13+
14+
import xarray as xr
15+
import numpy as np
16+
17+
data = np.array([[1 + 2j, 3 + 4j], [5 + 6j, 7 + 8j]])
18+
da = xr.DataArray(
19+
data,
20+
dims=["x", "y"],
21+
coords={"x": ["a", "b"], "y": [1, 2]},
22+
name="complex_nums",
23+
)
24+
25+
26+
Operations on Complex Data
27+
--------------------------
28+
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.
29+
30+
.. ipython:: python
31+
32+
da.real
33+
np.abs(da)
34+
35+
.. note::
36+
Like NumPy, ``.real`` and ``.imag`` typically return *views*, not copies, of the original data.
37+
38+
39+
Reading and Writing Complex Data
40+
--------------------------------
41+
42+
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:
43+
44+
45+
.. tab:: h5netcdf
46+
47+
This requires the `h5netcdf <https://h5netcdf.org>`_ library to be installed.
48+
49+
.. ipython:: python
50+
:okwarning:
51+
52+
# write the data to disk
53+
da.to_netcdf("complex_nums_h5.nc", engine="h5netcdf")
54+
# read the file back into memory
55+
ds_h5 = xr.open_dataset("complex_nums_h5.nc", engine="h5netcdf")
56+
# check the dtype
57+
ds_h5[da.name].dtype
58+
59+
60+
.. tab:: netcdf4
61+
62+
Requires the `netcdf4-python (>= 1.7.1) <https://github.com/Unidata/netcdf4-python>`_ library and you have to enable ``auto_complex=True``.
63+
64+
.. ipython:: python
65+
:okwarning:
66+
67+
# write the data to disk
68+
da.to_netcdf("complex_nums_nc4.nc", engine="netcdf4", auto_complex=True)
69+
# read the file back into memory
70+
ds_nc4 = xr.open_dataset(
71+
"complex_nums_nc4.nc", engine="netcdf4", auto_complex=True
72+
)
73+
# check the dtype
74+
ds_nc4[da.name].dtype
75+
76+
77+
.. warning::
78+
The ``scipy`` engine only supports NetCDF V3 and does *not* support complex arrays; writing with ``engine="scipy"`` raises a ``TypeError``.
79+
80+
81+
Alternative: Manual Handling
82+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83+
84+
If direct writing is not supported (e.g., targeting NetCDF3), you can manually
85+
split the complex array into separate real and imaginary variables before saving:
86+
87+
.. ipython:: python
88+
89+
# Write data to file
90+
ds_manual = xr.Dataset(
91+
{
92+
f"{da.name}_real": da.real,
93+
f"{da.name}_imag": da.imag,
94+
}
95+
)
96+
ds_manual.to_netcdf("complex_manual.nc", engine="scipy") # Example
97+
98+
# Read data from file
99+
ds = xr.open_dataset("complex_manual.nc", engine="scipy")
100+
reconstructed = ds[f"{da.name}_real"] + 1j * ds[f"{da.name}_imag"]
101+
102+
Recommendations
103+
^^^^^^^^^^^^^^^
104+
105+
- Use ``engine="netcdf4"`` with ``auto_complex=True`` for full compliance and ease.
106+
- Use ``h5netcdf`` for HDF5-based storage when interoperability with HDF5 is desired.
107+
- For maximum legacy support (NetCDF3), manually handle real/imaginary components.
108+
109+
.. ipython:: python
110+
:suppress:
111+
112+
# Cleanup
113+
import os
114+
115+
for f in ["complex_nums_nc4.nc", "complex_nums_h5.nc", "complex_manual.nc"]:
116+
if os.path.exists(f):
117+
os.remove(f)
118+
119+
120+
121+
See also
122+
--------
123+
- :ref:`io.netcdf` — full NetCDF I/O guide
124+
- `NumPy complex numbers <https://numpy.org/doc/stable/user/basics.types.html#complex>`__

doc/user-guide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ examples that describe many common tasks that you can accomplish with Xarray.
3232
:caption: I/O
3333

3434
io
35+
complex-numbers
3536

3637
.. toctree::
3738
:maxdepth: 2

doc/whats-new.rst

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ v2025.05.0 (unreleased)
2424

2525
New Features
2626
~~~~~~~~~~~~
27-
27+
- Allow an Xarray index that uses multiple dimensions checking equality with another
28+
index for only a subset of those dimensions (i.e., ignoring the dimensions
29+
that are excluded from alignment).
30+
(:issue:`10243`, :pull:`10293`)
31+
By `Benoit Bovy <https://github.com/benbovy>`_.
2832

2933
Breaking changes
3034
~~~~~~~~~~~~~~~~
3135

32-
3336
Deprecations
3437
~~~~~~~~~~~~
3538

@@ -47,9 +50,30 @@ Bug fixes
4750
~~~~~~~~~
4851
- Fix :py:class:`~xarray.groupers.BinGrouper` when ``labels`` is not specified (:issue:`10284`).
4952
By `Deepak Cherian <https://github.com/dcherian>`_.
50-
5153
- Allow accessing arbitrary attributes on Pandas ExtensionArrays.
5254
By `Deepak Cherian <https://github.com/dcherian>`_.
55+
- Fix coding empty (zero-size) timedelta64 arrays, ``units`` taking precedence when encoding,
56+
fallback to default values when decoding (:issue:`10310`, :pull:`10313`).
57+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
58+
- Use dtype from intermediate sum instead of source dtype or "int" for casting of count when
59+
calculating mean in rolling for correct operations (preserve float dtypes,
60+
correct mean of bool arrays) (:issue:`10340`, :pull:`10341`).
61+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
62+
- Raise an error when attempting to encode :py:class:`numpy.datetime64` values
63+
prior to the Gregorian calendar reform date of 1582-10-15 with a
64+
``"standard"`` or ``"gregorian"`` calendar. Previously we would warn and
65+
encode these as :py:class:`cftime.DatetimeGregorian` objects, but it is not
66+
clear that this is the user's intent, since this implicitly converts the
67+
calendar of the datetimes from ``"proleptic_gregorian"`` to ``"gregorian"``
68+
and prevents round-tripping them as :py:class:`numpy.datetime64` values
69+
(:pull:`10352`). By `Spencer Clark <https://github.com/spencerkclark>`_.
70+
71+
Performance
72+
~~~~~~~~~~~
73+
- Lazily indexed arrays now use less memory to store keys by avoiding copies
74+
in :py:class:`~xarray.indexing.VectorizedIndexer` and :py:class:`~xarray.indexing.OuterIndexer`
75+
(:issue:`10316`).
76+
By `Jesse Rusak <https://github.com/jder>`_.
5377

5478

5579
Documentation
@@ -71,6 +95,15 @@ Alban Farchi, Andrecho, Benoit Bovy, Deepak Cherian, Dimitri Papadopoulos Orfano
7195

7296
New Features
7397
~~~~~~~~~~~~
98+
- By default xarray now encodes :py:class:`numpy.timedelta64` values by
99+
converting to :py:class:`numpy.int64` values and storing ``"dtype"`` and
100+
``"units"`` attributes consistent with the dtype of the in-memory
101+
:py:class:`numpy.timedelta64` values, e.g. ``"timedelta64[s]"`` and
102+
``"seconds"`` for second-resolution timedeltas. These values will always be
103+
decoded to timedeltas without a warning moving forward. Timedeltas encoded
104+
via the previous approach can still be roundtripped exactly, but in the
105+
future will not be decoded by default (:issue:`1621`, :issue:`10099`,
106+
:pull:`10101`). By `Spencer Clark <https://github.com/spencerkclark>`_.
74107

75108
- Added `scipy-stubs <https://github.com/scipy/scipy-stubs>`_ to the ``xarray[types]`` dependencies.
76109
By `Joren Hammudoglu <https://github.com/jorenham>`_.
@@ -143,6 +176,9 @@ Documentation
143176
- Switch to `pydata-sphinx-theme <https://github.com/pydata/pydata-sphinx-theme>`_ from `sphinx-book-theme <https://github.com/executablebooks/sphinx-book-theme>`_ (:pull:`8708`).
144177
By `Scott Henderson <https://github.com/scottyhq>`_.
145178

179+
- Add a dedicated 'Complex Numbers' sections to the User Guide (:issue:`10213`, :pull:`10235`).
180+
By `Andre Wendlinger <https://github.com/andrewendlinger>`_.
181+
146182
Internal Changes
147183
~~~~~~~~~~~~~~~~
148184
- Avoid stacking when grouping by a chunked array. This can be a large performance improvement.

pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
authors = [{ name = "xarray Developers", email = "[email protected]" }]
33
classifiers = [
44
"Development Status :: 5 - Production/Stable",
5-
"License :: OSI Approved :: Apache Software License",
65
"Operating System :: OS Independent",
76
"Intended Audience :: Science/Research",
87
"Programming Language :: Python",
@@ -15,7 +14,7 @@ classifiers = [
1514
]
1615
description = "N-D labeled arrays and datasets in Python"
1716
dynamic = ["version"]
18-
license = { text = "Apache-2.0" }
17+
license = "Apache-2.0"
1918
name = "xarray"
2019
readme = "README.md"
2120
requires-python = ">=3.10"
@@ -94,8 +93,8 @@ dask = "xarray.namedarray.daskmanager:DaskManager"
9493
build-backend = "setuptools.build_meta"
9594
requires = ["setuptools>=42", "setuptools-scm>=7"]
9695

97-
[tool.setuptools]
98-
packages = ["xarray"]
96+
[tool.setuptools.packages.find]
97+
include = ["xarray*"]
9998

10099
[tool.setuptools_scm]
101100
fallback_version = "9999"

0 commit comments

Comments
 (0)