{"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/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/categorical.py#L123-L159","documentation":"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.","triggerScenarios":"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.","commonSituations":"Treating an ordinal label column ('low','med','high') as ordered without setting ordered=True; or assuming string categories compare lexicographically once cast to 'category'.","solutions":["Recreate the categorical as ordered: `cat = pd.Categorical(cat, categories=[...], ordered=True)` or `cat.cat.as_ordered()`.","If you only need equality semantics, switch to `==`/`!=` or `.isin([...]).`","For a DataFrame column: `df['col'] = df['col'].cat.set_categories(['low','med','high'], ordered=True)`."],"exampleFix":"# before\ncat = pd.Categorical(['low', 'high', 'med'])\ncat < 'high'\n# after\ncat = pd.Categorical(['low', 'high', 'med'], categories=['low','med','high'], ordered=True)\ncat < 'high'","handlingStrategy":"validation","validationCode":"def require_ordered(cat, opname):\n    if not getattr(cat, 'ordered', False) and opname in ('__lt__','__gt__','__le__','__ge__'):\n        raise TypeError(f\"{opname} requires an ordered Categorical\")\n    return cat\n\n# usage: require_ordered(cat, '__lt__'); cat < x","typeGuard":"def is_ordered_categorical(x) -> bool:\n    import pandas as pd\n    return isinstance(getattr(x, 'dtype', None), pd.CategoricalDtype) and x.dtype.ordered","tryCatchPattern":"try:\n    cat < threshold\nexcept TypeError as e:\n    if 'Unordered Categoricals' in str(e):\n        cat = cat.cat.as_ordered()\n        result = cat < threshold\n    else:\n        raise","preventionTips":["Declare ordered=True at construction for ordinal label columns.","Centralize a shared CategoricalDtype with ordered=True for repeated use.","Audit comparisons before running; only == / != work on unordered categoricals."],"tags":["categorical","unordered","comparison","ordinal","typeerror"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}