Skip to content

Commit 4ba0538

Browse files
authored
Merge branch 'main' into rel-2025.1.2
2 parents 35bf232 + 326dbe7 commit 4ba0538

File tree

9 files changed

+63
-14
lines changed

9 files changed

+63
-14
lines changed

doc/api.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This page provides an auto-generated summary of xarray's API. For more details
1010
and examples, refer to the relevant chapters in the main part of the
1111
documentation.
1212

13-
See also: :ref:`public api`
13+
See also: :ref:`public-api` and :ref:`api-stability`.
1414

1515
Top-level functions
1616
===================

doc/getting-started-guide/faq.rst

+13-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,19 @@ would certainly appreciate it. We recommend two citations.
416416
url = {https://doi.org/10.5281/zenodo.59499}
417417
}
418418

419-
.. _public api:
419+
.. _api-stability:
420+
421+
How stable is Xarray's API?
422+
---------------------------
423+
424+
Xarray tries very hard to maintain backwards compatibility in our :ref:`api` between released versions.
425+
Whilst we do occasionally make breaking changes in order to improve the library,
426+
we `signpost changes <https://docs.xarray.dev/en/stable/contributing.html#backwards-compatibility>`_ with ``DeprecationWarnings`` for many releases in advance.
427+
(An exception is bugs - whose behaviour we try to fix as soon as we notice them.)
428+
Our `test-driven development practices <https://docs.xarray.dev/en/stable/contributing.html#test-driven-development-code-writing>`_ helps to ensure any accidental regressions are caught.
429+
This philosophy applies to everything in the `public API <https://docs.xarray.dev/en/stable/getting-started-guide/faq.html#what-parts-of-xarray-are-considered-public-api>`_.
430+
431+
.. _public-api:
420432

421433
What parts of xarray are considered public API?
422434
-----------------------------------------------

doc/whats-new.rst

+6
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,17 @@ Bug fixes
108108
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
109109
- Fix weighted ``polyfit`` for arrays with more than two dimensions (:issue:`9972`, :pull:`9974`).
110110
By `Mattia Almansi <https://github.com/malmans2>`_.
111+
- Preserve order of variables in :py:func:`xarray.combine_by_coords` (:issue:`8828`, :pull:`9070`).
112+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
113+
- Cast ``numpy`` scalars to arrays in :py:meth:`NamedArray.from_arrays` (:issue:`10005`, :pull:`10008`)
114+
By `Justus Magin <https://github.com/keewis>`_.
111115

112116
Documentation
113117
~~~~~~~~~~~~~
114118
- A chapter on :ref:`internals.timecoding` is added to the internal section (:pull:`9618`).
115119
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
120+
- Clarified xarray's policy on API stability in the FAQ. (:issue:`9854`, :pull:`9855`)
121+
By `Tom Nicholas <https://github.com/TomNicholas>`_.
116122

117123
Internal Changes
118124
~~~~~~~~~~~~~~~~

xarray/core/combine.py

+19-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from __future__ import annotations
22

3-
import itertools
4-
from collections import Counter
5-
from collections.abc import Iterable, Iterator, Sequence
3+
from collections import Counter, defaultdict
4+
from collections.abc import Callable, Hashable, Iterable, Iterator, Sequence
65
from typing import TYPE_CHECKING, Literal, TypeVar, Union, cast
76

87
import pandas as pd
@@ -269,10 +268,7 @@ def _combine_all_along_first_dim(
269268
combine_attrs: CombineAttrsOptions = "drop",
270269
):
271270
# Group into lines of datasets which must be combined along dim
272-
# need to sort by _new_tile_id first for groupby to work
273-
# TODO: is the sorted need?
274-
combined_ids = dict(sorted(combined_ids.items(), key=_new_tile_id))
275-
grouped = itertools.groupby(combined_ids.items(), key=_new_tile_id)
271+
grouped = groupby_defaultdict(list(combined_ids.items()), key=_new_tile_id)
276272

277273
# Combine all of these datasets along dim
278274
new_combined_ids = {}
@@ -606,6 +602,21 @@ def vars_as_keys(ds):
606602
return tuple(sorted(ds))
607603

608604

605+
K = TypeVar("K", bound=Hashable)
606+
607+
608+
def groupby_defaultdict(
609+
iter: list[T],
610+
key: Callable[[T], K],
611+
) -> Iterator[tuple[K, Iterator[T]]]:
612+
"""replacement for itertools.groupby"""
613+
idx = defaultdict(list)
614+
for i, obj in enumerate(iter):
615+
idx[key(obj)].append(i)
616+
for k, ix in idx.items():
617+
yield k, (iter[i] for i in ix)
618+
619+
609620
def _combine_single_variable_hypercube(
610621
datasets,
611622
fill_value=dtypes.NA,
@@ -965,8 +976,7 @@ def combine_by_coords(
965976
]
966977

967978
# Group by data vars
968-
sorted_datasets = sorted(data_objects, key=vars_as_keys)
969-
grouped_by_vars = itertools.groupby(sorted_datasets, key=vars_as_keys)
979+
grouped_by_vars = groupby_defaultdict(data_objects, key=vars_as_keys)
970980

971981
# Perform the multidimensional combine on each group of data variables
972982
# before merging back together

xarray/core/dataset.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9141,7 +9141,7 @@ def polyfit(
91419141
lhs = np.vander(x, order)
91429142

91439143
if rcond is None:
9144-
rcond = x.shape[0] * np.finfo(x.dtype).eps # type: ignore[assignment]
9144+
rcond = x.shape[0] * np.finfo(x.dtype).eps
91459145

91469146
# Weights:
91479147
if w is not None:

xarray/core/dtypes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def maybe_promote(dtype: np.dtype) -> tuple[np.dtype, Any]:
6262
# N.B. these casting rules should match pandas
6363
dtype_: np.typing.DTypeLike
6464
fill_value: Any
65-
if HAS_STRING_DTYPE and np.issubdtype(dtype, np.dtypes.StringDType()): # type: ignore[attr-defined]
65+
if HAS_STRING_DTYPE and np.issubdtype(dtype, np.dtypes.StringDType()):
6666
# for now, we always promote string dtypes to object for consistency with existing behavior
6767
# TODO: refactor this once we have a better way to handle numpy vlen-string dtypes
6868
dtype_ = object

xarray/namedarray/core.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def from_array(
205205

206206
return NamedArray(dims, data, attrs)
207207

208-
if isinstance(data, _arrayfunction_or_api):
208+
if isinstance(data, _arrayfunction_or_api) and not isinstance(data, np.generic):
209209
return NamedArray(dims, data, attrs)
210210

211211
if isinstance(data, tuple):

xarray/tests/test_combine.py

+14
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,20 @@ def test_combine_by_coords_incomplete_hypercube(self):
10431043
with pytest.raises(ValueError):
10441044
combine_by_coords([x1, x2, x3], fill_value=None)
10451045

1046+
def test_combine_by_coords_override_order(self) -> None:
1047+
# regression test for https://github.com/pydata/xarray/issues/8828
1048+
x1 = Dataset({"a": (("y", "x"), [[1]])}, coords={"y": [0], "x": [0]})
1049+
x2 = Dataset(
1050+
{"a": (("y", "x"), [[2]]), "b": (("y", "x"), [[1]])},
1051+
coords={"y": [0], "x": [0]},
1052+
)
1053+
actual = combine_by_coords([x1, x2], compat="override")
1054+
assert_equal(actual["a"], actual["b"])
1055+
assert_equal(actual["a"], x1["a"])
1056+
1057+
actual = combine_by_coords([x2, x1], compat="override")
1058+
assert_equal(actual["a"], x2["a"])
1059+
10461060

10471061
class TestCombineMixedObjectsbyCoords:
10481062
def test_combine_by_coords_mixed_unnamed_dataarrays(self):

xarray/tests/test_namedarray.py

+7
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,13 @@ def test_warn_on_repeated_dimension_names(self) -> None:
591591
with pytest.warns(UserWarning, match="Duplicate dimension names"):
592592
NamedArray(("x", "x"), np.arange(4).reshape(2, 2))
593593

594+
def test_aggregation(self) -> None:
595+
x: NamedArray[Any, np.dtype[np.int64]]
596+
x = NamedArray(("x", "y"), np.arange(4).reshape(2, 2))
597+
598+
result = x.sum()
599+
assert isinstance(result.data, np.ndarray)
600+
594601

595602
def test_repr() -> None:
596603
x: NamedArray[Any, np.dtype[np.uint64]]

0 commit comments

Comments
 (0)