pandas-dev/pandas · error · TypeError

Cannot compare a Categorical for op {opname} with type {type

Error message

Cannot compare a Categorical for op {opname} with type {type(other)}.
If you want to compare values, use 'np.asarray(cat) <op> other'.

What it means

Raised when a non-equality comparison (__lt__/__gt__/__le__/__ge__) is attempted between a Categorical and a non-Categorical, non-hashable list-like operand. Pandas only supports positional ordering comparisons against scalars or other Categoricals; arbitrary list-likes are ambiguous so it directs you to explicitly convert via np.asarray.

Source

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

                if opname not in {"__eq__", "__ge__", "__gt__"}:
                    # GH#29820 performance trick; get_loc will always give i>=0,
                    #  so in the cases (__ne__, __le__, __lt__) the setting
                    #  here is a no-op, so can be skipped.
                    mask = self._codes == -1
                    ret[mask] = fill_value
                return ret
            else:
                return ops.invalid_comparison(self, other, op)
        else:
            # allow categorical vs object dtype array comparisons for equality
            # these are only positional comparisons
            # (hashable list-likes such as tuple/range take the branch above and
            #  are already treated as scalar-like, so only non-standard
            #  positional list-likes like ``deque`` warn here, GH#62423)
            ops.maybe_warn_listlike(other)
            if opname not in ["__eq__", "__ne__"]:
                raise TypeError(
                    f"Cannot compare a Categorical for op {opname} with "
                    f"type {type(other)}.\nIf you want to compare values, "
                    "use 'np.asarray(cat) <op> other'."
                )

            if isinstance(other, ExtensionArray) and needs_i8_conversion(other.dtype):
                # We would return NotImplemented here, but that messes up
                #  ExtensionIndex's wrapped methods
                return op(other, self)
            return getattr(np.array(self), opname)(np.array(other))

    func.__name__ = opname

    return func


def contains(cat, key, container) -> bool:
    """

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Convert to numpy first as the message suggests: `np.asarray(cat) < other`.
  2. If you meant comparing each element to one threshold, pass a scalar: `cat < threshold`.
  3. Reconsider whether the categorical should be ordered and compared against a scalar category instead.

Example fix

# before
cat = pd.Categorical(['a','b','c'], ordered=True)
cat < ['c','a','b']
# after
import numpy as np
cat = pd.Categorical(['a','b','c'], ordered=True)
np.asarray(cat) < ['c','a','b']
Defensive patterns

Strategy: fallback

Validate before calling

import numpy as np

def cat_ordering_compare(cat, other, op):
    import operator
    if not np.isscalar(other):
        return op(np.asarray(cat), np.asarray(other))
    return op(cat, other)

Type guard

def is_scalar_or_hashable(x) -> bool:
    import numpy as np
    from pandas.api.types import is_hashable
    return np.isscalar(x) or is_hashable(x)

Try / catch

try:
    res = cat < other_listlike
except TypeError as e:
    if 'Cannot compare a Categorical' in str(e):
        import numpy as np
        res = np.asarray(cat) < np.asarray(other_listlike)
    else:
        raise

Prevention

When it happens

Trigger: `cat < [1,2,3]` or `cat >= some_series` where the right side is a list-like and the op is not == or !=. The hashable branch (scalar/tuple category) is exempt; only list-likes hit this.

Common situations: Comparing a categorical column against a parallel list/Series expecting element-wise ordering; or feeding a list of thresholds to a categorical mask.

Related errors


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