pandas-dev/pandas · error · ValueError

abs(axis) must be less than ndim

Error message

abs(axis) must be less than ndim

What it means

Raised by DatetimeLikeArrayMixin.median (the AxisInt validation) when abs(axis) >= self.ndim. DatetimeLikeArrayMixin is 1-D, so any axis other than 0 (or -1 in 1-D, which equals 0) is out of range; the guard runs before nanops.nanmedian is called.

Solutions

  1. Pass axis=0 (or None) for 1-D datetimelike arrays.
  2. Drop the axis argument entirely — median() defaults to the only valid axis.
  3. If operating on a DataFrame, call .median(axis=1) on the DataFrame, not on an extracted 1-D array.

Example fix

// before
dta.median(axis=1)  # ValueError (1-D array)

// after
dta.median(axis=0)  # or simply dta.median()
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd

def median_safe(arr, axis=None):
    if axis is not None and abs(axis) >= arr.ndim:
        raise ValueError(f'abs(axis) must be < ndim={arr.ndim}')
    return arr.median(axis=axis)

Try / catch

try:
    return arr.median(axis=axis)
except ValueError as e:
    if 'abs(axis)' in str(e):
        return arr.median(axis=0)
    raise

Prevention

When it happens

Trigger: Calling DatetimeIndex.median(axis=1) on a 1-D index; TimedeltaArray.median(axis=2); passing an axis argument intended for a DataFrame down to a 1-D array reduction.

Common situations: Generic reduction code that passes axis=1 unconditionally; refactoring a DataFrame reduction into an Index/Array reduction without dropping the axis kwarg.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/1fe9bb3563fe85e0. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/datetimelike.py:1601

        if isinstance(self.dtype, PeriodDtype):
            # See discussion in GH#24757
            raise TypeError(
                f"mean is not implemented for {type(self).__name__} since the "
                "meaning is ambiguous.  An alternative is "
                "obj.to_timestamp(how='start').mean()"
            )

        result = nanops.nanmean(
            self._ndarray, axis=axis, skipna=skipna, mask=self.isna()
        )
        return self._wrap_reduction_result(axis, result)

    @_period_dispatch
    def median(self, *, axis: AxisInt | None = None, skipna: bool = True, **kwargs):
        nv.validate_median((), kwargs)

        if axis is not None and abs(axis) >= self.ndim:
            raise ValueError("abs(axis) must be less than ndim")

        result = nanops.nanmedian(self._ndarray, axis=axis, skipna=skipna)
        return self._wrap_reduction_result(axis, result)

    def _mode(self, dropna: bool = True):
        mask = None
        if dropna:
            mask = self.isna()

        i8modes, _ = algorithms.mode(self.view("i8"), mask=mask)
        npmodes = i8modes.view(self._ndarray.dtype)
        npmodes = cast("np.ndarray", npmodes)
        return self._from_backing_data(npmodes)

    # ------------------------------------------------------------------
    # GroupBy Methods

    def _groupby_op(

View on GitHub (pinned to 3b7651241d)