Skip to content

Fix idxmax/idxmin silently promoting integer label coordinate dtype to float64 (GH#7527) - #11544

Open
CAOShurong wants to merge 2 commits into
pydata:mainfrom
CAOShurong:fix-7527-idxminmax-coord-dtype
Open

Fix idxmax/idxmin silently promoting integer label coordinate dtype to float64 (GH#7527)#11544
CAOShurong wants to merge 2 commits into
pydata:mainfrom
CAOShurong:fix-7527-idxminmax-coord-dtype

Conversation

@CAOShurong

Copy link
Copy Markdown

Description

DataArray.idxmax/idxmin (and the Dataset equivalents) silently promoted the dtype of the returned label coordinate to float64 whenever the input data was floating-point — even when the reduced dimension's coordinate was an integer (e.g. int64). This is GH#7527, open since 2023 with no fix.

Root cause: in xarray/computation/computation.py::_calc_idxminmax, the res.where(~allna, fill_value) step ran unconditionally for every floating-point input. Calling .where with the default float fill_value cast the integer label coordinate to float, even for fully-valid reduction slices that never needed filling.

Fix: only call .where when at least one slice is actually all-NaN (allna.any()). Fully-valid slices keep their original coordinate dtype; all-NaN slices still get the float fill_value as before.

Checklist

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested any AI-generated content in my PR.
    • I take responsibility for any AI-generated content in my PR.
      Tools: OpenAI Codex CLI / GPT-5-Codex (assisted reproduction, root-cause trace, and regression test); all changes verified by running the xarray test suite locally (new regression test + existing TestReduce1D idxmin/idxmax suites + docstring doctests all pass).

…o float64 (GH#7527)

Previously, on floating-point data the integer coordinate labels of
idxmax/idxmin results were silently cast to float64 even for fully-valid
reduction slices, because _calc_idxminmax called res.where unconditionally.
Now .where only runs for non-chunked inputs when a slice is actually all-NaN,
so valid slices keep their coordinate dtype. Chunked (dask) inputs keep the
original delayed .where to avoid forcing an extra compute().

Closes pydata#7527
@CAOShurong
CAOShurong force-pushed the fix-7527-idxminmax-coord-dtype branch from 77369bf to 5d9750e Compare August 26, 2026 08:28
@CAOShurong

Copy link
Copy Markdown
Author

Pushed a fixup after the first CI run flagged RuntimeError: Too many computes on the dask-backed TestReduce2D idxmin/idxmax cases.

Root cause of that failure: my initial guard if allna.any(): forced a compute() on dask inputs (to evaluate whether any slice was all-NaN), which blew dask's compute budget. The original code avoided this because res.where(~allna, fill_value) is lazy and never materialised allna.

Fix (amended): for chunked (dask) inputs we keep the original unconditional lazy res.where; the dtype-preserving short-circuit (if allna.any():) now applies only to non-chunked inputs, where it is safe and fixes GH#7527. Verified locally with dask installed: TestReduce1D + TestReduce2D idxmin/idxmax (incl. dask variants) all pass, plus the new regression test and the four idxmin/idxmax docstring examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataArray.idxmax converts coordinates into float64 by default

1 participant