Skip to content

Commit 4edcc55

Browse files
jbrockmendeljorisvandenbossche
authored andcommitted
CLN: Make Series._values match Index._values (#31182)
1 parent 0a099d8 commit 4edcc55

File tree

6 files changed

+33
-7
lines changed

6 files changed

+33
-7
lines changed

pandas/core/apply.py

+3
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ def apply_standard(self):
278278
if (
279279
self.result_type in ["reduce", None]
280280
and not self.dtypes.apply(is_extension_array_dtype).any()
281+
# Disallow dtypes where setting _index_data will break
282+
# ExtensionArray values, see GH#31182
283+
and not self.dtypes.apply(lambda x: x.kind in ["m", "M"]).any()
281284
# Disallow complex_internals since libreduction shortcut raises a TypeError
282285
and not self.agg_axis._has_complex_internals
283286
):

pandas/core/base.py

+4
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,10 @@ def unique(self):
12651265
if hasattr(values, "unique"):
12661266

12671267
result = values.unique()
1268+
if self.dtype.kind in ["m", "M"] and isinstance(self, ABCSeries):
1269+
# GH#31182 Series._values returns EA, unpack for backward-compat
1270+
if getattr(self.dtype, "tz", None) is None:
1271+
result = np.asarray(result)
12681272
else:
12691273
result = unique1d(values)
12701274

pandas/core/internals/blocks.py

+5
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,10 @@ def get_values(self, dtype=None):
21512151
return result.reshape(self.values.shape)
21522152
return self.values
21532153

2154+
def internal_values(self):
2155+
# Override to return DatetimeArray and TimedeltaArray
2156+
return self.array_values()
2157+
21542158

21552159
class DatetimeBlock(DatetimeLikeBlockMixin, Block):
21562160
__slots__ = ()
@@ -2284,6 +2288,7 @@ class DatetimeTZBlock(ExtensionBlock, DatetimeBlock):
22842288
is_datetimetz = True
22852289
is_extension = True
22862290

2291+
internal_values = Block.internal_values
22872292
_can_hold_element = DatetimeBlock._can_hold_element
22882293
to_native_types = DatetimeBlock.to_native_types
22892294
fill_value = np.datetime64("NaT", "ns")

pandas/core/series.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ def _values(self):
493493
"""
494494
Return the internal repr of this data (defined by Block.interval_values).
495495
This are the values as stored in the Block (ndarray or ExtensionArray
496-
depending on the Block class).
496+
depending on the Block class), with datetime64[ns] and timedelta64[ns]
497+
wrapped in ExtensionArrays to match Index._values behavior.
497498
498499
Differs from the public ``.values`` for certain data types, because of
499500
historical backwards compatibility of the public attribute (e.g. period
@@ -502,8 +503,9 @@ def _values(self):
502503
cases).
503504
504505
Differs from ``.array`` in that this still returns the numpy array if
505-
the Block is backed by a numpy array, while ``.array`` ensures to always
506-
return an ExtensionArray.
506+
the Block is backed by a numpy array (except for datetime64 and
507+
timedelta64 dtypes), while ``.array`` ensures to always return an
508+
ExtensionArray.
507509
508510
Differs from ``._ndarray_values``, as that ensures to always return a
509511
numpy array (it will call ``_ndarray_values`` on the ExtensionArray, if
@@ -515,8 +517,9 @@ def _values(self):
515517
----------- | ------------- | ------------- | ------------- | --------------- |
516518
Numeric | ndarray | ndarray | PandasArray | ndarray |
517519
Category | Categorical | Categorical | Categorical | ndarray[int] |
518-
dt64[ns] | ndarray[M8ns] | ndarray[M8ns] | DatetimeArray | ndarray[M8ns] |
520+
dt64[ns] | ndarray[M8ns] | DatetimeArray | DatetimeArray | ndarray[M8ns] |
519521
dt64[ns tz] | ndarray[M8ns] | DatetimeArray | DatetimeArray | ndarray[M8ns] |
522+
td64[ns] | ndarray[m8ns] | TimedeltaArray| ndarray[m8ns] | ndarray[m8ns] |
520523
Period | ndarray[obj] | PeriodArray | PeriodArray | ndarray[int] |
521524
Nullable | EA | EA | EA | ndarray |
522525

pandas/tests/indexes/datetimes/test_tools.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,7 @@ def test_string_na_nat_conversion(self, cache):
15831583
for i in range(5):
15841584
x = series[i]
15851585
if isna(x):
1586-
expected[i] = iNaT
1586+
expected[i] = pd.NaT
15871587
else:
15881588
expected[i] = to_datetime(x, cache=cache)
15891589

pandas/tests/reductions/test_reductions.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,12 @@ def test_invalid_td64_reductions(self, opname):
316316
)
317317
td = s.diff()
318318

319-
msg = "reduction operation '{op}' not allowed for this dtype"
319+
msg = "|".join(
320+
[
321+
"reduction operation '{op}' not allowed for this dtype",
322+
r"cannot perform {op} with type timedelta64\[ns\]",
323+
]
324+
)
320325
msg = msg.format(op=opname)
321326

322327
with pytest.raises(TypeError, match=msg):
@@ -648,7 +653,13 @@ def test_ops_consistency_on_empty(self, method):
648653
# timedelta64[ns]
649654
tdser = Series([], dtype="m8[ns]")
650655
if method == "var":
651-
with pytest.raises(TypeError, match="operation 'var' not allowed"):
656+
msg = "|".join(
657+
[
658+
"operation 'var' not allowed",
659+
r"cannot perform var with type timedelta64\[ns\]",
660+
]
661+
)
662+
with pytest.raises(TypeError, match=msg):
652663
getattr(tdser, method)()
653664
else:
654665
result = getattr(tdser, method)()

0 commit comments

Comments
 (0)