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
- Construct the categorical as ordered: pd.Categorical(col, categories=[...], ordered=True) or astype(CategoricalDtype([...], ordered=True)).
- Call .as_ordered() on the existing data: df['col'] = df['col'].cat.as_ordered() then retry min/max.
- Reconsider whether min/max is meaningful; if not, use .mode() or value_counts() instead.
- 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
- Declare ordered=True at construction when the category set has a natural rank.
- Centralize categorical dtype definitions in a CategoricalDtype constant and reuse.
- Add a unit test asserting ordered flag for columns used in min/max.
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
- Cannot perform with non-ordered Categorical
- > 1 ndim Categorical are not supported at this time
- abs(axis) must be less than ndim
- Accumulation not supported for
- axis is out of bounds for array of dimension
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)