pandas-dev/pandas · error · TypeError

Categorical is not ordered for operation

Error message

Categorical is not ordered for operation {op}
you can use .as_ordered() to change the Categorical to an ordered one

What it means

Raised by Categorical.check_for_ordered when an operation that requires a total ordering (min, max, cummin, cummax) is invoked on a Categorical whose ordered flag is False. Unordered categoricals only encode set membership, not a rank relationship between categories, so pandas refuses to pick a 'smallest' or 'largest' value. The message points the user at .as_ordered() to flip the flag.

Solutions

  1. Construct the categorical as ordered: pd.Categorical(col, categories=[...], ordered=True) or astype(CategoricalDtype([...], ordered=True)).
  2. Call .as_ordered() on the existing data: df['col'] = df['col'].cat.as_ordered() then retry min/max.
  3. Reconsider whether min/max is meaningful; if not, use .mode() or value_counts() instead.
  4. If a natural order exists, set categories in that order and mark ordered: df['col'].cat.set_categories(['low','med','high'], ordered=True).

Example fix

# before
s = pd.Series(pd.Categorical(['b','a','c']))
s.min()  # TypeError: Categorical is not ordered for operation min

# after
s = pd.Series(pd.Categorical(['b','a','c'], ordered=True))
s.min()  # 'a'
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(df['col'].dtype, pd.CategoricalDtype) and not df['col'].cat.ordered:
    df['col'] = df['col'].cat.as_ordered()
# now safe to call .min() / .max()

Type guard

def is_ordered_categorical(s: pd.Series) -> bool:
    return isinstance(s.dtype, pd.CategoricalDtype) and s.cat.ordered

Try / catch

try:
    result = df['col'].min()
except TypeError as e:
    if 'not ordered' in str(e):
        df['col'] = df['col'].cat.as_ordered()
        result = df['col'].min()
    else:
        raise

Prevention

When it happens

Trigger: Calling .min() or .max() on an unordered Series/DataFrame column of dtype 'category' (categorical.py:2557, 2589). Calling .cummin() or .cummax() on such data (categorical.py:2672). Using np.min/np.max or < / > reductions that route through check_for_ordered. Sorting or ranking paths that internally call min/max on the categorical.

Common situations: A column was created with pd.Categorical(values) or astype('category') without specifying ordered=True, then the user tries df['col'].min(). Data loaded from CSV/parquet defaults to unordered even if the source had a logical order. Upgrading pandas surfaces this where older versions silently returned NaN. Comparing categoricals with operators that imply ordering.

Related errors


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

Appendix: source

Thrown at pandas/core/arrays/categorical.py:2011

            categorical.categories.dtype.
        """
        # if we are a datetime and period index, return Index to keep metadata
        if needs_i8_conversion(self.categories.dtype):
            return self.categories.take(
                self._codes, allow_fill=True, fill_value=NaT
            )._values
        elif is_integer_dtype(self.categories.dtype) and -1 in self._codes:
            return (
                self.categories.astype("object")
                .take(self._codes, allow_fill=True, fill_value=np.nan)
                ._values
            )
        return np.array(self)

    def check_for_ordered(self, op) -> None:
        """assert that we are ordered"""
        if not self.ordered:
            raise TypeError(
                f"Categorical is not ordered for operation {op}\n"
                "you can use .as_ordered() to change the "
                "Categorical to an ordered one\n"
            )

    def argsort(
        self, *, ascending: bool = True, kind: SortKind = "quicksort", **kwargs
    ) -> npt.NDArray[np.intp]:
        """
        Return the indices that would sort the Categorical.

        Missing values are sorted at the end.

        Parameters
        ----------
        ascending : bool, default True
            Whether the indices should result in an ascending
            or descending sort.

View on GitHub (pinned to 3b7651241d)