Skip to content

Commit 6a2f95b

Browse files
jbrockmendeljreback
authored andcommitted
REF: require PeriodArray in PeriodIndex._simple_new (#31128)
1 parent 71b6833 commit 6a2f95b

File tree

4 files changed

+44
-40
lines changed

4 files changed

+44
-40
lines changed

pandas/core/indexes/datetimelike.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,21 @@ def sort_values(self, return_indexer=False, ascending=True):
193193
# because the treatment of NaT has been changed to put NaT last
194194
# instead of first.
195195
sorted_values = np.sort(self.asi8)
196-
attribs = self._get_attributes_dict()
197-
freq = attribs["freq"]
198196

197+
freq = self.freq
199198
if freq is not None and not is_period_dtype(self):
200199
if freq.n > 0 and not ascending:
201200
freq = freq * -1
202201
elif freq.n < 0 and ascending:
203202
freq = freq * -1
204-
attribs["freq"] = freq
205203

206204
if not ascending:
207205
sorted_values = sorted_values[::-1]
208206

209-
return self._simple_new(sorted_values, **attribs)
207+
arr = type(self._data)._simple_new(
208+
sorted_values, dtype=self.dtype, freq=freq
209+
)
210+
return self._simple_new(arr, name=self.name)
210211

211212
@Appender(_index_shared_docs["take"] % _index_doc_kwargs)
212213
def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs):
@@ -503,22 +504,21 @@ def _concat_same_dtype(self, to_concat, name):
503504
"""
504505
Concatenate to_concat which has the same class.
505506
"""
506-
attribs = self._get_attributes_dict()
507-
attribs["name"] = name
507+
508508
# do not pass tz to set because tzlocal cannot be hashed
509509
if len({str(x.dtype) for x in to_concat}) != 1:
510510
raise ValueError("to_concat must have the same tz")
511511

512-
new_data = type(self._values)._concat_same_type(to_concat).asi8
512+
new_data = type(self._data)._concat_same_type(to_concat)
513513

514-
# GH 3232: If the concat result is evenly spaced, we can retain the
515-
# original frequency
516-
is_diff_evenly_spaced = len(unique_deltas(new_data)) == 1
517-
if not is_period_dtype(self) and not is_diff_evenly_spaced:
518-
# reset freq
519-
attribs["freq"] = None
514+
if not is_period_dtype(self.dtype):
515+
# GH 3232: If the concat result is evenly spaced, we can retain the
516+
# original frequency
517+
is_diff_evenly_spaced = len(unique_deltas(new_data.asi8)) == 1
518+
if is_diff_evenly_spaced:
519+
new_data._freq = self.freq
520520

521-
return self._simple_new(new_data, **attribs)
521+
return self._simple_new(new_data, name=name)
522522

523523
def shift(self, periods=1, freq=None):
524524
"""

pandas/core/indexes/period.py

+6-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
is_datetime64_any_dtype,
1616
is_dtype_equal,
1717
is_float,
18-
is_float_dtype,
1918
is_integer,
2019
is_integer_dtype,
2120
is_list_like,
@@ -234,21 +233,12 @@ def _simple_new(cls, values, name=None, freq=None, **kwargs):
234233
235234
Parameters
236235
----------
237-
values : PeriodArray, PeriodIndex, Index[int64], ndarray[int64]
236+
values : PeriodArray
238237
Values that can be converted to a PeriodArray without inference
239238
or coercion.
240-
241239
"""
242-
# TODO: raising on floats is tested, but maybe not useful.
243-
# Should the callers know not to pass floats?
244-
# At the very least, I think we can ensure that lists aren't passed.
245-
if isinstance(values, list):
246-
values = np.asarray(values)
247-
if is_float_dtype(values):
248-
raise TypeError("PeriodIndex._simple_new does not accept floats.")
249-
if freq:
250-
freq = Period._maybe_convert_freq(freq)
251-
values = PeriodArray(values, freq=freq)
240+
assert isinstance(values, PeriodArray), type(values)
241+
assert freq is None or freq == values.freq, (freq, values.freq)
252242

253243
result = object.__new__(cls)
254244
result._data = values
@@ -834,7 +824,9 @@ def _union(self, other, sort):
834824

835825
def _apply_meta(self, rawarr):
836826
if not isinstance(rawarr, PeriodIndex):
837-
rawarr = PeriodIndex._simple_new(rawarr, freq=self.freq, name=self.name)
827+
if not isinstance(rawarr, PeriodArray):
828+
rawarr = PeriodArray(rawarr, freq=self.freq)
829+
rawarr = PeriodIndex._simple_new(rawarr, name=self.name)
838830
return rawarr
839831

840832
def memory_usage(self, deep=False):

pandas/tests/arrays/test_datetimelike.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ def test_compare_len1_raises(self):
6565
# to the case where one has length-1, which numpy would broadcast
6666
data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
6767

68-
idx = self.index_cls._simple_new(data, freq="D")
69-
arr = self.array_cls(idx)
68+
idx = self.array_cls._simple_new(data, freq="D")
69+
arr = self.index_cls(idx)
7070

7171
with pytest.raises(ValueError, match="Lengths must match"):
7272
arr == arr[:1]
@@ -79,8 +79,8 @@ def test_take(self):
7979
data = np.arange(100, dtype="i8") * 24 * 3600 * 10 ** 9
8080
np.random.shuffle(data)
8181

82-
idx = self.index_cls._simple_new(data, freq="D")
83-
arr = self.array_cls(idx)
82+
arr = self.array_cls._simple_new(data, freq="D")
83+
idx = self.index_cls._simple_new(arr)
8484

8585
takers = [1, 4, 94]
8686
result = arr.take(takers)
@@ -97,8 +97,7 @@ def test_take(self):
9797
def test_take_fill(self):
9898
data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
9999

100-
idx = self.index_cls._simple_new(data, freq="D")
101-
arr = self.array_cls(idx)
100+
arr = self.array_cls._simple_new(data, freq="D")
102101

103102
result = arr.take([-1, 1], allow_fill=True, fill_value=None)
104103
assert result[0] is pd.NaT
@@ -121,7 +120,9 @@ def test_take_fill(self):
121120
def test_concat_same_type(self):
122121
data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
123122

124-
idx = self.index_cls._simple_new(data, freq="D").insert(0, pd.NaT)
123+
arr = self.array_cls._simple_new(data, freq="D")
124+
idx = self.index_cls(arr)
125+
idx = idx.insert(0, pd.NaT)
125126
arr = self.array_cls(idx)
126127

127128
result = arr._concat_same_type([arr[:-1], arr[1:], arr])

pandas/tests/indexes/period/test_constructors.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -322,22 +322,33 @@ def test_constructor_mixed(self):
322322

323323
def test_constructor_simple_new(self):
324324
idx = period_range("2007-01", name="p", periods=2, freq="M")
325-
result = idx._simple_new(idx, name="p", freq=idx.freq)
325+
326+
with pytest.raises(AssertionError, match="<class .*PeriodIndex'>"):
327+
idx._simple_new(idx, name="p", freq=idx.freq)
328+
329+
result = idx._simple_new(idx._data, name="p", freq=idx.freq)
326330
tm.assert_index_equal(result, idx)
327331

328-
result = idx._simple_new(idx.astype("i8"), name="p", freq=idx.freq)
332+
with pytest.raises(AssertionError):
333+
# Need ndarray, not Int64Index
334+
type(idx._data)._simple_new(idx.astype("i8"), freq=idx.freq)
335+
336+
arr = type(idx._data)._simple_new(idx.asi8, freq=idx.freq)
337+
result = idx._simple_new(arr, name="p")
329338
tm.assert_index_equal(result, idx)
330339

331340
def test_constructor_simple_new_empty(self):
332341
# GH13079
333342
idx = PeriodIndex([], freq="M", name="p")
334-
result = idx._simple_new(idx, name="p", freq="M")
343+
with pytest.raises(AssertionError, match="<class .*PeriodIndex'>"):
344+
idx._simple_new(idx, name="p", freq="M")
345+
346+
result = idx._simple_new(idx._data, name="p", freq="M")
335347
tm.assert_index_equal(result, idx)
336348

337349
@pytest.mark.parametrize("floats", [[1.1, 2.1], np.array([1.1, 2.1])])
338350
def test_constructor_floats(self, floats):
339-
msg = r"PeriodIndex\._simple_new does not accept floats"
340-
with pytest.raises(TypeError, match=msg):
351+
with pytest.raises(AssertionError, match="<class "):
341352
pd.PeriodIndex._simple_new(floats, freq="M")
342353

343354
msg = "PeriodIndex does not allow floating point in construction"

0 commit comments

Comments
 (0)