pandas-dev/pandas · error · TypeError
Unordered Categoricals can only compare equality or not
Error message
Unordered Categoricals can only compare equality or not
What it means
Raised when an ordering comparison (`<`, `>`, `<=`, `>=`) is attempted on a Categorical whose `ordered` attribute is False. Unordered categoricals only have a meaningful equality/inequality semantics — there is no defined rank order between categories, so inequality operators are rejected before any code lookup happens.
Solutions
- Construct with `ordered=True`: `pd.Categorical(values, categories=..., ordered=True)`.
- If already built, promote via `cat.as_ordered()`.
- For one-off ordering without mutating, use `cat.as_ordered() < 'b'`.
- If equality was the intent, switch the operator to `==` or `!=`.
Example fix
# before
cat = pd.Categorical(['low', 'high', 'mid'])
mask = cat > 'mid' # TypeError
# after
cat = pd.Categorical(
['low', 'high', 'mid'],
categories=['low', 'mid', 'high'],
ordered=True,
)
mask = cat > 'mid' Defensive patterns
Strategy: validation
Validate before calling
def ensure_ordered(cat, categories_order=None):
if not cat.ordered:
cat = cat.as_ordered() if categories_order is None else cat.set_categories(
categories_order, ordered=True
)
return cat Type guard
def supports_ordering(cat) -> bool:
return bool(getattr(cat, 'ordered', False)) Try / catch
try:
mask = cat < threshold
except TypeError as e:
if 'Unordered Categoricals' in str(e):
cat = cat.as_ordered()
mask = cat < threshold
else:
raise Prevention
- Declare `ordered=True` and a meaningful category order at construction time when you ever intend to use `<`/`>`.
- Use `cat.as_ordered()` defensively before ordering ops in pipelines that consume externally-built categoricals.
- Audit `astype('category')` outputs — they default to unordered.
When it happens
Trigger: Creating `pd.Categorical(...)` (default `ordered=False`) and then calling `cat < 'b'`, `cat > other`, sorting with a key that invokes `__lt__`, or filtering with a boolean mask built via `>`/`<`.
Common situations: Users assume category listing order implies comparison order; passing ordered categoricals through pipelines that strip the `ordered` flag (e.g., reconstructing via `Categorical.from_codes` without `ordered=True`); default-constructed categoricals from `astype('category')`.
Related errors
- Cannot compare a Categorical for op
- Categoricals can only be compared if 'categories' are the…
- 'values' is not ordered, please explicitly specify the…
- Cannot setitem on a Categorical with a new category
- Categorical input must be list-like
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/29051e60f8e19fa5.
Report an issue: GitHub.
Appendix: source
Thrown at pandas/core/arrays/categorical.py:141
Index,
Series,
)
def _cat_compare_op(op):
opname = f"__{op.__name__}__"
fill_value = op is operator.ne
@unpack_zerodim_and_defer(opname)
def func(self, other):
hashable = is_hashable(other)
if is_list_like(other) and len(other) != len(self) and not hashable:
# in hashable case we may have a tuple that is itself a category
raise ValueError("Lengths must match.")
if not self.ordered:
if opname in ["__lt__", "__gt__", "__le__", "__ge__"]:
raise TypeError(
"Unordered Categoricals can only compare equality or not"
)
if isinstance(other, Categorical):
# Two Categoricals can only be compared if the categories are
# the same (maybe up to ordering, depending on ordered)
msg = "Categoricals can only be compared if 'categories' are the same."
if not self._categories_match_up_to_permutation(other):
raise TypeError(msg)
if not self.ordered and not self.categories.equals(other.categories):
# both unordered and different order
other_codes = recode_for_categories(
other.codes, other.categories, self.categories, copy=False
)
else:
other_codes = other._codes
View on GitHub (pinned to 3b7651241d)