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 (__lt__, __gt__, __le__, __ge__) is attempted on a Categorical that was created without `ordered=True`. Unordered categoricals represent nominal data with no defined rank, so pandas forbids greater/less-than semantics; only equality (__eq__/__ne__) is meaningful.

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 71959b8cb9)

Solutions

  1. Recreate the categorical as ordered: `cat = pd.Categorical(cat, categories=[...], ordered=True)` or `cat.cat.as_ordered()`.
  2. If you only need equality semantics, switch to `==`/`!=` or `.isin([...]).`
  3. For a DataFrame column: `df['col'] = df['col'].cat.set_categories(['low','med','high'], ordered=True)`.

Example fix

# before
cat = pd.Categorical(['low', 'high', 'med'])
cat < 'high'
# after
cat = pd.Categorical(['low', 'high', 'med'], categories=['low','med','high'], ordered=True)
cat < 'high'
Defensive patterns

Strategy: validation

Validate before calling

def require_ordered(cat, opname):
    if not getattr(cat, 'ordered', False) and opname in ('__lt__','__gt__','__le__','__ge__'):
        raise TypeError(f"{opname} requires an ordered Categorical")
    return cat

# usage: require_ordered(cat, '__lt__'); cat < x

Type guard

def is_ordered_categorical(x) -> bool:
    import pandas as pd
    return isinstance(getattr(x, 'dtype', None), pd.CategoricalDtype) and x.dtype.ordered

Try / catch

try:
    cat < threshold
except TypeError as e:
    if 'Unordered Categoricals' in str(e):
        cat = cat.cat.as_ordered()
        result = cat < threshold
    else:
        raise

Prevention

When it happens

Trigger: Calling `cat < x`, `cat > x`, `cat <= x`, or `cat >= x` on a `pd.Categorical([...])` or `astype('category')` column that is unordered (the default). Sorting with a custom key that invokes `<` also triggers it.

Common situations: Treating an ordinal label column ('low','med','high') as ordered without setting ordered=True; or assuming string categories compare lexicographically once cast to 'category'.

Related errors


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