37
37
)
38
38
from pandas .core .dtypes .dtypes import CategoricalDtype
39
39
from pandas .core .dtypes .generic import ABCIndexClass , ABCSeries
40
- from pandas .core .dtypes .inference import is_hashable
41
40
from pandas .core .dtypes .missing import is_valid_nat_for_dtype , isna , notna
42
41
43
42
from pandas .core import ops
@@ -1630,6 +1629,7 @@ def fillna(self, value=None, method=None, limit=None):
1630
1629
value , method = validate_fillna_kwargs (
1631
1630
value , method , validate_scalar_dict_value = False
1632
1631
)
1632
+ value = extract_array (value , extract_numpy = True )
1633
1633
1634
1634
if value is None :
1635
1635
value = np .nan
@@ -1638,10 +1638,8 @@ def fillna(self, value=None, method=None, limit=None):
1638
1638
"specifying a limit for fillna has not been implemented yet"
1639
1639
)
1640
1640
1641
- codes = self ._codes
1642
-
1643
- # pad / bfill
1644
1641
if method is not None :
1642
+ # pad / bfill
1645
1643
1646
1644
# TODO: dispatch when self.categories is EA-dtype
1647
1645
values = np .asarray (self ).reshape (- 1 , len (self ))
@@ -1651,40 +1649,25 @@ def fillna(self, value=None, method=None, limit=None):
1651
1649
codes = _get_codes_for_values (values , self .categories )
1652
1650
1653
1651
else :
1652
+ # We copy even if there is nothing to fill
1653
+ codes = self ._ndarray .copy ()
1654
+ mask = self .isna ()
1654
1655
1655
- # If value is a dict or a Series (a dict value has already
1656
- # been converted to a Series)
1657
- if isinstance (value , (np .ndarray , Categorical , ABCSeries )):
1656
+ if isinstance (value , (np .ndarray , Categorical )):
1658
1657
# We get ndarray or Categorical if called via Series.fillna,
1659
1658
# where it will unwrap another aligned Series before getting here
1660
1659
1661
- mask = ~ algorithms .isin (value , self .categories )
1662
- if not isna (value [mask ]).all ():
1660
+ not_categories = ~ algorithms .isin (value , self .categories )
1661
+ if not isna (value [not_categories ]).all ():
1662
+ # All entries in `value` must either be a category or NA
1663
1663
raise ValueError ("fill value must be in categories" )
1664
1664
1665
1665
values_codes = _get_codes_for_values (value , self .categories )
1666
- indexer = np .where (codes == - 1 )
1667
- codes = codes .copy ()
1668
- codes [indexer ] = values_codes [indexer ]
1669
-
1670
- # If value is not a dict or Series it should be a scalar
1671
- elif is_hashable (value ):
1672
- if not isna (value ) and value not in self .categories :
1673
- raise ValueError ("fill value must be in categories" )
1674
-
1675
- mask = codes == - 1
1676
- if mask .any ():
1677
- codes = codes .copy ()
1678
- if isna (value ):
1679
- codes [mask ] = - 1
1680
- else :
1681
- codes [mask ] = self ._unbox_scalar (value )
1666
+ codes [mask ] = values_codes [mask ]
1682
1667
1683
1668
else :
1684
- raise TypeError (
1685
- f"'value' parameter must be a scalar, dict "
1686
- f"or Series, but you passed a { type (value ).__name__ } "
1687
- )
1669
+ new_code = self ._validate_fill_value (value )
1670
+ codes [mask ] = new_code
1688
1671
1689
1672
return self ._from_backing_data (codes )
1690
1673
0 commit comments