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

  1. Construct with `ordered=True`: `pd.Categorical(values, categories=..., ordered=True)`.
  2. If already built, promote via `cat.as_ordered()`.
  3. For one-off ordering without mutating, use `cat.as_ordered() < 'b'`.
  4. 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

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


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)