diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index b1f913e9ea641..9f0414cf7a806 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1655,21 +1655,15 @@ def fillna(self, value=None, method=None, limit=None): codes = self._ndarray.copy() mask = self.isna() + new_codes = self._validate_setitem_value(value) + if isinstance(value, (np.ndarray, Categorical)): # We get ndarray or Categorical if called via Series.fillna, # where it will unwrap another aligned Series before getting here - - not_categories = ~algorithms.isin(value, self.categories) - if not isna(value[not_categories]).all(): - # All entries in `value` must either be a category or NA - raise ValueError("fill value must be in categories") - - values_codes = _get_codes_for_values(value, self.categories) - codes[mask] = values_codes[mask] + codes[mask] = new_codes[mask] else: - new_code = self._validate_fill_value(value) - codes[mask] = new_code + codes[mask] = new_codes return self._from_backing_data(codes) diff --git a/pandas/tests/arrays/categorical/test_missing.py b/pandas/tests/arrays/categorical/test_missing.py index 21bea9356dcf0..364c290edc46c 100644 --- a/pandas/tests/arrays/categorical/test_missing.py +++ b/pandas/tests/arrays/categorical/test_missing.py @@ -60,7 +60,10 @@ def test_set_item_nan(self): ), (dict(), "Must specify a fill 'value' or 'method'."), (dict(method="bad"), "Invalid fill method. Expecting .* bad"), - (dict(value=Series([1, 2, 3, 4, "a"])), "fill value must be in categories"), + ( + dict(value=Series([1, 2, 3, 4, "a"])), + "Cannot setitem on a Categorical with a new category", + ), ], ) def test_fillna_raises(self, fillna_kwargs, msg): diff --git a/pandas/tests/frame/methods/test_fillna.py b/pandas/tests/frame/methods/test_fillna.py index 9fa1aa65379c5..bbb57da39705b 100644 --- a/pandas/tests/frame/methods/test_fillna.py +++ b/pandas/tests/frame/methods/test_fillna.py @@ -170,7 +170,7 @@ def test_na_actions_categorical(self): res = df.fillna(value={"cats": 3, "vals": "b"}) tm.assert_frame_equal(res, df_exp_fill) - msg = "'fill_value=4' is not present in this Categorical's categories" + msg = "Cannot setitem on a Categorical with a new category" with pytest.raises(ValueError, match=msg): df.fillna(value={"cats": 4, "vals": "c"}) diff --git a/pandas/tests/indexes/categorical/test_fillna.py b/pandas/tests/indexes/categorical/test_fillna.py index f6a6747166011..c8fc55c29054e 100644 --- a/pandas/tests/indexes/categorical/test_fillna.py +++ b/pandas/tests/indexes/categorical/test_fillna.py @@ -14,7 +14,7 @@ def test_fillna_categorical(self): tm.assert_index_equal(idx.fillna(1.0), exp) # fill by value not in categories raises ValueError - msg = "'fill_value=2.0' is not present in this Categorical's categories" + msg = "Cannot setitem on a Categorical with a new category" with pytest.raises(ValueError, match=msg): idx.fillna(2.0) @@ -36,7 +36,7 @@ def test_fillna_validates_with_no_nas(self): ci = CategoricalIndex([2, 3, 3]) cat = ci._data - msg = "'fill_value=False' is not present in this Categorical's categories" + msg = "Cannot setitem on a Categorical with a new category" with pytest.raises(ValueError, match=msg): ci.fillna(False) diff --git a/pandas/tests/series/methods/test_fillna.py b/pandas/tests/series/methods/test_fillna.py index d45486b9bdb29..aaa58cdb390f7 100644 --- a/pandas/tests/series/methods/test_fillna.py +++ b/pandas/tests/series/methods/test_fillna.py @@ -653,14 +653,14 @@ def test_fillna_categorical_raises(self): data = ["a", np.nan, "b", np.nan, np.nan] ser = Series(Categorical(data, categories=["a", "b"])) - msg = "'fill_value=d' is not present in this Categorical's categories" + msg = "Cannot setitem on a Categorical with a new category" with pytest.raises(ValueError, match=msg): ser.fillna("d") - with pytest.raises(ValueError, match="fill value must be in categories"): + with pytest.raises(ValueError, match=msg): ser.fillna(Series("d")) - with pytest.raises(ValueError, match="fill value must be in categories"): + with pytest.raises(ValueError, match=msg): ser.fillna({1: "d", 3: "a"}) msg = '"value" parameter must be a scalar or dict, but you passed a "list"'