Skip to content

Commit 8cfacef

Browse files
Merge branch 'pandas-dev:main' into ordered_cat_corr
2 parents ebfc3b0 + 9f66b81 commit 8cfacef

File tree

4 files changed

+41
-10
lines changed

4 files changed

+41
-10
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,7 @@ Timedelta
10181018
- Bug in :class:`Timedelta` constructor failing to raise when passed an invalid keyword (:issue:`53801`)
10191019
- Bug in :meth:`DataFrame.cumsum` which was raising ``IndexError`` if dtype is ``timedelta64[ns]`` (:issue:`57956`)
10201020
- Bug in multiplication operations with ``timedelta64`` dtype failing to raise ``TypeError`` when multiplying by ``bool`` objects or dtypes (:issue:`58054`)
1021+
- Bug in multiplication operations with ``timedelta64`` dtype incorrectly raising when multiplying by numpy-nullable dtypes or pyarrow integer dtypes (:issue:`58054`)
10211022

10221023
Timezones
10231024
^^^^^^^^^

pandas/core/arrays/numeric.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,9 @@ def _safe_cast(cls, values: np.ndarray, dtype: np.dtype, copy: bool) -> np.ndarr
139139
raise AbstractMethodError(cls)
140140

141141

142-
def _coerce_to_data_and_mask(
143-
values, dtype, copy: bool, dtype_cls: type[NumericDtype], default_dtype: np.dtype
144-
):
142+
def _coerce_to_data_and_mask(values, dtype, copy: bool, dtype_cls: type[NumericDtype]):
145143
checker = dtype_cls._checker
144+
default_dtype = dtype_cls._default_np_dtype
146145

147146
mask = None
148147
inferred_type = None
@@ -163,7 +162,7 @@ def _coerce_to_data_and_mask(
163162
if copy:
164163
values = values.copy()
165164
mask = mask.copy()
166-
return values, mask, dtype, inferred_type
165+
return values, mask
167166

168167
original = values
169168
if not copy:
@@ -174,6 +173,7 @@ def _coerce_to_data_and_mask(
174173
if values.dtype == object or is_string_dtype(values.dtype):
175174
inferred_type = lib.infer_dtype(values, skipna=True)
176175
if inferred_type == "boolean" and dtype is None:
176+
# object dtype array of bools
177177
name = dtype_cls.__name__.strip("_")
178178
raise TypeError(f"{values.dtype} cannot be converted to {name}")
179179

@@ -252,7 +252,7 @@ def _coerce_to_data_and_mask(
252252
values = values.astype(dtype, copy=copy)
253253
else:
254254
values = dtype_cls._safe_cast(values, dtype, copy=False)
255-
return values, mask, dtype, inferred_type
255+
return values, mask
256256

257257

258258
class NumericArray(BaseMaskedArray):
@@ -296,10 +296,7 @@ def _coerce_to_array(
296296
cls, value, *, dtype: DtypeObj, copy: bool = False
297297
) -> tuple[np.ndarray, np.ndarray]:
298298
dtype_cls = cls._dtype_cls
299-
default_dtype = dtype_cls._default_np_dtype
300-
values, mask, _, _ = _coerce_to_data_and_mask(
301-
value, dtype, copy, dtype_cls, default_dtype
302-
)
299+
values, mask = _coerce_to_data_and_mask(value, dtype, copy, dtype_cls)
303300
return values, mask
304301

305302
@classmethod

pandas/core/arrays/timedeltas.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@
5151
is_string_dtype,
5252
pandas_dtype,
5353
)
54-
from pandas.core.dtypes.dtypes import ExtensionDtype
54+
from pandas.core.dtypes.dtypes import (
55+
ArrowDtype,
56+
BaseMaskedDtype,
57+
ExtensionDtype,
58+
)
5559
from pandas.core.dtypes.missing import isna
5660

5761
from pandas.core import (
@@ -501,6 +505,10 @@ def __mul__(self, other) -> Self:
501505
f"Cannot multiply '{self.dtype}' by bool, explicitly cast to "
502506
"integers instead"
503507
)
508+
if isinstance(other.dtype, (ArrowDtype, BaseMaskedDtype)):
509+
# GH#58054
510+
return NotImplemented
511+
504512
if len(other) != len(self) and not lib.is_np_dtype(other.dtype, "m"):
505513
# Exclude timedelta64 here so we correctly raise TypeError
506514
# for that instead of ValueError

pandas/tests/arithmetic/test_timedelta64.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,31 @@ def test_td64arr_mul_bool_raises(self, dtype, box_with_array):
16021602
with pytest.raises(TypeError, match=msg2):
16031603
other * obj
16041604

1605+
@pytest.mark.parametrize(
1606+
"dtype",
1607+
[
1608+
"Int64",
1609+
"Float64",
1610+
pytest.param("int64[pyarrow]", marks=td.skip_if_no("pyarrow")),
1611+
],
1612+
)
1613+
def test_td64arr_mul_masked(self, dtype, box_with_array):
1614+
ser = Series(np.arange(5) * timedelta(hours=1))
1615+
obj = tm.box_expected(ser, box_with_array)
1616+
1617+
other = Series(np.arange(5), dtype=dtype)
1618+
other = tm.box_expected(other, box_with_array)
1619+
1620+
expected = Series([Timedelta(hours=n**2) for n in range(5)])
1621+
expected = tm.box_expected(expected, box_with_array)
1622+
if dtype == "int64[pyarrow]":
1623+
expected = expected.astype("duration[ns][pyarrow]")
1624+
1625+
result = obj * other
1626+
tm.assert_equal(result, expected)
1627+
result = other * obj
1628+
tm.assert_equal(result, expected)
1629+
16051630
# ------------------------------------------------------------------
16061631
# __div__, __rdiv__
16071632

0 commit comments

Comments
 (0)