Skip to content

Commit 682da85

Browse files
committed
DEPR: casting non-standard listlikes to array in arithmetic operations
1 parent c8213d1 commit 682da85

File tree

15 files changed

+194
-20
lines changed

15 files changed

+194
-20
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ Other Deprecations
715715
- Deprecated using ``epoch`` date format in :meth:`DataFrame.to_json` and :meth:`Series.to_json`, use ``iso`` instead. (:issue:`57063`)
716716
- Deprecated allowing ``fill_value`` that cannot be held in the original dtype (excepting NA values for integer and bool dtypes) in :meth:`Series.unstack` and :meth:`DataFrame.unstack` (:issue:`12189`, :issue:`53868`)
717717
- Deprecated allowing ``fill_value`` that cannot be held in the original dtype (excepting NA values for integer and bool dtypes) in :meth:`Series.shift` and :meth:`DataFrame.shift` (:issue:`53802`)
718+
- Deprecated arithmetic operations between pandas objects (:class:`DataFrame`, :class:`Series`, :class:`Index`, and pandas-implemented :class:`ExtensionArray` subclasses) and list-likes other than ``list``, ``np.ndarray``, :class:`ExtensionArray`, :class:`Index`, :class:`Series`, :class:`DataFrame`. For e.g. ``tuple`` or ``range``, explicitly cast these to a supported object instead. In a future version, these will be treated as scalar-like for pointwise operation (:issue:`62423`)
718719
- Deprecated slicing on a :class:`Series` or :class:`DataFrame` with a :class:`DatetimeIndex` using a ``datetime.date`` object, explicitly cast to :class:`Timestamp` instead (:issue:`35830`)
719720

720721
.. ---------------------------------------------------------------------------

pandas/core/arrays/arrow/array.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,18 @@ def _op_method_error_message(self, other, op) -> str:
942942
)
943943

944944
def _evaluate_op_method(self, other, op, arrow_funcs) -> Self:
945+
if is_list_like(other) and not isinstance(
946+
other, (np.ndarray, ExtensionArray, list)
947+
):
948+
warnings.warn(
949+
f"Operation with {type(other).__name__} are deprecated. "
950+
"In a future version these will be treated as scalar-like. "
951+
"To retain the old behavior, explicitly wrap in a Series "
952+
"instead.",
953+
Pandas4Warning,
954+
stacklevel=find_stack_level(),
955+
)
956+
945957
pa_type = self._pa_array.type
946958
other_original = other
947959
other = self._box_pa(other)

pandas/core/arrays/boolean.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,25 @@
77
Self,
88
cast,
99
)
10+
import warnings
1011

1112
import numpy as np
1213

1314
from pandas._libs import (
1415
lib,
1516
missing as libmissing,
1617
)
18+
from pandas.errors import Pandas4Warning
1719
from pandas.util._decorators import set_module
20+
from pandas.util._exceptions import find_stack_level
1821

1922
from pandas.core.dtypes.common import is_list_like
2023
from pandas.core.dtypes.dtypes import register_extension_dtype
2124
from pandas.core.dtypes.missing import isna
2225

2326
from pandas.core import ops
2427
from pandas.core.array_algos import masked_accumulations
28+
from pandas.core.arrays import ExtensionArray
2529
from pandas.core.arrays.masked import (
2630
BaseMaskedArray,
2731
BaseMaskedDtype,
@@ -378,6 +382,16 @@ def _logical_method(self, other, op):
378382
if isinstance(other, BooleanArray):
379383
other, mask = other._data, other._mask
380384
elif is_list_like(other):
385+
if not isinstance(other, (list, ExtensionArray, np.ndarray)):
386+
warnings.warn(
387+
f"Operation with {type(other).__name__} are deprecated. "
388+
"In a future version these will be treated as scalar-like. "
389+
"To retain the old behavior, explicitly wrap in a Series "
390+
"instead.",
391+
Pandas4Warning,
392+
stacklevel=find_stack_level(),
393+
)
394+
381395
other = np.asarray(other, dtype="bool")
382396
if other.ndim > 1:
383397
return NotImplemented

pandas/core/arrays/datetimelike.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
from pandas.errors import (
7979
AbstractMethodError,
8080
InvalidComparison,
81+
Pandas4Warning,
8182
PerformanceWarning,
8283
)
8384
from pandas.util._decorators import (
@@ -968,6 +969,17 @@ def _cmp_method(self, other, op):
968969
# TODO: handle 2D-like listlikes
969970
return op(self.ravel(), other.ravel()).reshape(self.shape)
970971

972+
if is_list_like(other):
973+
if not isinstance(other, (list, np.ndarray, ExtensionArray)):
974+
warnings.warn(
975+
f"Operation with {type(other).__name__} are deprecated. "
976+
"In a future version these will be treated as scalar-like. "
977+
"To retain the old behavior, explicitly wrap in a Series "
978+
"instead.",
979+
Pandas4Warning,
980+
stacklevel=find_stack_level(),
981+
)
982+
971983
try:
972984
other = self._validate_comparison_value(other)
973985
except InvalidComparison:

pandas/core/arrays/interval.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
TypeAlias,
1414
overload,
1515
)
16+
import warnings
1617

1718
import numpy as np
1819

@@ -38,8 +39,12 @@
3839
npt,
3940
)
4041
from pandas.compat.numpy import function as nv
41-
from pandas.errors import IntCastingNaNError
42+
from pandas.errors import (
43+
IntCastingNaNError,
44+
Pandas4Warning,
45+
)
4246
from pandas.util._decorators import Appender
47+
from pandas.util._exceptions import find_stack_level
4348

4449
from pandas.core.dtypes.cast import (
4550
LossySetitemError,
@@ -736,6 +741,15 @@ def __setitem__(self, key, value) -> None:
736741
def _cmp_method(self, other, op):
737742
# ensure pandas array for list-like and eliminate non-interval scalars
738743
if is_list_like(other):
744+
if not isinstance(other, (list, np.ndarray, ExtensionArray)):
745+
warnings.warn(
746+
f"Operation with {type(other).__name__} are deprecated. "
747+
"In a future version these will be treated as scalar-like. "
748+
"To retain the old behavior, explicitly wrap in a Series "
749+
"instead.",
750+
Pandas4Warning,
751+
stacklevel=find_stack_level(),
752+
)
739753
if len(self) != len(other):
740754
raise ValueError("Lengths must match to compare")
741755
other = pd_array(other)

pandas/core/arrays/masked.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424
IS64,
2525
is_platform_windows,
2626
)
27-
from pandas.errors import AbstractMethodError
27+
from pandas.errors import (
28+
AbstractMethodError,
29+
Pandas4Warning,
30+
)
2831
from pandas.util._decorators import doc
32+
from pandas.util._exceptions import find_stack_level
2933

3034
from pandas.core.dtypes.base import ExtensionDtype
3135
from pandas.core.dtypes.cast import maybe_downcast_to_dtype
@@ -743,6 +747,18 @@ def _arith_method(self, other, op):
743747
op_name = op.__name__
744748
omask = None
745749

750+
if is_list_like(other) and not isinstance(
751+
other, (list, np.ndarray, ExtensionArray)
752+
):
753+
warnings.warn(
754+
f"Operation with {type(other).__name__} are deprecated. "
755+
"In a future version these will be treated as scalar-like. "
756+
"To retain the old behavior, explicitly wrap in a Series "
757+
"instead.",
758+
Pandas4Warning,
759+
stacklevel=find_stack_level(),
760+
)
761+
746762
if (
747763
not hasattr(other, "dtype")
748764
and is_list_like(other)
@@ -847,6 +863,15 @@ def _cmp_method(self, other, op) -> BooleanArray:
847863
other, mask = other._data, other._mask
848864

849865
elif is_list_like(other):
866+
if not isinstance(other, (list, np.ndarray, ExtensionArray)):
867+
warnings.warn(
868+
f"Operation with {type(other).__name__} are deprecated. "
869+
"In a future version these will be treated as scalar-like. "
870+
"To retain the old behavior, explicitly wrap in a Series "
871+
"instead.",
872+
Pandas4Warning,
873+
stacklevel=find_stack_level(),
874+
)
850875
other = np.asarray(other)
851876
if other.ndim > 1:
852877
raise NotImplementedError("can only perform ops with 1-d structures")

pandas/core/arrays/sparse/array.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
)
3131
from pandas._libs.tslibs import NaT
3232
from pandas.compat.numpy import function as nv
33-
from pandas.errors import PerformanceWarning
33+
from pandas.errors import (
34+
Pandas4Warning,
35+
PerformanceWarning,
36+
)
3437
from pandas.util._decorators import doc
3538
from pandas.util._exceptions import find_stack_level
3639
from pandas.util._validators import (
@@ -1805,6 +1808,16 @@ def _arith_method(self, other, op):
18051808
return _wrap_result(op_name, result, self.sp_index, fill)
18061809

18071810
else:
1811+
if not isinstance(other, (list, np.ndarray, ExtensionArray)):
1812+
warnings.warn(
1813+
f"Operation with {type(other).__name__} are deprecated. "
1814+
"In a future version these will be treated as scalar-like. "
1815+
"To retain the old behavior, explicitly wrap in a Series "
1816+
"instead.",
1817+
Pandas4Warning,
1818+
stacklevel=find_stack_level(),
1819+
)
1820+
18081821
other = np.asarray(other)
18091822
with np.errstate(all="ignore"):
18101823
if len(self) != len(other):
@@ -1817,6 +1830,17 @@ def _arith_method(self, other, op):
18171830
return _sparse_array_op(self, other, op, op_name)
18181831

18191832
def _cmp_method(self, other, op) -> SparseArray:
1833+
if is_list_like(other) and not isinstance(
1834+
other, (list, np.ndarray, ExtensionArray)
1835+
):
1836+
warnings.warn(
1837+
f"Operation with {type(other).__name__} are deprecated. "
1838+
"In a future version these will be treated as scalar-like. "
1839+
"To retain the old behavior, explicitly wrap in a Series "
1840+
"instead.",
1841+
Pandas4Warning,
1842+
stacklevel=find_stack_level(),
1843+
)
18201844
if not is_scalar(other) and not isinstance(other, type(self)):
18211845
# convert list-like to ndarray
18221846
other = np.asarray(other)

pandas/core/arrays/string_.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,15 @@ def _cmp_method(self, other, op):
11021102
valid = ~mask
11031103

11041104
if lib.is_list_like(other):
1105+
if not isinstance(other, (list, ExtensionArray, np.ndarray)):
1106+
warnings.warn(
1107+
f"Operation with {type(other).__name__} are deprecated. "
1108+
"In a future version these will be treated as scalar-like. "
1109+
"To retain the old behavior, explicitly wrap in a Series "
1110+
"instead.",
1111+
Pandas4Warning,
1112+
stacklevel=find_stack_level(),
1113+
)
11051114
if len(other) != len(self):
11061115
# prevent improper broadcasting when other is 2D
11071116
raise ValueError(

pandas/core/frame.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8430,6 +8430,16 @@ def to_series(right):
84308430
)
84318431

84328432
elif is_list_like(right) and not isinstance(right, (Series, DataFrame)):
8433+
if not isinstance(right, (np.ndarray, ExtensionArray, Index, list)):
8434+
warnings.warn(
8435+
f"Operation with {type(right).__name__} are deprecated. "
8436+
"In a future version these will be treated as scalar-like. "
8437+
"To retain the old behavior, explicitly wrap in a Series "
8438+
"instead.",
8439+
Pandas4Warning,
8440+
stacklevel=find_stack_level(),
8441+
)
8442+
84338443
# GH#36702. Raise when attempting arithmetic with list of array-like.
84348444
if any(is_array_like(el) for el in right):
84358445
raise ValueError(

pandas/core/series.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6035,6 +6035,15 @@ def _flex_method(self, other, op, *, level=None, fill_value=None, axis: Axis = 0
60356035
if isinstance(other, Series):
60366036
return self._binop(other, op, level=level, fill_value=fill_value)
60376037
elif isinstance(other, (np.ndarray, list, tuple)):
6038+
if isinstance(other, tuple):
6039+
op_name = op.__name__.strip("_")
6040+
warnings.warn(
6041+
f"Series.{op_name} with a tuple is deprecated and will be "
6042+
"treated as scalar-like in a future version. "
6043+
"Explicitly wrap in a numpy array instead.",
6044+
Pandas4Warning,
6045+
stacklevel=find_stack_level(),
6046+
)
60386047
if len(other) != len(self):
60396048
raise ValueError("Lengths must be equal")
60406049
other = self._constructor(other, self.index, copy=False)

0 commit comments

Comments
 (0)