pandas-dev/pandas · error · TypeError
Cannot compare a Categorical for op
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 Categorical is compared with a non-hashable, non-Categorical list-like (e.g. `collections.deque`) using an operator other than `==`/`!=`. For such positional list-likes only equality is supported; ordering comparisons have no meaningful semantics and are rejected. The message points users at `np.asarray(cat) <op> other` for raw value comparison.
Solutions
- Convert the list-like to a plain list/ndarray first: `cat < list(deque_val)` — though note this still requires equal length and ordered=True for ordering ops.
- Compare values directly via `np.asarray(cat) < np.asarray(other)` as the message suggests.
- Switch to equality (`==`/`!=`) which is the only op supported for raw list-likes here.
- Wrap the Categorical in a Series for richer alignment semantics.
Example fix
# before from collections import deque cat = pd.Categorical([1, 2, 3], ordered=True) cat < deque([2, 3, 4]) # TypeError # after import numpy as np np.asarray(cat) < np.asarray(deque([2, 3, 4]))
Defensive patterns
Strategy: validation
Validate before calling
from collections.abc import Iterable
import numpy as np
def materialize_for_compare(other):
if isinstance(other, (list, tuple, np.ndarray)):
return other
if isinstance(other, Iterable):
return list(other)
return other Type guard
def is_plain_list_like(other) -> bool:
return isinstance(other, (list, tuple, np.ndarray)) Try / catch
try:
result = cat < other
except TypeError as e:
if 'Cannot compare a Categorical' in str(e):
import numpy as np
result = np.asarray(cat) < np.asarray(other)
else:
raise Prevention
- Convert `deque`/generator/custom sequences to list/ndarray before comparing with a Categorical.
- For value-wise ordering comparison, prefer `np.asarray(cat) <op> np.asarray(other)`.
- Reserve direct Categorical comparison ops for hashable scalars or aligned Categorical operands.
When it happens
Trigger: Using `<`, `>`, `<=`, `>=` between a Categorical and a `deque`, custom non-ndarray sequence, or other exotic list-like that is not hashable and not a Categorical.
Common situations: Migrating code that compared against a list and later swapped the list for a `deque` (GH#62423); passing generator-derived sequences that lost hashability; interop with non-pandas containers.
Related errors
- Categoricals can only be compared if 'categories' are the…
- Unordered Categoricals can only compare equality or not
- Cannot setitem on a Categorical with a new category
- Categorical input must be list-like
- Input must be list-like
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/75953ba57d27e82a.
Report an issue: GitHub.
Appendix: 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 3b7651241d)