diff --git a/doc/source/whatsnew/v0.19.0.txt b/doc/source/whatsnew/v0.19.0.txt index e60ac7f3773f0..418532ca817c6 100644 --- a/doc/source/whatsnew/v0.19.0.txt +++ b/doc/source/whatsnew/v0.19.0.txt @@ -966,6 +966,7 @@ Bug Fixes - Bug in ``DataFrame`` assignment with an object-dtyped ``Index`` where the resultant column is mutable to the original object. (:issue:`13522`) - Bug in matplotlib ``AutoDataFormatter``; this restores the second scaled formatting and re-adds micro-second scaled formatting (:issue:`13131`) - Bug in selection from a ``HDFStore`` with a fixed format and ``start`` and/or ``stop`` specified will now return the selected range (:issue:`8287`) +- Bug in ``Categorical.from_codes()`` where an unhelpful error was raised when an invalid ``ordered`` parameter was passed in (:issue:`14058`) - Bug in ``Series`` construction from a tuple of integers on windows not returning default dtype (int64) (:issue:`13646`) - Bug in ``.groupby(..).resample(..)`` when the same object is called multiple times (:issue:`13174`) diff --git a/pandas/core/categorical.py b/pandas/core/categorical.py index 6ea0a5e96672d..3ec1c7085c87d 100644 --- a/pandas/core/categorical.py +++ b/pandas/core/categorical.py @@ -231,6 +231,8 @@ class Categorical(PandasObject): def __init__(self, values, categories=None, ordered=False, name=None, fastpath=False): + self._validate_ordered(ordered) + if fastpath: # fast path self._codes = _coerce_indexer_dtype(values, categories) @@ -502,6 +504,25 @@ def _get_labels(self): _categories = None + @classmethod + def _validate_ordered(cls, ordered): + """ + Validates that we have a valid ordered parameter. If + it is not a boolean, a TypeError will be raised. + + Parameters + ---------- + ordered : object + The parameter to be verified. + + Raises + ------ + TypeError + If 'ordered' is not a boolean. + """ + if not is_bool(ordered): + raise TypeError("'ordered' must either be 'True' or 'False'") + @classmethod def _validate_categories(cls, categories, fastpath=False): """ @@ -588,8 +609,7 @@ def set_ordered(self, value, inplace=False): Whether or not to set the ordered attribute inplace or return a copy of this categorical with ordered set to the value """ - if not is_bool(value): - raise TypeError("ordered must be a boolean value") + self._validate_ordered(value) cat = self if inplace else self.copy() cat._ordered = value if not inplace: diff --git a/pandas/indexes/category.py b/pandas/indexes/category.py index f1d4fe2f26bdd..23c534624930f 100644 --- a/pandas/indexes/category.py +++ b/pandas/indexes/category.py @@ -123,6 +123,7 @@ def _create_categorical(self, data, categories=None, ordered=None): Categorical """ if not isinstance(data, ABCCategorical): + ordered = False if ordered is None else ordered from pandas.core.categorical import Categorical data = Categorical(data, categories=categories, ordered=ordered) else: diff --git a/pandas/tests/test_categorical.py b/pandas/tests/test_categorical.py index b630e0914259e..70e07b1e4930a 100644 --- a/pandas/tests/test_categorical.py +++ b/pandas/tests/test_categorical.py @@ -398,6 +398,24 @@ def f(): codes = np.random.choice([0, 1], 5, p=[0.9, 0.1]) pd.Categorical.from_codes(codes, categories=["train", "test"]) + def test_validate_ordered(self): + # see gh-14058 + exp_msg = "'ordered' must either be 'True' or 'False'" + exp_err = TypeError + + # This should be a boolean. + ordered = np.array([0, 1, 2]) + + with tm.assertRaisesRegexp(exp_err, exp_msg): + Categorical([1, 2, 3], ordered=ordered) + + with tm.assertRaisesRegexp(exp_err, exp_msg): + Categorical.from_array([1, 2, 3], ordered=ordered) + + with tm.assertRaisesRegexp(exp_err, exp_msg): + Categorical.from_codes([0, 0, 1], categories=['a', 'b', 'c'], + ordered=ordered) + def test_comparisons(self): result = self.factor[self.factor == 'a']