Skip to content

Commit 997e8ae

Browse files
Revert "DEPR: CategoricalBlock; combine Block.replace methods (pandas-dev#40527)"
This reverts commit 09e2036.
1 parent 05a0e98 commit 997e8ae

File tree

7 files changed

+53
-42
lines changed

7 files changed

+53
-42
lines changed

pandas/core/internals/__init__.py

+2-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010
from pandas.core.internals.blocks import ( # io.pytables, io.packers
1111
Block,
12+
CategoricalBlock,
1213
DatetimeBlock,
1314
DatetimeTZBlock,
1415
ExtensionBlock,
@@ -26,6 +27,7 @@
2627

2728
__all__ = [
2829
"Block",
30+
"CategoricalBlock",
2931
"NumericBlock",
3032
"DatetimeBlock",
3133
"DatetimeTZBlock",
@@ -44,18 +46,3 @@
4446
"create_block_manager_from_arrays",
4547
"create_block_manager_from_blocks",
4648
]
47-
48-
49-
def __getattr__(name: str):
50-
import warnings
51-
52-
if name == "CategoricalBlock":
53-
warnings.warn(
54-
"CategoricalBlock is deprecated and will be removed in a future version. "
55-
"Use ExtensionBlock instead.",
56-
DeprecationWarning,
57-
stacklevel=2,
58-
)
59-
return ExtensionBlock
60-
61-
raise AttributeError(f"module 'pandas.core.internals' has no attribute '{name}'")

pandas/core/internals/blocks.py

+42-22
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,6 @@ def copy(self, deep: bool = True):
678678
# ---------------------------------------------------------------------
679679
# Replace
680680

681-
@final
682681
def replace(
683682
self,
684683
to_replace,
@@ -693,30 +692,15 @@ def replace(
693692
"""
694693
inplace = validate_bool_kwarg(inplace, "inplace")
695694

696-
# Note: the checks we do in NDFrame.replace ensure we never get
697-
# here with listlike to_replace or value, as those cases
698-
# go through _replace_list
699-
700-
values = self.values
701-
702-
if isinstance(values, Categorical):
703-
# TODO: avoid special-casing
704-
blk = self if inplace else self.copy()
705-
blk.values.replace(to_replace, value, inplace=True)
706-
return [blk]
707-
708-
regex = should_use_regex(regex, to_replace)
709-
710-
if regex:
711-
return self._replace_regex(to_replace, value, inplace=inplace)
712-
713695
if not self._can_hold_element(to_replace):
714696
# We cannot hold `to_replace`, so we know immediately that
715697
# replacing it is a no-op.
716698
# Note: If to_replace were a list, NDFrame.replace would call
717699
# replace_list instead of replace.
718700
return [self] if inplace else [self.copy()]
719701

702+
values = self.values
703+
720704
mask = missing.mask_missing(values, to_replace)
721705
if not mask.any():
722706
# Note: we get here with test_replace_extension_other incorrectly
@@ -741,7 +725,7 @@ def replace(
741725
else:
742726
# split so that we only upcast where necessary
743727
return self.split_and_operate(
744-
type(self).replace, to_replace, value, inplace=True, regex=regex
728+
type(self).replace, to_replace, value, inplace=inplace, regex=regex
745729
)
746730

747731
@final
@@ -1244,7 +1228,7 @@ def take_nd(
12441228
Take values according to indexer and return them as a block.bb
12451229
12461230
"""
1247-
# algos.take_nd dispatches for DatetimeTZBlock
1231+
# algos.take_nd dispatches for DatetimeTZBlock, CategoricalBlock
12481232
# so need to preserve types
12491233
# sparse is treated like an ndarray, but needs .get_values() shaping
12501234

@@ -1443,7 +1427,7 @@ class ExtensionBlock(Block):
14431427
Notes
14441428
-----
14451429
This holds all 3rd-party extension array types. It's also the immediate
1446-
parent class for our internal extension types' blocks.
1430+
parent class for our internal extension types' blocks, CategoricalBlock.
14471431
14481432
ExtensionArrays are limited to 1-D.
14491433
"""
@@ -1595,6 +1579,7 @@ def take_nd(
15951579

15961580
def _can_hold_element(self, element: Any) -> bool:
15971581
# TODO: We may need to think about pushing this onto the array.
1582+
# We're doing the same as CategoricalBlock here.
15981583
return True
15991584

16001585
def _slice(self, slicer):
@@ -2016,6 +2001,41 @@ def _maybe_downcast(self, blocks: List[Block], downcast=None) -> List[Block]:
20162001
def _can_hold_element(self, element: Any) -> bool:
20172002
return True
20182003

2004+
def replace(
2005+
self,
2006+
to_replace,
2007+
value,
2008+
inplace: bool = False,
2009+
regex: bool = False,
2010+
) -> List[Block]:
2011+
# Note: the checks we do in NDFrame.replace ensure we never get
2012+
# here with listlike to_replace or value, as those cases
2013+
# go through _replace_list
2014+
2015+
regex = should_use_regex(regex, to_replace)
2016+
2017+
if regex:
2018+
return self._replace_regex(to_replace, value, inplace=inplace)
2019+
else:
2020+
return super().replace(to_replace, value, inplace=inplace, regex=False)
2021+
2022+
2023+
class CategoricalBlock(ExtensionBlock):
2024+
__slots__ = ()
2025+
2026+
def replace(
2027+
self,
2028+
to_replace,
2029+
value,
2030+
inplace: bool = False,
2031+
regex: bool = False,
2032+
) -> List[Block]:
2033+
inplace = validate_bool_kwarg(inplace, "inplace")
2034+
result = self if inplace else self.copy()
2035+
2036+
result.values.replace(to_replace, value, inplace=True)
2037+
return [result]
2038+
20192039

20202040
# -----------------------------------------------------------------
20212041
# Constructor Helpers
@@ -2078,7 +2098,7 @@ def get_block_type(values, dtype: Optional[Dtype] = None):
20782098
# Need this first(ish) so that Sparse[datetime] is sparse
20792099
cls = ExtensionBlock
20802100
elif isinstance(dtype, CategoricalDtype):
2081-
cls = ExtensionBlock
2101+
cls = CategoricalBlock
20822102
elif vtype is Timestamp:
20832103
cls = DatetimeTZBlock
20842104
elif vtype is Interval or vtype is Period:

pandas/core/internals/managers.py

+8
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
)
6868
from pandas.core.internals.blocks import (
6969
Block,
70+
CategoricalBlock,
7071
DatetimeTZBlock,
7172
ExtensionBlock,
7273
ObjectValuesExtensionBlock,
@@ -1860,6 +1861,13 @@ def _form_blocks(
18601861
object_blocks = _simple_blockify(items_dict["ObjectBlock"], np.object_)
18611862
blocks.extend(object_blocks)
18621863

1864+
if len(items_dict["CategoricalBlock"]) > 0:
1865+
cat_blocks = [
1866+
new_block(array, klass=CategoricalBlock, placement=i, ndim=2)
1867+
for i, array in items_dict["CategoricalBlock"]
1868+
]
1869+
blocks.extend(cat_blocks)
1870+
18631871
if len(items_dict["ExtensionBlock"]):
18641872
external_blocks = [
18651873
new_block(array, klass=ExtensionBlock, placement=i, ndim=2)

pandas/tests/internals/test_api.py

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def test_namespace():
2626
]
2727
expected = [
2828
"Block",
29+
"CategoricalBlock",
2930
"NumericBlock",
3031
"DatetimeBlock",
3132
"DatetimeTZBlock",

pandas/tests/io/test_common.py

-3
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,6 @@ def test_read_expands_user_home_dir(
258258
),
259259
],
260260
)
261-
@pytest.mark.filterwarnings(
262-
"ignore:CategoricalBlock is deprecated:DeprecationWarning"
263-
)
264261
def test_read_fspath_all(self, reader, module, path, datapath):
265262
pytest.importorskip(module)
266263
path = datapath(*path)

pandas/tests/io/test_feather.py

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
@filter_sparse
2222
@pytest.mark.single
23-
@pytest.mark.filterwarnings("ignore:CategoricalBlock is deprecated:DeprecationWarning")
2423
class TestFeather:
2524
def check_error_on_write(self, df, exc, err_msg):
2625
# check that we are raising the exception

pandas/tests/io/test_parquet.py

-1
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,6 @@ def test_write_column_index_nonstring(self, pa):
573573
self.check_error_on_write(df, engine, ValueError, msg)
574574

575575

576-
@pytest.mark.filterwarnings("ignore:CategoricalBlock is deprecated:DeprecationWarning")
577576
class TestParquetPyArrow(Base):
578577
def test_basic(self, pa, df_full):
579578

0 commit comments

Comments
 (0)