From 0a7477bb1a45bfff7cb7d6a48986b6e16d6cb770 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 26 Oct 2023 19:14:05 -0700 Subject: [PATCH] BUG: Index.view with non-nano (#55710) * BUG: Index.view with non-nano * GH ref --- doc/source/whatsnew/v2.2.0.rst | 1 + pandas/core/arrays/_mixins.py | 22 +++++++++++--------- pandas/core/indexes/base.py | 17 +++++++-------- pandas/tests/indexes/numeric/test_numeric.py | 16 ++++++++++++++ 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index 55318623128fa..4ea5532736844 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -322,6 +322,7 @@ Datetimelike ^^^^^^^^^^^^ - Bug in :func:`concat` raising ``AttributeError`` when concatenating all-NA DataFrame with :class:`DatetimeTZDtype` dtype DataFrame. (:issue:`52093`) - Bug in :meth:`DatetimeIndex.union` returning object dtype for tz-aware indexes with the same timezone but different units (:issue:`55238`) +- Bug in :meth:`Index.view` to a datetime64 dtype with non-supported resolution incorrectly raising (:issue:`55710`) - Bug in :meth:`Tick.delta` with very large ticks raising ``OverflowError`` instead of ``OutOfBoundsTimedelta`` (:issue:`55503`) - Bug in adding or subtracting a :class:`Week` offset to a ``datetime64`` :class:`Series`, :class:`Index`, or :class:`DataFrame` column with non-nanosecond resolution returning incorrect results (:issue:`55583`) - Bug in addition or subtraction of :class:`BusinessDay` offset with ``offset`` attribute to non-nanosecond :class:`Index`, :class:`Series`, or :class:`DataFrame` column giving incorrect results (:issue:`55608`) diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index 8fb9e02cc34aa..d6f4dbfe7f549 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -13,6 +13,10 @@ from pandas._libs import lib from pandas._libs.arrays import NDArrayBacked +from pandas._libs.tslibs import ( + get_unit_from_dtype, + is_supported_unit, +) from pandas._typing import ( ArrayLike, AxisInt, @@ -137,21 +141,19 @@ def view(self, dtype: Dtype | None = None) -> ArrayLike: cls = dtype.construct_array_type() # type: ignore[assignment] dt64_values = arr.view(f"M8[{dtype.unit}]") return cls(dt64_values, dtype=dtype) - elif dtype == "M8[ns]": + elif lib.is_np_dtype(dtype, "M") and is_supported_unit( + get_unit_from_dtype(dtype) + ): from pandas.core.arrays import DatetimeArray - # error: Argument 1 to "view" of "ndarray" has incompatible type - # "ExtensionDtype | dtype[Any]"; expected "dtype[Any] | type[Any] - # | _SupportsDType[dtype[Any]]" - dt64_values = arr.view(dtype) # type: ignore[arg-type] + dt64_values = arr.view(dtype) return DatetimeArray(dt64_values, dtype=dtype) - elif dtype == "m8[ns]": + elif lib.is_np_dtype(dtype, "m") and is_supported_unit( + get_unit_from_dtype(dtype) + ): from pandas.core.arrays import TimedeltaArray - # error: Argument 1 to "view" of "ndarray" has incompatible type - # "ExtensionDtype | dtype[Any]"; expected "dtype[Any] | type[Any] - # | _SupportsDType[dtype[Any]]" - td64_values = arr.view(dtype) # type: ignore[arg-type] + td64_values = arr.view(dtype) return TimedeltaArray(td64_values, dtype=dtype) # error: Argument "dtype" to "view" of "_ArrayOrScalarCommon" has incompatible diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index da88c55ea814e..761f5df3bb4e0 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -999,17 +999,14 @@ def view(self, cls=None): dtype = pandas_dtype(cls) if needs_i8_conversion(dtype): - if dtype.kind == "m" and dtype != "m8[ns]": - # e.g. m8[s] - return self._data.view(cls) - idx_cls = self._dtype_to_subclass(dtype) - # NB: we only get here for subclasses that override - # _data_cls such that it is a type and not a tuple - # of types. - arr_cls = idx_cls._data_cls - arr = arr_cls(self._data.view("i8"), dtype=dtype) - return idx_cls._simple_new(arr, name=self.name, refs=self._references) + arr = self.array.view(dtype) + if isinstance(arr, ExtensionArray): + # here we exclude non-supported dt64/td64 dtypes + return idx_cls._simple_new( + arr, name=self.name, refs=self._references + ) + return arr result = self._data.view(cls) else: diff --git a/pandas/tests/indexes/numeric/test_numeric.py b/pandas/tests/indexes/numeric/test_numeric.py index 8cd295802a5d1..944e215ee17bd 100644 --- a/pandas/tests/indexes/numeric/test_numeric.py +++ b/pandas/tests/indexes/numeric/test_numeric.py @@ -527,3 +527,19 @@ def test_map_dtype_inference_overflows(): # TODO: we could plausibly try to infer down to int16 here expected = Index([1000, 2000, 3000], dtype=np.int64) tm.assert_index_equal(result, expected) + + +def test_view_to_datetimelike(): + # GH#55710 + idx = Index([1, 2, 3]) + res = idx.view("m8[s]") + expected = pd.TimedeltaIndex(idx.values.view("m8[s]")) + tm.assert_index_equal(res, expected) + + res2 = idx.view("m8[D]") + expected2 = idx.values.view("m8[D]") + tm.assert_numpy_array_equal(res2, expected2) + + res3 = idx.view("M8[h]") + expected3 = idx.values.view("M8[h]") + tm.assert_numpy_array_equal(res3, expected3)