pandas-dev/pandas · error · NotImplementedError

groupby first/last only supports 1D ExtensionArrays

Error message

groupby first/last only supports 1D ExtensionArrays

What it means

ExtensionArray._groupby_first_last (base.py:3208) only handles 1-D arrays; if self.ndim != 1 it raises NotImplementedError. The optimized first/last algorithm relies on per-group index lookup that is inherently 1-D.

Source

Thrown at pandas/core/arrays/base.py:3208

    def _groupby_first_last(
        self,
        *,
        how: str,
        min_count: int,
        ngroups: int,
        ids: npt.NDArray[np.intp],
        skipna: bool = True,
    ) -> Self:
        """
        Optimized implementation of groupby first/last for ExtensionArrays.

        Uses an index-based approach: computes the index of the first/last
        non-NA element per group, then gathers results via take(). This avoids
        any dtype conversion and works for all EA types.
        """
        if self.ndim != 1:
            raise NotImplementedError(
                "groupby first/last only supports 1D ExtensionArrays"
            )
        isna_mask = np.asarray(self.isna(), dtype=np.uint8)

        result_indices, result_mask = libgroupby.group_first_last_indexer(
            labels=ids,
            mask=isna_mask,
            ngroups=ngroups,
            skipna=skipna,
            is_last=(how == "last"),
        )

        # Apply min_count: require at least min_count non-NA observations.
        # For first/last, the natural minimum is 1 (need at least one value).
        if min_count > 1:
            nobs = np.zeros(ngroups, dtype=np.int64)
            non_na_indices = np.where((~isna_mask.view(bool)) & (ids >= 0))[0]
            np.add.at(nobs, ids[non_na_indices], 1)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Ensure the ExtensionArray is 1-D (ndim == 1) before groupby first/last.
  2. Flatten or split a multi-dimensional EA into 1-D arrays/Series.
  3. Override _groupby_first_last in your subclass if you genuinely need N-D handling.
  4. Fall back to a non-optimized path via apply() if N-D semantics are required.

Example fix

# before: custom EA with ndim == 2 hits first/last dispatch
# raises 'groupby first/last only supports 1D ExtensionArrays'

# after
for col in range(ea.shape[1]):
    ea_1d = ea[:, col]  # split into 1-D
    ...  # then groupby first/last
Defensive patterns

Strategy: validation

Validate before calling

def ensure_1d_ea(arr):
    import numpy as np
    if arr.ndim != 1:
        raise ValueError(f"expected 1D EA, got ndim={arr.ndim}")
    return arr

Type guard

def is_1d(arr) -> bool:
    return getattr(arr, "ndim", 1) == 1

Try / catch

try:
    grouped = grouped_obj.first()
except NotImplementedError as e:
    if "only supports 1D" in str(e):
        # flatten or split N-D EA first
        grouped = [arr[:, i] for i in range(arr.shape[1])]
    else:
        raise

Prevention

When it happens

Trigger: Triggered internally when pandas dispatches groupby first/last to an ExtensionArray whose ndim is not 1 (e.g. a multi-dimensional EA). Mostly an implementer/internal path; ordinary 1-D Series/columns never hit this.

Common situations: A custom ExtensionArray that is genuinely multi-dimensional (ndim>1) being used in a groupby first/last context; experimental stacked-array extensions.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/e71dff4725e9d291. Report an issue: GitHub.