You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
quantile fails on an empty dimension where other reductions return NaN #11549
quantile on a dimension of length zero fails three different ways, while the other reductions return nan.
empty.mean() / .median() / .std() -> nan
empty.quantile(0.5) -> AxisError: source: axis 0 is out of bounds for array of dimension 0
empty.quantile(0.5, skipna=False) -> IndexError: index -1 is out of bounds for axis 0 with size 0
DataArray(zeros((0,3))).quantile(0.5, dim="y") -> ValueError: applied function returned data with an unexpected number of dimensions
That last one reduces over y, which has length 3. Only x is empty.
median and quantile(0.5) are the same operation and disagree, and pandas returns nan:
pd.Series([], dtype=float).quantile(0.5) # nan
Where it comes from
np.nanquantile drops the leading q axis when the reduced axis is empty:
Variable.quantile._wrapper does moveaxis(_quantile_func(npa, **kwargs), 0, -1), which assumes that axis is there, so apply_ufunc then rejects the result. The skipna=False path goes to np.quantile, which raises on empty input by itself.
What did you expect to happen?
nan, matching mean, median, std and pandas, with the same result for both skipna values.
Minimal Complete Verifiable Example
importnumpyasnpimportxarrayasxrempty=xr.DataArray(np.array([], dtype=float), dims="x", coords={"x": []})
print(empty.median().item()) # nanprint(empty.quantile(0.5)) # AxisError# reached without building an empty array by handd=xr.DataArray([1.0, 2.0], dims="x", coords={"x": [0, 1]})
d.where(d>99, drop=True).quantile(0.5)
Happy to take this. The guard belongs in _wrapper: reshape when _quantile_func drops the q axis on an empty reduction, so the three failure modes collapse into the nan that median already returns. Would you want skipna=False matched to that too, or left as numpy's raise?
Activity
chiruu12 commentedon Aug 30, 2026
Happy to take this. The guard belongs in
_wrapper: reshape when_quantile_funcdrops the q axis on an empty reduction, so the three failure modes collapse into thenanthatmedianalready returns. Would you wantskipna=Falsematched to that too, or left as numpy's raise?dcherian commentedon Aug 31, 2026
yep seems right. We'll take a PR for it