From 8c3d0bacee126b51621bb9bcf79a8a3a495b634c Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 15 Dec 2020 14:31:54 -0800 Subject: [PATCH] BUG: .item() incorrectly casts td64/dt64 to int --- pandas/core/dtypes/cast.py | 2 +- pandas/core/tools/timedeltas.py | 3 ++- pandas/tests/dtypes/cast/test_infer_dtype.py | 20 +++++++++++++++++++- pandas/tests/tools/test_to_timedelta.py | 17 +++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 63445d0e1598d..29d3130aa3316 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -733,7 +733,7 @@ def infer_dtype_from_scalar(val, pandas_dtype: bool = False) -> Tuple[DtypeObj, raise ValueError(msg) dtype = val.dtype - val = val.item() + val = lib.item_from_zerodim(val) elif isinstance(val, str): diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 5a69f5e4b012f..0a274dcfd1d73 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -4,6 +4,7 @@ import numpy as np +from pandas._libs import lib from pandas._libs.tslibs import NaT from pandas._libs.tslibs.timedeltas import Timedelta, parse_timedelta_unit @@ -117,7 +118,7 @@ def to_timedelta(arg, unit=None, errors="raise"): return _convert_listlike(arg, unit=unit, errors=errors, name=arg.name) elif isinstance(arg, np.ndarray) and arg.ndim == 0: # extract array scalar and process below - arg = arg.item() + arg = lib.item_from_zerodim(arg) elif is_list_like(arg) and getattr(arg, "ndim", 1) == 1: return _convert_listlike(arg, unit=unit, errors=errors) elif getattr(arg, "ndim", 1) > 1: diff --git a/pandas/tests/dtypes/cast/test_infer_dtype.py b/pandas/tests/dtypes/cast/test_infer_dtype.py index 65da8985843f9..16fb6f327a4d5 100644 --- a/pandas/tests/dtypes/cast/test_infer_dtype.py +++ b/pandas/tests/dtypes/cast/test_infer_dtype.py @@ -3,7 +3,11 @@ import numpy as np import pytest -from pandas.core.dtypes.cast import infer_dtype_from_array, infer_dtype_from_scalar +from pandas.core.dtypes.cast import ( + infer_dtype_from, + infer_dtype_from_array, + infer_dtype_from_scalar, +) from pandas.core.dtypes.common import is_dtype_equal from pandas import ( @@ -171,3 +175,17 @@ def test_infer_dtype_from_scalar_errors(): def test_infer_dtype_from_array(arr, expected, pandas_dtype): dtype, _ = infer_dtype_from_array(arr, pandas_dtype=pandas_dtype) assert is_dtype_equal(dtype, expected) + + +@pytest.mark.parametrize("cls", [np.datetime64, np.timedelta64]) +def test_infer_dtype_from_scalar_zerodim_datetimelike(cls): + # ndarray.item() can incorrectly return int instead of td64/dt64 + val = cls(1234, "ns") + arr = np.array(val) + + dtype, res = infer_dtype_from_scalar(arr) + assert dtype.type is cls + assert isinstance(res, cls) + + dtype, res = infer_dtype_from(arr) + assert dtype.type is cls diff --git a/pandas/tests/tools/test_to_timedelta.py b/pandas/tests/tools/test_to_timedelta.py index 585ad4a7fab51..c0196549cee33 100644 --- a/pandas/tests/tools/test_to_timedelta.py +++ b/pandas/tests/tools/test_to_timedelta.py @@ -227,3 +227,20 @@ def test_to_timedelta_precision_over_nanos(self, input, expected, func): expected = pd.Timedelta(expected) result = func(input) assert result == expected + + def test_to_timedelta_zerodim(self): + # ndarray.item() incorrectly returns int for dt64[ns] and td64[ns] + dt64 = pd.Timestamp.now().to_datetime64() + arg = np.array(dt64) + + msg = ( + "Value must be Timedelta, string, integer, float, timedelta " + "or convertible, not datetime64" + ) + with pytest.raises(ValueError, match=msg): + to_timedelta(arg) + + arg2 = arg.view("m8[ns]") + result = to_timedelta(arg2) + assert isinstance(result, pd.Timedelta) + assert result.value == dt64.view("i8")