Skip to content

Commit 376ac9a

Browse files
jbrockmendelim-vinicius
authored and
im-vinicius
committed
REF: share methods AM/BM (pandas-dev#53859)
1 parent 48fb2b7 commit 376ac9a

File tree

3 files changed

+133
-185
lines changed

3 files changed

+133
-185
lines changed

pandas/core/internals/array_manager.py

+4-82
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import itertools
77
from typing import (
88
TYPE_CHECKING,
9-
Any,
109
Callable,
1110
Hashable,
1211
Literal,
@@ -16,10 +15,8 @@
1615

1716
from pandas._libs import (
1817
NaT,
19-
algos as libalgos,
2018
lib,
2119
)
22-
from pandas.util._validators import validate_bool_kwarg
2320

2421
from pandas.core.dtypes.astype import (
2522
astype_array,
@@ -261,10 +258,11 @@ def apply(
261258
# expected "List[Union[ndarray, ExtensionArray]]"
262259
return type(self)(result_arrays, new_axes) # type: ignore[arg-type]
263260

264-
def apply_with_block(
265-
self, f, align_keys=None, swap_axis: bool = True, **kwargs
266-
) -> Self:
261+
def apply_with_block(self, f, align_keys=None, **kwargs) -> Self:
267262
# switch axis to follow BlockManager logic
263+
swap_axis = True
264+
if f == "interpolate":
265+
swap_axis = False
268266
if swap_axis and "axis" in kwargs and self.ndim == 2:
269267
kwargs["axis"] = 1 if kwargs["axis"] == 0 else 0
270268

@@ -319,50 +317,13 @@ def apply_with_block(
319317

320318
return type(self)(result_arrays, self._axes)
321319

322-
def where(self, other, cond, align: bool) -> Self:
323-
if align:
324-
align_keys = ["other", "cond"]
325-
else:
326-
align_keys = ["cond"]
327-
other = extract_array(other, extract_numpy=True)
328-
329-
return self.apply_with_block(
330-
"where",
331-
align_keys=align_keys,
332-
other=other,
333-
cond=cond,
334-
)
335-
336-
def round(self, decimals: int, using_cow: bool = False) -> Self:
337-
return self.apply_with_block("round", decimals=decimals, using_cow=using_cow)
338-
339320
def setitem(self, indexer, value) -> Self:
340321
return self.apply_with_block("setitem", indexer=indexer, value=value)
341322

342-
def putmask(self, mask, new, align: bool = True) -> Self:
343-
if align:
344-
align_keys = ["new", "mask"]
345-
else:
346-
align_keys = ["mask"]
347-
new = extract_array(new, extract_numpy=True)
348-
349-
return self.apply_with_block(
350-
"putmask",
351-
align_keys=align_keys,
352-
mask=mask,
353-
new=new,
354-
)
355-
356323
def diff(self, n: int) -> Self:
357324
assert self.ndim == 2 # caller ensures
358325
return self.apply(algos.diff, n=n)
359326

360-
def pad_or_backfill(self, **kwargs) -> Self:
361-
return self.apply_with_block("pad_or_backfill", swap_axis=False, **kwargs)
362-
363-
def interpolate(self, **kwargs) -> Self:
364-
return self.apply_with_block("interpolate", swap_axis=False, **kwargs)
365-
366327
def shift(self, periods: int, axis: AxisInt, fill_value) -> Self:
367328
if fill_value is lib.no_default:
368329
fill_value = None
@@ -375,15 +336,6 @@ def shift(self, periods: int, axis: AxisInt, fill_value) -> Self:
375336
"shift", periods=periods, axis=axis, fill_value=fill_value
376337
)
377338

378-
def fillna(self, value, limit: int | None, inplace: bool, downcast) -> Self:
379-
if limit is not None:
380-
# Do this validation even if we go through one of the no-op paths
381-
limit = libalgos.validate_limit(None, limit=limit)
382-
383-
return self.apply_with_block(
384-
"fillna", value=value, limit=limit, inplace=inplace, downcast=downcast
385-
)
386-
387339
def astype(self, dtype, copy: bool | None = False, errors: str = "raise") -> Self:
388340
if copy is None:
389341
copy = True
@@ -410,36 +362,6 @@ def _convert(arr):
410362

411363
return self.apply(_convert)
412364

413-
def replace_regex(self, **kwargs) -> Self:
414-
return self.apply_with_block("_replace_regex", **kwargs)
415-
416-
def replace(self, to_replace, value, inplace: bool) -> Self:
417-
inplace = validate_bool_kwarg(inplace, "inplace")
418-
assert np.ndim(value) == 0, value
419-
# TODO "replace" is right now implemented on the blocks, we should move
420-
# it to general array algos so it can be reused here
421-
return self.apply_with_block(
422-
"replace", value=value, to_replace=to_replace, inplace=inplace
423-
)
424-
425-
def replace_list(
426-
self,
427-
src_list: list[Any],
428-
dest_list: list[Any],
429-
inplace: bool = False,
430-
regex: bool = False,
431-
) -> Self:
432-
"""do a list replace"""
433-
inplace = validate_bool_kwarg(inplace, "inplace")
434-
435-
return self.apply_with_block(
436-
"replace_list",
437-
src_list=src_list,
438-
dest_list=dest_list,
439-
inplace=inplace,
440-
regex=regex,
441-
)
442-
443365
def to_native_types(self, **kwargs) -> Self:
444366
return self.apply(to_native_types, **kwargs)
445367

pandas/core/internals/base.py

+127
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,29 @@
66

77
from typing import (
88
TYPE_CHECKING,
9+
Any,
910
Literal,
1011
final,
1112
)
1213

1314
import numpy as np
1415

16+
from pandas._config import using_copy_on_write
17+
18+
from pandas._libs import (
19+
algos as libalgos,
20+
lib,
21+
)
1522
from pandas.errors import AbstractMethodError
23+
from pandas.util._validators import validate_bool_kwarg
1624

1725
from pandas.core.dtypes.cast import (
1826
find_common_type,
1927
np_can_hold_element,
2028
)
2129

2230
from pandas.core.base import PandasObject
31+
from pandas.core.construction import extract_array
2332
from pandas.core.indexes.api import (
2433
Index,
2534
default_index,
@@ -138,10 +147,128 @@ def apply(
138147
) -> Self:
139148
raise AbstractMethodError(self)
140149

150+
def apply_with_block(
151+
self,
152+
f,
153+
align_keys: list[str] | None = None,
154+
**kwargs,
155+
) -> Self:
156+
raise AbstractMethodError(self)
157+
141158
@final
142159
def isna(self, func) -> Self:
143160
return self.apply("apply", func=func)
144161

162+
@final
163+
def fillna(self, value, limit: int | None, inplace: bool, downcast) -> Self:
164+
if limit is not None:
165+
# Do this validation even if we go through one of the no-op paths
166+
limit = libalgos.validate_limit(None, limit=limit)
167+
168+
return self.apply_with_block(
169+
"fillna",
170+
value=value,
171+
limit=limit,
172+
inplace=inplace,
173+
downcast=downcast,
174+
using_cow=using_copy_on_write(),
175+
)
176+
177+
@final
178+
def where(self, other, cond, align: bool) -> Self:
179+
if align:
180+
align_keys = ["other", "cond"]
181+
else:
182+
align_keys = ["cond"]
183+
other = extract_array(other, extract_numpy=True)
184+
185+
return self.apply_with_block(
186+
"where",
187+
align_keys=align_keys,
188+
other=other,
189+
cond=cond,
190+
using_cow=using_copy_on_write(),
191+
)
192+
193+
@final
194+
def putmask(self, mask, new, align: bool = True) -> Self:
195+
if align:
196+
align_keys = ["new", "mask"]
197+
else:
198+
align_keys = ["mask"]
199+
new = extract_array(new, extract_numpy=True)
200+
201+
return self.apply_with_block(
202+
"putmask",
203+
align_keys=align_keys,
204+
mask=mask,
205+
new=new,
206+
using_cow=using_copy_on_write(),
207+
)
208+
209+
@final
210+
def round(self, decimals: int, using_cow: bool = False) -> Self:
211+
return self.apply_with_block(
212+
"round",
213+
decimals=decimals,
214+
using_cow=using_cow,
215+
)
216+
217+
@final
218+
def replace(self, to_replace, value, inplace: bool) -> Self:
219+
inplace = validate_bool_kwarg(inplace, "inplace")
220+
# NDFrame.replace ensures the not-is_list_likes here
221+
assert not lib.is_list_like(to_replace)
222+
assert not lib.is_list_like(value)
223+
return self.apply_with_block(
224+
"replace",
225+
to_replace=to_replace,
226+
value=value,
227+
inplace=inplace,
228+
using_cow=using_copy_on_write(),
229+
)
230+
231+
@final
232+
def replace_regex(self, **kwargs) -> Self:
233+
return self.apply_with_block(
234+
"_replace_regex", **kwargs, using_cow=using_copy_on_write()
235+
)
236+
237+
@final
238+
def replace_list(
239+
self,
240+
src_list: list[Any],
241+
dest_list: list[Any],
242+
inplace: bool = False,
243+
regex: bool = False,
244+
) -> Self:
245+
"""do a list replace"""
246+
inplace = validate_bool_kwarg(inplace, "inplace")
247+
248+
bm = self.apply_with_block(
249+
"replace_list",
250+
src_list=src_list,
251+
dest_list=dest_list,
252+
inplace=inplace,
253+
regex=regex,
254+
using_cow=using_copy_on_write(),
255+
)
256+
bm._consolidate_inplace()
257+
return bm
258+
259+
def interpolate(self, inplace: bool, **kwargs) -> Self:
260+
return self.apply_with_block(
261+
"interpolate", inplace=inplace, **kwargs, using_cow=using_copy_on_write()
262+
)
263+
264+
def pad_or_backfill(self, inplace: bool, **kwargs) -> Self:
265+
return self.apply_with_block(
266+
"pad_or_backfill",
267+
inplace=inplace,
268+
**kwargs,
269+
using_cow=using_copy_on_write(),
270+
)
271+
145272
# --------------------------------------------------------------------
146273
# Consolidation: No-ops for all but BlockManager
147274

0 commit comments

Comments
 (0)