{"record":{"id":"9643e0caa308d25d","repo":"pandas-dev/pandas","slug":"categorical-is-not-ordered-for-operation-op-you","errorCode":null,"errorMessage":"Categorical is not ordered for operation {op}\nyou can use .as_ordered() to change the Categorical to an ordered one\n","messagePattern":"Categorical is not ordered for operation (.+?)\nyou can use \\.as_ordered\\(\\) to change the Categorical to an ordered one\n","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/categorical.py","lineNumber":2011,"sourceCode":"            categorical.categories.dtype.\n        \"\"\"\n        # if we are a datetime and period index, return Index to keep metadata\n        if needs_i8_conversion(self.categories.dtype):\n            return self.categories.take(\n                self._codes, allow_fill=True, fill_value=NaT\n            )._values\n        elif is_integer_dtype(self.categories.dtype) and -1 in self._codes:\n            return (\n                self.categories.astype(\"object\")\n                .take(self._codes, allow_fill=True, fill_value=np.nan)\n                ._values\n            )\n        return np.array(self)\n\n    def check_for_ordered(self, op) -> None:\n        \"\"\"assert that we are ordered\"\"\"\n        if not self.ordered:\n            raise TypeError(\n                f\"Categorical is not ordered for operation {op}\\n\"\n                \"you can use .as_ordered() to change the \"\n                \"Categorical to an ordered one\\n\"\n            )\n\n    def argsort(\n        self, *, ascending: bool = True, kind: SortKind = \"quicksort\", **kwargs\n    ) -> npt.NDArray[np.intp]:\n        \"\"\"\n        Return the indices that would sort the Categorical.\n\n        Missing values are sorted at the end.\n\n        Parameters\n        ----------\n        ascending : bool, default True\n            Whether the indices should result in an ascending\n            or descending sort.","sourceCodeStart":1993,"sourceCodeEnd":2029,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/categorical.py#L1993-L2029","documentation":"Raised by Categorical.check_for_ordered when an operation that requires a total ordering (min, max, cummin, cummax) is invoked on a Categorical whose ordered flag is False. Unordered categoricals only encode set membership, not a rank relationship between categories, so pandas refuses to pick a 'smallest' or 'largest' value. The message points the user at .as_ordered() to flip the flag.","triggerScenarios":"Calling .min() or .max() on an unordered Series/DataFrame column of dtype 'category' (categorical.py:2557, 2589). Calling .cummin() or .cummax() on such data (categorical.py:2672). Using np.min/np.max or < / > reductions that route through check_for_ordered. Sorting or ranking paths that internally call min/max on the categorical.","commonSituations":"A column was created with pd.Categorical(values) or astype('category') without specifying ordered=True, then the user tries df['col'].min(). Data loaded from CSV/parquet defaults to unordered even if the source had a logical order. Upgrading pandas surfaces this where older versions silently returned NaN. Comparing categoricals with operators that imply ordering.","solutions":["Construct the categorical as ordered: pd.Categorical(col, categories=[...], ordered=True) or astype(CategoricalDtype([...], ordered=True)).","Call .as_ordered() on the existing data: df['col'] = df['col'].cat.as_ordered() then retry min/max.","Reconsider whether min/max is meaningful; if not, use .mode() or value_counts() instead.","If a natural order exists, set categories in that order and mark ordered: df['col'].cat.set_categories(['low','med','high'], ordered=True)."],"exampleFix":"# before\ns = pd.Series(pd.Categorical(['b','a','c']))\ns.min()  # TypeError: Categorical is not ordered for operation min\n\n# after\ns = pd.Series(pd.Categorical(['b','a','c'], ordered=True))\ns.min()  # 'a'","handlingStrategy":"validation","validationCode":"if isinstance(df['col'].dtype, pd.CategoricalDtype) and not df['col'].cat.ordered:\n    df['col'] = df['col'].cat.as_ordered()\n# now safe to call .min() / .max()","typeGuard":"def is_ordered_categorical(s: pd.Series) -> bool:\n    return isinstance(s.dtype, pd.CategoricalDtype) and s.cat.ordered","tryCatchPattern":"try:\n    result = df['col'].min()\nexcept TypeError as e:\n    if 'not ordered' in str(e):\n        df['col'] = df['col'].cat.as_ordered()\n        result = df['col'].min()\n    else:\n        raise","preventionTips":["Declare ordered=True at construction when the category set has a natural rank.","Centralize categorical dtype definitions in a CategoricalDtype constant and reuse.","Add a unit test asserting ordered flag for columns used in min/max."],"tags":["categorical","ordering","min-max","reduction"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}