Skip to content

Commit b19ab49

Browse files
authored
Merge branch 'main' into issue54938-struct-accessor
2 parents 48796a7 + faeedad commit b19ab49

26 files changed

+290
-144
lines changed

doc/source/whatsnew/v2.1.1.rst

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Bug fixes
3434
~~~~~~~~~
3535
- Fixed bug for :class:`ArrowDtype` raising ``NotImplementedError`` for fixed-size list (:issue:`55000`)
3636
- Fixed bug in :meth:`DataFrame.stack` with ``future_stack=True`` and columns a non-:class:`MultiIndex` consisting of tuples (:issue:`54948`)
37+
- Fixed bug in :meth:`Series.pct_change` and :meth:`DataFrame.pct_change` showing unnecessary ``FutureWarning`` (:issue:`54981`)
3738

3839
.. ---------------------------------------------------------------------------
3940
.. _whatsnew_211.other:

doc/source/whatsnew/v2.2.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ Groupby/resample/rolling
271271

272272
Reshaping
273273
^^^^^^^^^
274-
-
274+
- Bug in :func:`merge` returning columns in incorrect order when left and/or right is empty (:issue:`51929`)
275275
-
276276

277277
Sparse

pandas/core/arrays/string_arrow.py

+23-8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050

5151

5252
if TYPE_CHECKING:
53+
from collections.abc import Sequence
54+
5355
from pandas._typing import (
5456
Dtype,
5557
Scalar,
@@ -337,19 +339,13 @@ def _str_startswith(self, pat: str, na=None):
337339
result = pc.starts_with(self._pa_array, pattern=pat)
338340
if not isna(na):
339341
result = result.fill_null(na)
340-
result = self._result_converter(result)
341-
if not isna(na):
342-
result[isna(result)] = bool(na)
343-
return result
342+
return self._result_converter(result)
344343

345344
def _str_endswith(self, pat: str, na=None):
346345
result = pc.ends_with(self._pa_array, pattern=pat)
347346
if not isna(na):
348347
result = result.fill_null(na)
349-
result = self._result_converter(result)
350-
if not isna(na):
351-
result[isna(result)] = bool(na)
352-
return result
348+
return self._result_converter(result)
353349

354350
def _str_replace(
355351
self,
@@ -368,6 +364,12 @@ def _str_replace(
368364
result = func(self._pa_array, pattern=pat, replacement=repl, max_replacements=n)
369365
return type(self)(result)
370366

367+
def _str_repeat(self, repeats: int | Sequence[int]):
368+
if not isinstance(repeats, int):
369+
return super()._str_repeat(repeats)
370+
else:
371+
return type(self)(pc.binary_repeat(self._pa_array, repeats))
372+
371373
def _str_match(
372374
self, pat: str, case: bool = True, flags: int = 0, na: Scalar | None = None
373375
):
@@ -382,6 +384,19 @@ def _str_fullmatch(
382384
pat = f"{pat}$"
383385
return self._str_match(pat, case, flags, na)
384386

387+
def _str_slice(
388+
self, start: int | None = None, stop: int | None = None, step: int | None = None
389+
):
390+
if stop is None:
391+
return super()._str_slice(start, stop, step)
392+
if start is None:
393+
start = 0
394+
if step is None:
395+
step = 1
396+
return type(self)(
397+
pc.utf8_slice_codeunits(self._pa_array, start=start, stop=stop, step=step)
398+
)
399+
385400
def _str_isalnum(self):
386401
result = pc.utf8_is_alnum(self._pa_array)
387402
return self._result_converter(result)

pandas/core/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,8 @@ def array(self) -> ExtensionArray:
485485
types, this is the actual array. For NumPy native types, this
486486
is a thin (no copy) wrapper around :class:`numpy.ndarray`.
487487
488-
``.array`` differs ``.values`` which may require converting the
489-
data to a different form.
488+
``.array`` differs from ``.values``, which may require converting
489+
the data to a different form.
490490
491491
See Also
492492
--------

pandas/core/frame.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -1926,11 +1926,17 @@ def to_dict(
19261926
self,
19271927
orient: Literal["dict", "list", "series", "split", "tight", "index"] = ...,
19281928
into: type[dict] = ...,
1929+
index: bool = ...,
19291930
) -> dict:
19301931
...
19311932

19321933
@overload
1933-
def to_dict(self, orient: Literal["records"], into: type[dict] = ...) -> list[dict]:
1934+
def to_dict(
1935+
self,
1936+
orient: Literal["records"],
1937+
into: type[dict] = ...,
1938+
index: bool = ...,
1939+
) -> list[dict]:
19341940
...
19351941

19361942
@deprecate_nonkeyword_arguments(
@@ -11297,7 +11303,7 @@ def _reduce_axis1(self, name: str, func, skipna: bool) -> Series:
1129711303
def any( # type: ignore[override]
1129811304
self,
1129911305
*,
11300-
axis: Axis = 0,
11306+
axis: Axis | None = 0,
1130111307
bool_only: bool = False,
1130211308
skipna: bool = True,
1130311309
**kwargs,
@@ -11312,7 +11318,7 @@ def any( # type: ignore[override]
1131211318
@doc(make_doc("all", ndim=2))
1131311319
def all(
1131411320
self,
11315-
axis: Axis = 0,
11321+
axis: Axis | None = 0,
1131611322
bool_only: bool = False,
1131711323
skipna: bool = True,
1131811324
**kwargs,
@@ -11711,6 +11717,7 @@ def quantile(
1171111717
axis: Axis = ...,
1171211718
numeric_only: bool = ...,
1171311719
interpolation: QuantileInterpolation = ...,
11720+
method: Literal["single", "table"] = ...,
1171411721
) -> Series:
1171511722
...
1171611723

@@ -11721,6 +11728,7 @@ def quantile(
1172111728
axis: Axis = ...,
1172211729
numeric_only: bool = ...,
1172311730
interpolation: QuantileInterpolation = ...,
11731+
method: Literal["single", "table"] = ...,
1172411732
) -> Series | DataFrame:
1172511733
...
1172611734

@@ -11731,6 +11739,7 @@ def quantile(
1173111739
axis: Axis = ...,
1173211740
numeric_only: bool = ...,
1173311741
interpolation: QuantileInterpolation = ...,
11742+
method: Literal["single", "table"] = ...,
1173411743
) -> Series | DataFrame:
1173511744
...
1173611745

@@ -11830,11 +11839,10 @@ def quantile(
1183011839

1183111840
if not is_list_like(q):
1183211841
# BlockManager.quantile expects listlike, so we wrap and unwrap here
11833-
# error: List item 0 has incompatible type "Union[float, Union[Union[
11834-
# ExtensionArray, ndarray[Any, Any]], Index, Series], Sequence[float]]";
11835-
# expected "float"
11836-
res_df = self.quantile( # type: ignore[call-overload]
11837-
[q],
11842+
# error: List item 0 has incompatible type "float | ExtensionArray |
11843+
# ndarray[Any, Any] | Index | Series | Sequence[float]"; expected "float"
11844+
res_df = self.quantile(
11845+
[q], # type: ignore[list-item]
1183811846
axis=axis,
1183911847
numeric_only=numeric_only,
1184011848
interpolation=interpolation,

pandas/core/generic.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -11793,15 +11793,21 @@ def pct_change(
1179311793
stacklevel=find_stack_level(),
1179411794
)
1179511795
if fill_method is lib.no_default:
11796-
if self.isna().values.any():
11797-
warnings.warn(
11798-
"The default fill_method='pad' in "
11799-
f"{type(self).__name__}.pct_change is deprecated and will be "
11800-
"removed in a future version. Call ffill before calling "
11801-
"pct_change to retain current behavior and silence this warning.",
11802-
FutureWarning,
11803-
stacklevel=find_stack_level(),
11804-
)
11796+
cols = self.items() if self.ndim == 2 else [(None, self)]
11797+
for _, col in cols:
11798+
mask = col.isna().values
11799+
mask = mask[np.argmax(~mask) :]
11800+
if mask.any():
11801+
warnings.warn(
11802+
"The default fill_method='pad' in "
11803+
f"{type(self).__name__}.pct_change is deprecated and will be "
11804+
"removed in a future version. Call ffill before calling "
11805+
"pct_change to retain current behavior and silence this "
11806+
"warning.",
11807+
FutureWarning,
11808+
stacklevel=find_stack_level(),
11809+
)
11810+
break
1180511811
fill_method = "pad"
1180611812
if limit is lib.no_default:
1180711813
limit = None
@@ -11827,7 +11833,7 @@ def _logical_func(
1182711833
self,
1182811834
name: str,
1182911835
func,
11830-
axis: Axis = 0,
11836+
axis: Axis | None = 0,
1183111837
bool_only: bool_t = False,
1183211838
skipna: bool_t = True,
1183311839
**kwargs,
@@ -11840,7 +11846,10 @@ def _logical_func(
1184011846
res = self._logical_func(
1184111847
name, func, axis=0, bool_only=bool_only, skipna=skipna, **kwargs
1184211848
)
11843-
return res._logical_func(name, func, skipna=skipna, **kwargs)
11849+
# error: Item "bool" of "Series | bool" has no attribute "_logical_func"
11850+
return res._logical_func( # type: ignore[union-attr]
11851+
name, func, skipna=skipna, **kwargs
11852+
)
1184411853
elif axis is None:
1184511854
axis = 0
1184611855

pandas/core/reshape/merge.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -1272,12 +1272,7 @@ def _get_merge_keys(
12721272
# work-around for merge_asof(right_index=True)
12731273
right_keys.append(right.index._values)
12741274
if lk is not None and lk == rk: # FIXME: what about other NAs?
1275-
# avoid key upcast in corner case (length-0)
1276-
lk = cast(Hashable, lk)
1277-
if len(left) > 0:
1278-
right_drop.append(rk)
1279-
else:
1280-
left_drop.append(lk)
1275+
right_drop.append(rk)
12811276
else:
12821277
rk = cast(ArrayLike, rk)
12831278
right_keys.append(rk)

pandas/io/excel/_base.py

+12-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import abc
43
from collections.abc import (
54
Hashable,
65
Iterable,
@@ -549,7 +548,7 @@ def read_excel(
549548
_WorkbookT = TypeVar("_WorkbookT")
550549

551550

552-
class BaseExcelReader(Generic[_WorkbookT], metaclass=abc.ABCMeta):
551+
class BaseExcelReader(Generic[_WorkbookT]):
553552
book: _WorkbookT
554553

555554
def __init__(
@@ -589,13 +588,11 @@ def __init__(
589588
)
590589

591590
@property
592-
@abc.abstractmethod
593591
def _workbook_class(self) -> type[_WorkbookT]:
594-
pass
592+
raise NotImplementedError
595593

596-
@abc.abstractmethod
597594
def load_workbook(self, filepath_or_buffer, engine_kwargs) -> _WorkbookT:
598-
pass
595+
raise NotImplementedError
599596

600597
def close(self) -> None:
601598
if hasattr(self, "book"):
@@ -611,21 +608,17 @@ def close(self) -> None:
611608
self.handles.close()
612609

613610
@property
614-
@abc.abstractmethod
615611
def sheet_names(self) -> list[str]:
616-
pass
612+
raise NotImplementedError
617613

618-
@abc.abstractmethod
619614
def get_sheet_by_name(self, name: str):
620-
pass
615+
raise NotImplementedError
621616

622-
@abc.abstractmethod
623617
def get_sheet_by_index(self, index: int):
624-
pass
618+
raise NotImplementedError
625619

626-
@abc.abstractmethod
627620
def get_sheet_data(self, sheet, rows: int | None = None):
628-
pass
621+
raise NotImplementedError
629622

630623
def raise_if_bad_sheet_by_index(self, index: int) -> None:
631624
n_sheets = len(self.sheet_names)
@@ -940,7 +933,7 @@ def parse(
940933

941934

942935
@doc(storage_options=_shared_docs["storage_options"])
943-
class ExcelWriter(Generic[_WorkbookT], metaclass=abc.ABCMeta):
936+
class ExcelWriter(Generic[_WorkbookT]):
944937
"""
945938
Class for writing DataFrame objects into excel sheets.
946939
@@ -1178,20 +1171,19 @@ def engine(self) -> str:
11781171
return self._engine
11791172

11801173
@property
1181-
@abc.abstractmethod
11821174
def sheets(self) -> dict[str, Any]:
11831175
"""Mapping of sheet names to sheet objects."""
1176+
raise NotImplementedError
11841177

11851178
@property
1186-
@abc.abstractmethod
11871179
def book(self) -> _WorkbookT:
11881180
"""
11891181
Book instance. Class type will depend on the engine used.
11901182
11911183
This attribute can be used to access engine-specific features.
11921184
"""
1185+
raise NotImplementedError
11931186

1194-
@abc.abstractmethod
11951187
def _write_cells(
11961188
self,
11971189
cells,
@@ -1214,12 +1206,13 @@ def _write_cells(
12141206
freeze_panes: int tuple of length 2
12151207
contains the bottom-most row and right-most column to freeze
12161208
"""
1209+
raise NotImplementedError
12171210

1218-
@abc.abstractmethod
12191211
def _save(self) -> None:
12201212
"""
12211213
Save workbook to disk.
12221214
"""
1215+
raise NotImplementedError
12231216

12241217
def __init__(
12251218
self,

pandas/io/formats/excel.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -941,9 +941,7 @@ def write(
941941
if isinstance(writer, ExcelWriter):
942942
need_save = False
943943
else:
944-
# error: Cannot instantiate abstract class 'ExcelWriter' with abstract
945-
# attributes 'engine', 'save', 'supported_extensions' and 'write_cells'
946-
writer = ExcelWriter( # type: ignore[abstract]
944+
writer = ExcelWriter(
947945
writer,
948946
engine=engine,
949947
storage_options=storage_options,

pandas/io/json/_json.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
JSONEngine,
8383
JSONSerializable,
8484
ReadBuffer,
85+
Self,
8586
StorageOptions,
8687
WriteBuffer,
8788
)
@@ -1056,7 +1057,7 @@ def close(self) -> None:
10561057
if self.handles is not None:
10571058
self.handles.close()
10581059

1059-
def __iter__(self: JsonReader[FrameSeriesStrT]) -> JsonReader[FrameSeriesStrT]:
1060+
def __iter__(self) -> Self:
10601061
return self
10611062

10621063
@overload
@@ -1099,7 +1100,7 @@ def __next__(self) -> DataFrame | Series:
10991100
else:
11001101
return obj
11011102

1102-
def __enter__(self) -> JsonReader[FrameSeriesStrT]:
1103+
def __enter__(self) -> Self:
11031104
return self
11041105

11051106
def __exit__(

0 commit comments

Comments
 (0)