Skip to content

Taking a mean along some dimension with units fails. #10842

@zeichenreihe

Description

@zeichenreihe

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 work

Steps 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_value

Anything 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugneeds triageIssue that has not been reviewed by xarray team member

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions