diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 6fd758abb1f33d..5c31a9e932d41f 100755 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -486,6 +486,7 @@ Documentation Improvements Deprecations ~~~~~~~~~~~~ +- :meth:`Series.item` and :meth:`Index.item` have been _undeprecated_ (:issue:`29250`) - ``Index.set_value`` has been deprecated. For a given index ``idx``, array ``arr``, value in ``idx`` of ``idx_val`` and a new value of ``val``, ``idx.set_value(arr, idx_val, val)`` is equivalent to ``arr[idx.get_loc(idx_val)] = val``, which should be used instead (:issue:`28621`). @@ -703,6 +704,8 @@ Datetimelike - Bug in :attr:`Timestamp.resolution` being a property instead of a class attribute (:issue:`29910`) - Bug in :func:`pandas.to_datetime` when called with ``None`` raising ``TypeError`` instead of returning ``NaT`` (:issue:`30011`) - Bug in :func:`pandas.to_datetime` failing for `deques` when using ``cache=True`` (the default) (:issue:`29403`) +- Bug in :meth:`Series.item` with ``datetime64`` or ``timedelta64`` dtype, :meth:`DatetimeIndex.item`, and :meth:`TimedeltaIndex.item` returning an integer instead of a :class:`Timestamp` or :class:`Timedelta` (:issue:`30175`) +- Timedelta ^^^^^^^^^ diff --git a/pandas/core/base.py b/pandas/core/base.py index c900a0ba722c05..381d45d829e620 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -5,7 +5,6 @@ from collections import OrderedDict import textwrap from typing import Dict, FrozenSet, List, Optional -import warnings import numpy as np @@ -26,6 +25,7 @@ is_object_dtype, is_scalar, is_timedelta64_ns_dtype, + needs_i8_conversion, ) from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries from pandas.core.dtypes.missing import isna @@ -659,19 +659,27 @@ def item(self): """ Return the first element of the underlying data as a python scalar. - .. deprecated:: 0.25.0 - Returns ------- scalar The first element of %(klass)s. + + Raises + ------ + ValueError + If the data is not length-1. """ - warnings.warn( - "`item` has been deprecated and will be removed in a future version", - FutureWarning, - stacklevel=2, - ) - return self.values.item() + if not ( + is_extension_array_dtype(self.dtype) or needs_i8_conversion(self.dtype) + ): + # numpy returns ints instead of datetime64/timedelta64 objects, + # which we need to wrap in Timestamp/Timedelta/Period regardless. + return self.values.item() + + if len(self) == 1: + return next(iter(self)) + else: + raise ValueError("can only convert an array of size 1 to a Python scalar") @property def nbytes(self): diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 979ab275f64f2f..9485116a8084a2 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -import warnings import weakref import numpy as np @@ -862,27 +861,6 @@ def __setstate__(self, state): _unpickle_compat = __setstate__ - def item(self): - """ - Return the first element of the underlying data as a python - scalar - - .. deprecated:: 0.25.0 - - """ - warnings.warn( - "`item` has been deprecated and will be removed in a future version", - FutureWarning, - stacklevel=2, - ) - # TODO(DatetimeArray): remove - if len(self) == 1: - return self[0] - else: - # TODO: is this still necessary? - # copy numpy's message here because Py26 raises an IndexError - raise ValueError("can only convert an array of size 1 to a Python scalar") - def memory_usage(self, deep=False): result = super().memory_usage(deep=deep) if hasattr(self, "_cache") and "_int64index" in self._cache: diff --git a/pandas/tests/base/test_ops.py b/pandas/tests/base/test_ops.py index bcd6b931a0f85d..04277ce929bca0 100644 --- a/pandas/tests/base/test_ops.py +++ b/pandas/tests/base/test_ops.py @@ -236,15 +236,13 @@ def test_ndarray_compat_properties(self): assert not hasattr(o, p) with pytest.raises(ValueError): - with tm.assert_produces_warning(FutureWarning): - o.item() # len > 1 + o.item() # len > 1 assert o.ndim == 1 assert o.size == len(o) - with tm.assert_produces_warning(FutureWarning): - assert Index([1]).item() == 1 - assert Series([1]).item() == 1 + assert Index([1]).item() == 1 + assert Series([1]).item() == 1 def test_value_counts_unique_nunique(self): for orig in self.objs: diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index 5da0ee9b5b1c0b..f8cf6b6a54d143 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -12,13 +12,14 @@ DatetimeIndex, Index, Series, + Timedelta, TimedeltaIndex, + Timestamp, date_range, period_range, timedelta_range, ) from pandas.core.arrays import PeriodArray -from pandas.core.indexes.datetimes import Timestamp import pandas.util.testing as tm import pandas.io.formats.printing as printing @@ -398,6 +399,50 @@ def test_numpy_unique(self, datetime_series): # it works! np.unique(datetime_series) + def test_item(self): + s = Series([1]) + result = s.item() + assert result == 1 + assert result == s.iloc[0] + assert isinstance(result, int) # i.e. not np.int64 + + ser = Series([0.5], index=[3]) + result = ser.item() + assert isinstance(result, float) + assert result == 0.5 + + ser = Series([1, 2]) + msg = "can only convert an array of size 1" + with pytest.raises(ValueError, match=msg): + ser.item() + + dti = pd.date_range("2016-01-01", periods=2) + with pytest.raises(ValueError, match=msg): + dti.item() + with pytest.raises(ValueError, match=msg): + Series(dti).item() + + val = dti[:1].item() + assert isinstance(val, Timestamp) + val = Series(dti)[:1].item() + assert isinstance(val, Timestamp) + + tdi = dti - dti + with pytest.raises(ValueError, match=msg): + tdi.item() + with pytest.raises(ValueError, match=msg): + Series(tdi).item() + + val = tdi[:1].item() + assert isinstance(val, Timedelta) + val = Series(tdi)[:1].item() + assert isinstance(val, Timedelta) + + # Case where ser[0] would not work + ser = Series(dti, index=[5, 6]) + val = ser[:1].item() + assert val == dti[0] + def test_ndarray_compat(self): # test numpy compat with Series as sub-class of NDFrame @@ -414,13 +459,6 @@ def f(x): expected = tsdf.max() tm.assert_series_equal(result, expected) - # .item() - with tm.assert_produces_warning(FutureWarning): - s = Series([1]) - result = s.item() - assert result == 1 - assert s.item() == s.iloc[0] - # using an ndarray like function s = Series(np.random.randn(10)) result = Series(np.ones_like(s))