{"record":{"id":"3598517defe9c86c","repo":"pandas-dev/pandas","slug":"categoricals-can-only-be-compared-if-categories","errorCode":null,"errorMessage":"Categoricals can only be compared if 'categories' are the same.","messagePattern":"Categoricals can only be compared if 'categories' are the same\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/categorical.py","lineNumber":150,"sourceCode":"    @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\n            ret = op(self._codes, other_codes)\n            mask = (self._codes == -1) | (other_codes == -1)\n            if mask.any():\n                ret[mask] = fill_value\n            return ret\n\n        if hashable:\n            if other in self.categories:\n                i = self._unbox_scalar(other)","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/categorical.py#L132-L168","documentation":"Raised when comparing two Categorical objects whose categories do not match up to a permutation (checked via _categories_match_up_to_permutation). For ordered comparisons the categories must be identical in identity/order; for unordered they must be the same set. Pandas refuses to silently compare codes that reference different category meanings.","triggerScenarios":"Evaluating `cat1 < cat2`, `cat1 == cat2`, etc. where both operands are Categorical but `cat1.categories` and `cat2.categories` differ in members or count (e.g. one has an extra category, or different dtypes).","commonSituations":"Comparing categorical columns from two DataFrames merged from different sources; concatenating categoricals with union_categories then comparing the pieces; dtype drift after astype('category') on different value sets.","solutions":["Unify categories first: `cat2 = cat2.cat.set_categories(cat1.categories)` (and ordered flag) before comparing.","Compare the underlying values instead: `np.asarray(cat1) == np.asarray(cat2)`.","Build both categoricals from a shared CategoricalDtype so categories are guaranteed identical."],"exampleFix":"# before\nc1 = pd.Categorical(['a','b'], categories=['a','b'])\nc2 = pd.Categorical(['a','b'], categories=['a','b','c'])\nc1 == c2\n# after\nc1 = pd.Categorical(['a','b'], categories=['a','b'])\nc2 = pd.Categorical(['a','b'], categories=['a','b','c']).cat.remove_unused_categories()\nc1 == c2","handlingStrategy":"validation","validationCode":"def align_categories(cat1, cat2):\n    if not cat1._categories_match_up_to_permutation(cat2):\n        cat2 = cat2.cat.set_categories(cat1.categories, ordered=cat1.ordered)\n    return cat2\n\n# usage: cat2 = align_categories(cat1, cat2); cat1 == cat2","typeGuard":"def categories_match(cat1, cat2) -> bool:\n    try:\n        return cat1._categories_match_up_to_permutation(cat2)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    res = cat1 < cat2\nexcept TypeError as e:\n    if \"categories\" in str(e):\n        cat2 = cat2.cat.set_categories(cat1.categories)\n        res = cat1 < cat2\n    else:\n        raise","preventionTips":["Build both categoricals from one shared CategoricalDtype instance.","After concat/merge, harmonize categories with union_categoricals or set_categories.","Drop unused categories before comparing to reduce mismatch surface."],"tags":["categorical","comparison","categories-mismatch","typeerror"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}