From 35908bbeccd3fd96295258d9fa4a60991f51d0de Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 9 Jan 2021 23:17:58 -0800 Subject: [PATCH 1/2] BUG: setting td64 value into Series[numeric] --- pandas/core/dtypes/cast.py | 12 +++++----- pandas/core/internals/blocks.py | 14 ++++-------- pandas/tests/series/indexing/test_setitem.py | 24 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index b1b7c28c04ebd..6c8711d940558 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1874,9 +1874,9 @@ def validate_numeric_casting(dtype: np.dtype, value: Scalar) -> None: ): raise ValueError("Cannot assign nan to integer series") - if ( - issubclass(dtype.type, (np.integer, np.floating, complex)) - and not issubclass(dtype.type, np.bool_) - and is_bool(value) - ): - raise ValueError("Cannot assign bool to float/integer series") + if dtype.kind in ["i", "u", "f", "c"]: + if is_bool(value) or isinstance(value, np.timedelta64): + # numpy will cast td64 to integer if we're not careful + raise ValueError( + f"Cannot assign {type(value).__name__} to float/integer series" + ) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 653b5ccf5f1ba..bb83c4543a989 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -678,11 +678,7 @@ def convert( def _can_hold_element(self, element: Any) -> bool: """ require the same dtype as ourselves """ - dtype = self.values.dtype.type - tipo = maybe_infer_dtype_type(element) - if tipo is not None: - return issubclass(tipo.type, dtype) - return isinstance(element, dtype) + raise NotImplementedError("Implemented on subclasses") def should_store(self, value: ArrayLike) -> bool: """ @@ -1969,10 +1965,10 @@ class ComplexBlock(NumericBlock): def _can_hold_element(self, element: Any) -> bool: tipo = maybe_infer_dtype_type(element) if tipo is not None: - return issubclass(tipo.type, (np.floating, np.integer, np.complexfloating)) - return isinstance( - element, (float, int, complex, np.float_, np.int_) - ) and not isinstance(element, (bool, np.bool_)) + return tipo.kind in ["c", "f", "i", "u"] + return ( + lib.is_integer(element) or lib.is_complex(element) or lib.is_float(element) + ) class IntBlock(NumericBlock): diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 8e4a6d50f5070..7f469f361fec7 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -395,3 +395,27 @@ def test_setitem_slice_into_readonly_backing_data(): series[1:3] = 1 assert not array.any() + + +@pytest.mark.parametrize( + "key", [0, slice(0, 1), [0], np.array([0]), range(1)], ids=type +) +@pytest.mark.parametrize("dtype", [complex, int, float]) +def test_setitem_td64_into_complex(key, dtype, indexer_sli): + # timedelta64 should not be treated as integers + arr = np.arange(5).astype(dtype) + ser = Series(arr) + td = np.timedelta64(4, "ns") + + indexer_sli(ser)[key] = td + assert ser.dtype == object + assert arr[0] == 0 # original array is unchanged + + if not isinstance(key, int) and not ( + indexer_sli is tm.loc and isinstance(key, slice) + ): + # skip key/indexer_sli combinations that will have mismatched lengths + ser = Series(arr) + indexer_sli(ser)[key] = np.full((1,), td) + assert ser.dtype == object + assert arr[0] == 0 # original array is unchanged From 0838408094284ec52083dfbeaa7b50f06e410d0f Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 11 Jan 2021 08:15:23 -0800 Subject: [PATCH 2/2] whatsnew --- doc/source/whatsnew/v1.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 3d19f5ebe4381..e978bf102dedd 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -251,7 +251,7 @@ Indexing - Bug in :meth:`DataFrame.__setitem__` raising ``ValueError`` with empty :class:`DataFrame` and specified columns for string indexer and non empty :class:`DataFrame` to set (:issue:`38831`) - Bug in :meth:`DataFrame.iloc.__setitem__` and :meth:`DataFrame.loc.__setitem__` with mixed dtypes when setting with a dictionary value (:issue:`38335`) - Bug in :meth:`DataFrame.loc` dropping levels of :class:`MultiIndex` when :class:`DataFrame` used as input has only one row (:issue:`10521`) -- +- Bug in setting ``timedelta64`` values into numeric :class:`Series` failing to cast to object dtype (:issue:`39086`) Missing ^^^^^^^