-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
What happened?
I'm nesting uncertainties from the uncertainties package inside of an (2,7)-shaped numpy array. That array is then nested in pint's Quantity by multiplying with an unit. This is then nested in an DataArray, with the information that the first dimension (the axis with length 2) is called a, and the second one is called b.
Calling .mean(dim='b') on the DataArray then fails.
What did you expect to happen?
I did it expect to just compute the mean along the specified dimension, while it takes proper care of the units and of the uncertainties.
Minimal Complete Verifiable Example
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "xarray[complete]@git+https://github.com/pydata/xarray.git@main",
# "uncertainties",
# "pint",
# ]
# ///
#
# This script automatically imports the development branch of xarray to check for issues.
# Please delete this header if you have _not_ tested this script with `uv run`!
%pip install uncertainties
%pip install pint
# create some uncertainties values
from uncertainties import ufloat
l1 = [ufloat(1, 0.1), ufloat(2, 0.2), ufloat(3, 0.3), ufloat(4, 0.4), ufloat(5, 0.5), ufloat(6, 0.6), ufloat(7, 0.7)]
l2 = [ufloat(10, 1.), ufloat(20, 2.), ufloat(30, 3.), ufloat(40, 4.), ufloat(50, 5.), ufloat(60, 6.), ufloat(70, 7.)]
# assemble a numpy array, we're at numpy(uncertainties)
import numpy as np
numpy_array = np.array([l1, l2])
print(numpy_array.mean(axis=1)) # works as expected
# attach a unit to it, we're at pint(numpy(uncertainties))
import pint
ureg = pint.UnitRegistry()
with_unit = numpy_array * ureg.second
print(with_unit.mean(axis=1)) # still works
# now assemble a DataArray, we're at xarray(pint(numpy(uncertainties)))
import xarray as xr
data_array = xr.DataArray(with_unit, dims=("a", "b"))
print(data_array.mean(dim="b")) # this doesn't workSteps to reproduce
You can paste the code into the linked binder notebook and it will fail with the error.
MVCE confirmation
- Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- Complete example — the example is self-contained, including all data and the text of any traceback.
- Verifiable example — the example copy & pastes into an IPython prompt or Binder notebook, returning the result.
- New issue — a search of GitHub Issues suggests this is not a duplicate.
- Recent environment — the issue occurs with the latest version of xarray and its dependencies.
Relevant log output
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[6], line 36
32 import xarray as xr
33 data_array = xr.DataArray(with_unit, dims=("a", "b"))
---> 36 print(data_array.mean(dim="b")) # this doesn't work
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/core/_aggregations.py:2974, in DataArrayAggregations.mean(self, dim, skipna, keep_attrs, **kwargs)
2903 def mean(
2904 self,
2905 dim: Dims = None,
(...) 2909 **kwargs: Any,
2910 ) -> Self:
2911 """
2912 Reduce this DataArray's data by applying ``mean`` along some dimension(s).
2913
(...) 2972 array(nan)
2973 """
-> 2974 return self.reduce(
2975 duck_array_ops.mean,
2976 dim=dim,
2977 skipna=skipna,
2978 keep_attrs=keep_attrs,
2979 **kwargs,
2980 )
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/core/dataarray.py:3909, in DataArray.reduce(self, func, dim, axis, keep_attrs, keepdims, **kwargs)
3865 def reduce(
3866 self,
3867 func: Callable[..., Any],
(...) 3873 **kwargs: Any,
3874 ) -> Self:
3875 """Reduce this array by applying `func` along some dimension(s).
3876
3877 Parameters
(...) 3906 summarized data and the indicated dimension(s) removed.
3907 """
-> 3909 var = self.variable.reduce(func, dim, axis, keep_attrs, keepdims, **kwargs)
3910 return self._replace_maybe_drop_dims(var)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/core/variable.py:1766, in Variable.reduce(self, func, dim, axis, keep_attrs, keepdims, **kwargs)
1759 keep_attrs_ = (
1760 _get_keep_attrs(default=False) if keep_attrs is None else keep_attrs
1761 )
1763 # Note that the call order for Variable.mean is
1764 # Variable.mean -> NamedArray.mean -> Variable.reduce
1765 # -> NamedArray.reduce
-> 1766 result = super().reduce(
1767 func=func, dim=dim, axis=axis, keepdims=keepdims, **kwargs
1768 )
1770 # return Variable always to support IndexVariable
1771 return Variable(
1772 result.dims, result._data, attrs=result._attrs if keep_attrs_ else None
1773 )
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/namedarray/core.py:917, in NamedArray.reduce(self, func, dim, axis, keepdims, **kwargs)
913 if isinstance(axis, tuple) and len(axis) == 1:
914 # unpack axis for the benefit of functions
915 # like np.argmin which can't handle tuple arguments
916 axis = axis[0]
--> 917 data = func(self.data, axis=axis, **kwargs)
918 else:
919 data = func(self.data, **kwargs)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/core/duck_array_ops.py:777, in mean(array, axis, skipna, **kwargs)
775 return _to_pytimedelta(mean_timedeltas, unit="us") + offset
776 else:
--> 777 return _mean(array, axis=axis, skipna=skipna, **kwargs)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/core/duck_array_ops.py:532, in _create_nan_agg_method.<locals>.f(values, axis, skipna, **kwargs)
530 with warnings.catch_warnings():
531 warnings.filterwarnings("ignore", "All-NaN slice encountered")
--> 532 return func(values, axis=axis, **kwargs)
533 except AttributeError:
534 if not is_duck_dask_array(values):
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/computation/nanops.py:122, in nanmean(a, axis, dtype, out)
120 def nanmean(a, axis=None, dtype=None, out=None):
121 if a.dtype.kind == "O":
--> 122 return _nanmean_ddof_object(0, a, axis=axis, dtype=dtype)
124 with warnings.catch_warnings():
125 warnings.filterwarnings(
126 "ignore", r"Mean of empty slice", category=RuntimeWarning
127 )
File /srv/conda/envs/notebook/lib/python3.11/site-packages/xarray/computation/nanops.py:115, in _nanmean_ddof_object(ddof, value, axis, dtype, **kwargs)
112 if dtype is None and value.dtype.kind == "O":
113 dtype = float
--> 115 data = np.sum(value, axis=axis, dtype=dtype, **kwargs)
116 data = data / (valid_count - ddof)
117 return where_method(data, valid_count != 0)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/pint/facets/numpy/quantity.py:75, in NumpyQuantity.__array_function__(self, func, types, args, kwargs)
74 def __array_function__(self, func, types, args, kwargs):
---> 75 return numpy_wrap("function", func, args, kwargs, types)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/pint/facets/numpy/numpy_func.py:1069, in numpy_wrap(func_type, func, args, kwargs, types)
1067 if name not in handled or any(is_upcast_type(t) for t in types):
1068 return NotImplemented
-> 1069 return handled[name](*args, **kwargs)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/pint/facets/numpy/numpy_func.py:322, in implement_func.<locals>.implementation(*args, **kwargs)
317 stripped_args, stripped_kwargs = convert_to_consistent_units(
318 *args, pre_calc_units=pre_calc_units, **kwargs
319 )
321 # Determine result through plain numpy function on stripped arguments
--> 322 result_magnitude = func(*stripped_args, **stripped_kwargs)
324 if output_unit is None:
325 # Short circuit and return magnitude alone
326 return result_magnitude
File /srv/conda/envs/notebook/lib/python3.11/site-packages/numpy/_core/fromnumeric.py:2466, in sum(a, axis, dtype, out, keepdims, initial, where)
2463 return out
2464 return res
-> 2466 return _wrapreduction(
2467 a, np.add, 'sum', axis, dtype, out,
2468 keepdims=keepdims, initial=initial, where=where
2469 )
File /srv/conda/envs/notebook/lib/python3.11/site-packages/numpy/_core/fromnumeric.py:86, in _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs)
83 else:
84 return reduction(axis=axis, out=out, **passkwargs)
---> 86 return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
File /srv/conda/envs/notebook/lib/python3.11/site-packages/uncertainties/ops.py:285, in add_arithmetic_ops.<locals>.raise_error(self)
284 def raise_error(self):
--> 285 raise TypeError(
286 "can't convert an affine function (%s)"
287 " to %s; use x.nominal_value" % (self.__class__, coercion_type)
288 # In case AffineScalarFunc is sub-classed:
289 )
TypeError: can't convert an affine function (<class 'uncertainties.core.AffineScalarFunc'>) to float; use x.nominal_valueAnything else we need to know?
No response
Environment
INSTALLED VERSIONS
commit: None
python: 3.11.13 | packaged by conda-forge | (main, Jun 4 2025, 14:48:23) [GCC 13.3.0]
python-bits: 64
OS: Linux
OS-release: 6.8.0-63-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
LOCALE: ('en_US', 'UTF-8')
libhdf5: 1.14.6
libnetcdf: 4.9.3
xarray: 2025.10.1
pandas: 2.3.3
numpy: 2.3.3
scipy: 1.16.2
netCDF4: 1.7.2
pydap: 3.5.8
h5netcdf: 1.6.4
h5py: 3.14.0
zarr: 3.1.3
cftime: 1.6.4
nc_time_axis: 1.4.1
iris: 3.13.1
bottleneck: 1.6.0
dask: 2025.9.1
distributed: 2025.9.1
matplotlib: 3.10.6
cartopy: 0.25.0
seaborn: 0.13.2
numbagg: 0.9.3
fsspec: 2025.9.0
cupy: None
pint: 0.25
sparse: 0.17.0
flox: None
numpy_groupies: None
setuptools: 80.9.0
pip: 25.2
conda: None
pytest: None
mypy: None
IPython: 9.4.0
sphinx: None