Skip to content

Commit d74cb77

Browse files
authored
BUG: Series.view from int64 to datetimelike (#44581)
1 parent 96eb4c9 commit d74cb77

File tree

5 files changed

+67
-41
lines changed

5 files changed

+67
-41
lines changed

pandas/core/arrays/_mixins.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from pandas._libs import lib
1717
from pandas._libs.arrays import NDArrayBacked
1818
from pandas._typing import (
19+
ArrayLike,
20+
Dtype,
1921
F,
2022
PositionalIndexer2D,
2123
PositionalIndexerTuple,
@@ -34,8 +36,15 @@
3436
validate_insert_loc,
3537
)
3638

37-
from pandas.core.dtypes.common import is_dtype_equal
38-
from pandas.core.dtypes.dtypes import ExtensionDtype
39+
from pandas.core.dtypes.common import (
40+
is_dtype_equal,
41+
pandas_dtype,
42+
)
43+
from pandas.core.dtypes.dtypes import (
44+
DatetimeTZDtype,
45+
ExtensionDtype,
46+
PeriodDtype,
47+
)
3948
from pandas.core.dtypes.missing import array_equivalent
4049

4150
from pandas.core import missing
@@ -101,6 +110,41 @@ def _validate_scalar(self, value):
101110

102111
# ------------------------------------------------------------------------
103112

113+
def view(self, dtype: Dtype | None = None) -> ArrayLike:
114+
# We handle datetime64, datetime64tz, timedelta64, and period
115+
# dtypes here. Everything else we pass through to the underlying
116+
# ndarray.
117+
if dtype is None or dtype is self.dtype:
118+
return self._from_backing_data(self._ndarray)
119+
120+
if isinstance(dtype, type):
121+
# we sometimes pass non-dtype objects, e.g np.ndarray;
122+
# pass those through to the underlying ndarray
123+
return self._ndarray.view(dtype)
124+
125+
dtype = pandas_dtype(dtype)
126+
arr = self._ndarray
127+
128+
if isinstance(dtype, (PeriodDtype, DatetimeTZDtype)):
129+
cls = dtype.construct_array_type()
130+
return cls(arr.view("i8"), dtype=dtype)
131+
elif dtype == "M8[ns]":
132+
from pandas.core.arrays import DatetimeArray
133+
134+
return DatetimeArray(arr.view("i8"), dtype=dtype)
135+
elif dtype == "m8[ns]":
136+
from pandas.core.arrays import TimedeltaArray
137+
138+
return TimedeltaArray(arr.view("i8"), dtype=dtype)
139+
140+
# error: Incompatible return value type (got "ndarray", expected
141+
# "ExtensionArray")
142+
# error: Argument "dtype" to "view" of "_ArrayOrScalarCommon" has incompatible
143+
# type "Union[ExtensionDtype, dtype[Any]]"; expected "Union[dtype[Any], None,
144+
# type, _SupportsDType, str, Union[Tuple[Any, int], Tuple[Any, Union[int,
145+
# Sequence[int]]], List[Any], _DTypeDict, Tuple[Any, Any]]]"
146+
return arr.view(dtype=dtype) # type: ignore[return-value,arg-type]
147+
104148
def take(
105149
self: NDArrayBackedExtensionArrayT,
106150
indices: TakeIndexer,

pandas/core/arrays/datetimelike.py

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,7 @@
8585
is_unsigned_integer_dtype,
8686
pandas_dtype,
8787
)
88-
from pandas.core.dtypes.dtypes import (
89-
DatetimeTZDtype,
90-
ExtensionDtype,
91-
PeriodDtype,
92-
)
88+
from pandas.core.dtypes.dtypes import ExtensionDtype
9389
from pandas.core.dtypes.missing import (
9490
is_valid_na_for_dtype,
9591
isna,
@@ -461,36 +457,9 @@ def view(self, dtype: Dtype | None = ...) -> ArrayLike:
461457
...
462458

463459
def view(self, dtype: Dtype | None = None) -> ArrayLike:
464-
# We handle datetime64, datetime64tz, timedelta64, and period
465-
# dtypes here. Everything else we pass through to the underlying
466-
# ndarray.
467-
if dtype is None or dtype is self.dtype:
468-
return type(self)(self._ndarray, dtype=self.dtype)
469-
470-
if isinstance(dtype, type):
471-
# we sometimes pass non-dtype objects, e.g np.ndarray;
472-
# pass those through to the underlying ndarray
473-
return self._ndarray.view(dtype)
474-
475-
dtype = pandas_dtype(dtype)
476-
if isinstance(dtype, (PeriodDtype, DatetimeTZDtype)):
477-
cls = dtype.construct_array_type()
478-
return cls(self.asi8, dtype=dtype)
479-
elif dtype == "M8[ns]":
480-
from pandas.core.arrays import DatetimeArray
481-
482-
return DatetimeArray(self.asi8, dtype=dtype)
483-
elif dtype == "m8[ns]":
484-
from pandas.core.arrays import TimedeltaArray
485-
486-
return TimedeltaArray(self.asi8, dtype=dtype)
487-
# error: Incompatible return value type (got "ndarray", expected
488-
# "ExtensionArray")
489-
# error: Argument "dtype" to "view" of "_ArrayOrScalarCommon" has incompatible
490-
# type "Union[ExtensionDtype, dtype[Any]]"; expected "Union[dtype[Any], None,
491-
# type, _SupportsDType, str, Union[Tuple[Any, int], Tuple[Any, Union[int,
492-
# Sequence[int]]], List[Any], _DTypeDict, Tuple[Any, Any]]]"
493-
return self._ndarray.view(dtype=dtype) # type: ignore[return-value,arg-type]
460+
# we need to explicitly call super() method as long as the `@overload`s
461+
# are present in this file.
462+
return super().view(dtype)
494463

495464
# ------------------------------------------------------------------
496465
# ExtensionArray Interface

pandas/core/indexes/base.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,6 @@ def view(self, cls=None):
947947
# e.g. m8[s]
948948
return self._data.view(cls)
949949

950-
arr = self._data.view("i8")
951950
idx_cls = self._dtype_to_subclass(dtype)
952951
arr_cls = idx_cls._data_cls
953952
arr = arr_cls(self._data.view("i8"), dtype=dtype)

pandas/core/series.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -802,9 +802,11 @@ def view(self, dtype: Dtype | None = None) -> Series:
802802
4 2
803803
dtype: int8
804804
"""
805-
return self._constructor(
806-
self._values.view(dtype), index=self.index
807-
).__finalize__(self, method="view")
805+
# self.array instead of self._values so we piggyback on PandasArray
806+
# implementation
807+
res_values = self.array.view(dtype)
808+
res_ser = self._constructor(res_values, index=self.index)
809+
return res_ser.__finalize__(self, method="view")
808810

809811
# ----------------------------------------------------------------------
810812
# NDArray Compat

pandas/tests/series/methods/test_view.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111

1212

1313
class TestView:
14+
def test_view_i8_to_datetimelike(self):
15+
dti = date_range("2000", periods=4, tz="US/Central")
16+
ser = Series(dti.asi8)
17+
18+
result = ser.view(dti.dtype)
19+
tm.assert_datetime_array_equal(result._values, dti._data._with_freq(None))
20+
21+
pi = dti.tz_localize(None).to_period("D")
22+
ser = Series(pi.asi8)
23+
result = ser.view(pi.dtype)
24+
tm.assert_period_array_equal(result._values, pi._data)
25+
1426
def test_view_tz(self):
1527
# GH#24024
1628
ser = Series(date_range("2000", periods=4, tz="US/Central"))

0 commit comments

Comments
 (0)