{"record":{"id":"75953ba57d27e82a","repo":"pandas-dev/pandas","slug":"cannot-compare-a-categorical-for-op-opname-with","errorCode":null,"errorMessage":"Cannot compare a Categorical for op {opname} with type {type(other)}.\nIf you want to compare values, use 'np.asarray(cat) <op> other'.","messagePattern":"Cannot compare a Categorical for op (.+?) with type (.+?)\\.\nIf you want to compare values, use 'np\\.asarray\\(cat\\) <op> other'\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/categorical.py","lineNumber":188,"sourceCode":"\n                if opname not in {\"__eq__\", \"__ge__\", \"__gt__\"}:\n                    # GH#29820 performance trick; get_loc will always give i>=0,\n                    #  so in the cases (__ne__, __le__, __lt__) the setting\n                    #  here is a no-op, so can be skipped.\n                    mask = self._codes == -1\n                    ret[mask] = fill_value\n                return ret\n            else:\n                return ops.invalid_comparison(self, other, op)\n        else:\n            # allow categorical vs object dtype array comparisons for equality\n            # these are only positional comparisons\n            # (hashable list-likes such as tuple/range take the branch above and\n            #  are already treated as scalar-like, so only non-standard\n            #  positional list-likes like ``deque`` warn here, GH#62423)\n            ops.maybe_warn_listlike(other)\n            if opname not in [\"__eq__\", \"__ne__\"]:\n                raise TypeError(\n                    f\"Cannot compare a Categorical for op {opname} with \"\n                    f\"type {type(other)}.\\nIf you want to compare values, \"\n                    \"use 'np.asarray(cat) <op> other'.\"\n                )\n\n            if isinstance(other, ExtensionArray) and needs_i8_conversion(other.dtype):\n                # We would return NotImplemented here, but that messes up\n                #  ExtensionIndex's wrapped methods\n                return op(other, self)\n            return getattr(np.array(self), opname)(np.array(other))\n\n    func.__name__ = opname\n\n    return func\n\n\ndef contains(cat, key, container) -> bool:\n    \"\"\"","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/categorical.py#L170-L206","documentation":"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.","triggerScenarios":"Using `<`, `>`, `<=`, `>=` between a Categorical and a `deque`, custom non-ndarray sequence, or other exotic list-like that is not hashable and not a Categorical.","commonSituations":"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.","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."],"exampleFix":"# before\nfrom collections import deque\ncat = pd.Categorical([1, 2, 3], ordered=True)\ncat < deque([2, 3, 4])  # TypeError\n\n# after\nimport numpy as np\nnp.asarray(cat) < np.asarray(deque([2, 3, 4]))","handlingStrategy":"validation","validationCode":"from collections.abc import Iterable\nimport numpy as np\n\ndef materialize_for_compare(other):\n    if isinstance(other, (list, tuple, np.ndarray)):\n        return other\n    if isinstance(other, Iterable):\n        return list(other)\n    return other","typeGuard":"def is_plain_list_like(other) -> bool:\n    return isinstance(other, (list, tuple, np.ndarray))","tryCatchPattern":"try:\n    result = cat < other\nexcept TypeError as e:\n    if 'Cannot compare a Categorical' in str(e):\n        import numpy as np\n        result = np.asarray(cat) < np.asarray(other)\n    else:\n        raise","preventionTips":["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."],"tags":["categorical","comparison","list-like","typeerror"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}