diff --git a/doc/source/whatsnew/v0.23.1.txt b/doc/source/whatsnew/v0.23.1.txt index 4876678baaa6e..f33f03a3eb500 100644 --- a/doc/source/whatsnew/v0.23.1.txt +++ b/doc/source/whatsnew/v0.23.1.txt @@ -66,6 +66,7 @@ Categorical ^^^^^^^^^^^ - Bug in :func:`pandas.util.testing.assert_index_equal` which raised ``AssertionError`` incorrectly, when comparing two :class:`CategoricalIndex` objects with param ``check_categorical=False`` (:issue:`19776`) +- Bug in :meth:`Categorical.fillna` incorrectly raising a ``TypeError`` when `value` the individual categories are iterable and `value` is an iterable (:issue:`21097`, :issue:`19788`) Conversion ^^^^^^^^^^ diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index abcb9ae3494b5..a1a8f098b582e 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -12,6 +12,7 @@ from pandas.core.dtypes.generic import ( ABCSeries, ABCIndexClass, ABCCategoricalIndex) from pandas.core.dtypes.missing import isna, notna +from pandas.core.dtypes.inference import is_hashable from pandas.core.dtypes.cast import ( maybe_infer_to_datetimelike, coerce_indexer_dtype) @@ -1751,7 +1752,7 @@ def fillna(self, value=None, method=None, limit=None): values[indexer] = values_codes[values_codes != -1] # If value is not a dict or Series it should be a scalar - elif is_scalar(value): + elif is_hashable(value): if not isna(value) and value not in self.categories: raise ValueError("fill value must be in categories") diff --git a/pandas/tests/categorical/test_missing.py b/pandas/tests/categorical/test_missing.py index 5133c97d8b590..c78f02245a5b4 100644 --- a/pandas/tests/categorical/test_missing.py +++ b/pandas/tests/categorical/test_missing.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import collections + import numpy as np import pytest @@ -68,3 +70,16 @@ def test_fillna_raises(self, fillna_kwargs, msg): with tm.assert_raises_regex(ValueError, msg): cat.fillna(**fillna_kwargs) + + @pytest.mark.parametrize("named", [True, False]) + def test_fillna_iterable_category(self, named): + # https://github.com/pandas-dev/pandas/issues/21097 + if named: + Point = collections.namedtuple("Point", "x y") + else: + Point = lambda *args: args # tuple + cat = Categorical([Point(0, 0), Point(0, 1), None]) + result = cat.fillna(Point(0, 0)) + expected = Categorical([Point(0, 0), Point(0, 1), Point(0, 0)]) + + tm.assert_categorical_equal(result, expected)