{"record":{"id":"29051e60f8e19fa5","repo":"pandas-dev/pandas","slug":"unordered-categoricals-can-only-compare-equality-o","errorCode":null,"errorMessage":"Unordered Categoricals can only compare equality or not","messagePattern":"Unordered Categoricals can only compare equality or not","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/categorical.py","lineNumber":141,"sourceCode":"        Index,\n        Series,\n    )\n\n\ndef _cat_compare_op(op):\n    opname = f\"__{op.__name__}__\"\n    fill_value = op is operator.ne\n\n    @unpack_zerodim_and_defer(opname)\n    def func(self, other):\n        hashable = is_hashable(other)\n        if is_list_like(other) and len(other) != len(self) and not hashable:\n            # in hashable case we may have a tuple that is itself a category\n            raise ValueError(\"Lengths must match.\")\n\n        if not self.ordered:\n            if opname in [\"__lt__\", \"__gt__\", \"__le__\", \"__ge__\"]:\n                raise TypeError(\n                    \"Unordered Categoricals can only compare equality or not\"\n                )\n        if isinstance(other, Categorical):\n            # Two Categoricals can only be compared if the categories are\n            # the same (maybe up to ordering, depending on ordered)\n\n            msg = \"Categoricals can only be compared if 'categories' are the same.\"\n            if not self._categories_match_up_to_permutation(other):\n                raise TypeError(msg)\n\n            if not self.ordered and not self.categories.equals(other.categories):\n                # both unordered and different order\n                other_codes = recode_for_categories(\n                    other.codes, other.categories, self.categories, copy=False\n                )\n            else:\n                other_codes = other._codes\n","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/categorical.py#L123-L159","documentation":"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.","triggerScenarios":"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 `>`/`<`.","commonSituations":"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')`.","solutions":["Construct with `ordered=True`: `pd.Categorical(values, categories=..., ordered=True)`.","If already built, promote via `cat.as_ordered()`.","For one-off ordering without mutating, use `cat.as_ordered() < 'b'`.","If equality was the intent, switch the operator to `==` or `!=`."],"exampleFix":"# before\ncat = pd.Categorical(['low', 'high', 'mid'])\nmask = cat > 'mid'  # TypeError\n\n# after\ncat = pd.Categorical(\n    ['low', 'high', 'mid'],\n    categories=['low', 'mid', 'high'],\n    ordered=True,\n)\nmask = cat > 'mid'","handlingStrategy":"validation","validationCode":"def ensure_ordered(cat, categories_order=None):\n    if not cat.ordered:\n        cat = cat.as_ordered() if categories_order is None else cat.set_categories(\n            categories_order, ordered=True\n        )\n    return cat","typeGuard":"def supports_ordering(cat) -> bool:\n    return bool(getattr(cat, 'ordered', False))","tryCatchPattern":"try:\n    mask = cat < threshold\nexcept TypeError as e:\n    if 'Unordered Categoricals' in str(e):\n        cat = cat.as_ordered()\n        mask = cat < threshold\n    else:\n        raise","preventionTips":["Declare `ordered=True` and a meaningful category order at construction time when you ever intend to use `<`/`>`.","Use `cat.as_ordered()` defensively before ordering ops in pipelines that consume externally-built categoricals.","Audit `astype('category')` outputs — they default to unordered."],"tags":["categorical","comparison","ordered","typeerror"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}