diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 7823f74b7a153..48c06dc049e4e 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -365,6 +365,7 @@ Timedelta ^^^^^^^^^ - Accuracy improvement in :meth:`Timedelta.to_pytimedelta` to round microseconds consistently for large nanosecond based Timedelta (:issue:`57841`) - Bug in :meth:`DataFrame.cumsum` which was raising ``IndexError`` if dtype is ``timedelta64[ns]`` (:issue:`57956`) +- Bug in :meth:`TimedeltaArray._simple_new` which was raising ``AssertionError`` When multiplying a Series with a timedelta64 dtype with another Series that uses any of the pandas nullable dtypes ``('Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'Float32', 'Float64', or 'boolean')`` (:issue:`58054`) Timezones ^^^^^^^^^ diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 6eb4d234b349d..4ae8da889ad1d 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -41,8 +41,10 @@ from pandas.core.dtypes.common import ( TD64NS_DTYPE, + is_bool_dtype, is_float_dtype, is_integer_dtype, + is_numeric_dtype, is_object_dtype, is_scalar, is_string_dtype, @@ -492,6 +494,16 @@ def __mul__(self, other) -> Self: result = np.array(result) return type(self)._simple_new(result, dtype=result.dtype) + if is_bool_dtype(other.dtype) or is_numeric_dtype(other.dtype): + # this multiplication will succeed only if all elements of other + # are any of the pandas nullable dtypes ('Int8', 'Int16', 'Int32', + # 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'Float32', + # 'Float64', or 'boolean'), so we will end up with + # timedelta64[ns]-dtyped result + result = [self._ndarray[n] * other[n] for n in range(len(self))] + result = np.array(result) + return type(self)(result) + # numpy will accept float or int dtype, raise TypeError for others result = self._ndarray * other return type(self)._simple_new(result, dtype=result.dtype) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 44bf3475b85a6..bbd1bc001fc16 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -368,6 +368,45 @@ def test_add_list_to_masked_array_boolean(self, request): result = [True, None, True] + ser tm.assert_series_equal(result, expected) + def test_mul_nullable_dtype(self): + # GH#58054. Multiplying a TimeDelta Series with another series containing + # any of the Pandas nullable dtypes should work the same as with the + # Numpy nullable dtypes + td_series = Series([timedelta(hours=1)]) + other = Series([True]) + + pandas_types = [ + "Int8", + "Int16", + "Int32", + "Int64", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "Float32", + "Float64", + "boolean", + ] + numpy_types = [ + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float32", + "float64", + "bool", + ] + + for dtype1, dtype2 in zip(pandas_types, numpy_types): + result = td_series * other.astype(dtype1) + expected = td_series * other.astype(dtype2) + tm.assert_series_equal(result, expected) + # ------------------------------------------------------------------ # Comparisons