Skip to content

Commit ca81e6c

Browse files
authored
BUG: FloatingArray * np.timedelta64 (#44772)
1 parent c1c202c commit ca81e6c

File tree

7 files changed

+54
-88
lines changed

7 files changed

+54
-88
lines changed

doc/source/whatsnew/v1.4.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ Numeric
644644
- Bug in arithmetic operations involving :class:`RangeIndex` where the result would have the incorrect ``name`` (:issue:`43962`)
645645
- Bug in arithmetic operations involving :class:`Series` where the result could have the incorrect ``name`` when the operands having matching NA or matching tuple names (:issue:`44459`)
646646
- Bug in division with ``IntegerDtype`` or ``BooleanDtype`` array and NA scalar incorrectly raising (:issue:`44685`)
647+
- Bug in multiplying a :class:`Series` with ``FloatingDtype`` with a timedelta-like scalar incorrectly raising (:issue:`44772`)
647648
-
648649

649650
Conversion

pandas/core/arrays/boolean.py

-31
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from pandas.core.dtypes.common import (
2525
is_bool_dtype,
26-
is_float,
2726
is_float_dtype,
2827
is_integer_dtype,
2928
is_list_like,
@@ -532,35 +531,5 @@ def _arith_method(self, other, op):
532531

533532
return self._maybe_mask_result(result, mask, other, op_name)
534533

535-
def _maybe_mask_result(self, result, mask, other, op_name: str):
536-
"""
537-
Parameters
538-
----------
539-
result : array-like
540-
mask : array-like bool
541-
other : scalar or array-like
542-
op_name : str
543-
"""
544-
# if we have a float operand we are by-definition
545-
# a float result
546-
# or our op is a divide
547-
if (is_float_dtype(other) or is_float(other)) or (
548-
op_name in ["rtruediv", "truediv"]
549-
):
550-
from pandas.core.arrays import FloatingArray
551-
552-
return FloatingArray(result, mask, copy=False)
553-
554-
elif is_bool_dtype(result):
555-
return BooleanArray(result, mask, copy=False)
556-
557-
elif is_integer_dtype(result):
558-
from pandas.core.arrays import IntegerArray
559-
560-
return IntegerArray(result, mask, copy=False)
561-
else:
562-
result[mask] = np.nan
563-
return result
564-
565534
def __abs__(self):
566535
return self.copy()

pandas/core/arrays/floating.py

-21
Original file line numberDiff line numberDiff line change
@@ -354,27 +354,6 @@ def max(self, *, skipna=True, axis: int | None = 0, **kwargs):
354354
nv.validate_max((), kwargs)
355355
return super()._reduce("max", skipna=skipna, axis=axis)
356356

357-
def _maybe_mask_result(self, result, mask, other, op_name: str):
358-
"""
359-
Parameters
360-
----------
361-
result : array-like
362-
mask : array-like bool
363-
other : scalar or array-like
364-
op_name : str
365-
"""
366-
# TODO are there cases we don't end up with float?
367-
# if we have a float operand we are by-definition
368-
# a float result
369-
# or our op is a divide
370-
# if (is_float_dtype(other) or is_float(other)) or (
371-
# op_name in ["rtruediv", "truediv"]
372-
# ):
373-
# result[mask] = np.nan
374-
# return result
375-
376-
return type(self)(result, mask, copy=False)
377-
378357

379358
_dtype_docstring = """
380359
An ExtensionDtype for {dtype} data.

pandas/core/arrays/integer.py

-29
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import numpy as np
66

77
from pandas._libs import (
8-
iNaT,
98
lib,
109
missing as libmissing,
1110
)
@@ -26,7 +25,6 @@
2625
from pandas.core.dtypes.common import (
2726
is_bool_dtype,
2827
is_datetime64_dtype,
29-
is_float,
3028
is_float_dtype,
3129
is_integer_dtype,
3230
is_object_dtype,
@@ -427,33 +425,6 @@ def max(self, *, skipna=True, axis: int | None = 0, **kwargs):
427425
nv.validate_max((), kwargs)
428426
return super()._reduce("max", skipna=skipna, axis=axis)
429427

430-
def _maybe_mask_result(self, result, mask, other, op_name: str):
431-
"""
432-
Parameters
433-
----------
434-
result : array-like
435-
mask : array-like bool
436-
other : scalar or array-like
437-
op_name : str
438-
"""
439-
# if we have a float operand we are by-definition
440-
# a float result
441-
# or our op is a divide
442-
if (is_float_dtype(other) or is_float(other)) or (
443-
op_name in ["rtruediv", "truediv"]
444-
):
445-
from pandas.core.arrays import FloatingArray
446-
447-
return FloatingArray(result, mask, copy=False)
448-
449-
if result.dtype == "timedelta64[ns]":
450-
from pandas.core.arrays import TimedeltaArray
451-
452-
result[mask] = iNaT
453-
return TimedeltaArray._simple_new(result)
454-
455-
return type(self)(result, mask, copy=False)
456-
457428

458429
_dtype_docstring = """
459430
An ExtensionDtype for {dtype} integer data.

pandas/core/arrays/masked.py

+45
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import numpy as np
1313

1414
from pandas._libs import (
15+
iNaT,
1516
lib,
1617
missing as libmissing,
1718
)
@@ -39,9 +40,11 @@
3940
is_bool,
4041
is_bool_dtype,
4142
is_dtype_equal,
43+
is_float,
4244
is_float_dtype,
4345
is_integer_dtype,
4446
is_list_like,
47+
is_numeric_dtype,
4548
is_object_dtype,
4649
is_scalar,
4750
is_string_dtype,
@@ -543,6 +546,48 @@ def _cmp_method(self, other, op) -> BooleanArray:
543546

544547
return BooleanArray(result, mask, copy=False)
545548

549+
def _maybe_mask_result(self, result, mask, other, op_name: str):
550+
"""
551+
Parameters
552+
----------
553+
result : array-like
554+
mask : array-like bool
555+
other : scalar or array-like
556+
op_name : str
557+
"""
558+
# if we have a float operand we are by-definition
559+
# a float result
560+
# or our op is a divide
561+
if (
562+
(is_float_dtype(other) or is_float(other))
563+
or (op_name in ["rtruediv", "truediv"])
564+
or (is_float_dtype(self.dtype) and is_numeric_dtype(result.dtype))
565+
):
566+
from pandas.core.arrays import FloatingArray
567+
568+
return FloatingArray(result, mask, copy=False)
569+
570+
elif is_bool_dtype(result):
571+
from pandas.core.arrays import BooleanArray
572+
573+
return BooleanArray(result, mask, copy=False)
574+
575+
elif result.dtype == "timedelta64[ns]":
576+
# e.g. test_numeric_arr_mul_tdscalar_numexpr_path
577+
from pandas.core.arrays import TimedeltaArray
578+
579+
result[mask] = iNaT
580+
return TimedeltaArray._simple_new(result)
581+
582+
elif is_integer_dtype(result):
583+
from pandas.core.arrays import IntegerArray
584+
585+
return IntegerArray(result, mask, copy=False)
586+
587+
else:
588+
result[mask] = np.nan
589+
return result
590+
546591
def isna(self) -> np.ndarray:
547592
return self._mask.copy()
548593

pandas/core/arrays/numeric.py

-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
missing as libmissing,
1515
)
1616
from pandas.compat.numpy import function as nv
17-
from pandas.errors import AbstractMethodError
1817

1918
from pandas.core.dtypes.common import (
2019
is_float,
@@ -80,9 +79,6 @@ class NumericArray(BaseMaskedArray):
8079
Base class for IntegerArray and FloatingArray.
8180
"""
8281

83-
def _maybe_mask_result(self, result, mask, other, op_name: str):
84-
raise AbstractMethodError(self)
85-
8682
def _arith_method(self, other, op):
8783
op_name = op.__name__
8884
omask = None

pandas/tests/arithmetic/test_numeric.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,18 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, numeric_idx, box_with_array):
213213
],
214214
ids=lambda x: type(x).__name__,
215215
)
216-
def test_numeric_arr_mul_tdscalar_numexpr_path(self, scalar_td, box_with_array):
216+
@pytest.mark.parametrize("dtype", [np.int64, np.float64])
217+
def test_numeric_arr_mul_tdscalar_numexpr_path(
218+
self, dtype, scalar_td, box_with_array
219+
):
220+
# GH#44772 for the float64 case
217221
box = box_with_array
218222

219-
arr = np.arange(2 * 10 ** 4).astype(np.int64)
223+
arr_i8 = np.arange(2 * 10 ** 4).astype(np.int64, copy=False)
224+
arr = arr_i8.astype(dtype, copy=False)
220225
obj = tm.box_expected(arr, box, transpose=False)
221226

222-
expected = arr.view("timedelta64[D]").astype("timedelta64[ns]")
227+
expected = arr_i8.view("timedelta64[D]").astype("timedelta64[ns]")
223228
expected = tm.box_expected(expected, box, transpose=False)
224229

225230
result = obj * scalar_td

0 commit comments

Comments
 (0)