Skip to content

Commit 36b4990

Browse files
authored
BUG: raise on RangeIndex.array (#40022)
1 parent 5d1f13e commit 36b4990

File tree

9 files changed

+63
-13
lines changed

9 files changed

+63
-13
lines changed

pandas/_testing/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
)
101101
from pandas.core.arrays import (
102102
DatetimeArray,
103+
PandasArray,
103104
PeriodArray,
104105
TimedeltaArray,
105106
period_array,
@@ -204,7 +205,11 @@ def box_expected(expected, box_cls, transpose=True):
204205
subclass of box_cls
205206
"""
206207
if box_cls is pd.array:
207-
expected = pd.array(expected)
208+
if isinstance(expected, RangeIndex):
209+
# pd.array would return an IntegerArray
210+
expected = PandasArray(np.asarray(expected._values))
211+
else:
212+
expected = pd.array(expected)
208213
elif box_cls is Index:
209214
expected = Index(expected)
210215
elif box_cls is Series:

pandas/core/construction.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
ABCExtensionArray,
5959
ABCIndex,
6060
ABCPandasArray,
61+
ABCRangeIndex,
6162
ABCSeries,
6263
)
6364
from pandas.core.dtypes.missing import isna
@@ -367,7 +368,9 @@ def array(
367368
return PandasArray._from_sequence(data, dtype=dtype, copy=copy)
368369

369370

370-
def extract_array(obj: object, extract_numpy: bool = False) -> Any | ArrayLike:
371+
def extract_array(
372+
obj: object, extract_numpy: bool = False, extract_range: bool = False
373+
) -> Any | ArrayLike:
371374
"""
372375
Extract the ndarray or ExtensionArray from a Series or Index.
373376
@@ -382,6 +385,10 @@ def extract_array(obj: object, extract_numpy: bool = False) -> Any | ArrayLike:
382385
extract_numpy : bool, default False
383386
Whether to extract the ndarray from a PandasArray
384387
388+
extract_range : bool, default False
389+
If we have a RangeIndex, return range._values if True
390+
(which is a materialized integer ndarray), otherwise return unchanged.
391+
385392
Returns
386393
-------
387394
arr : object
@@ -410,6 +417,11 @@ def extract_array(obj: object, extract_numpy: bool = False) -> Any | ArrayLike:
410417
array([1, 2, 3])
411418
"""
412419
if isinstance(obj, (ABCIndex, ABCSeries)):
420+
if isinstance(obj, ABCRangeIndex):
421+
if extract_range:
422+
return obj._values
423+
return obj
424+
413425
obj = obj.array
414426

415427
if extract_numpy and isinstance(obj, ABCPandasArray):

pandas/core/indexes/range.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,17 @@ def argsort(self, *args, **kwargs) -> np.ndarray:
528528
--------
529529
numpy.ndarray.argsort
530530
"""
531+
ascending = kwargs.pop("ascending", True) # EA compat
531532
nv.validate_argsort(args, kwargs)
532533

533534
if self._range.step > 0:
534-
return np.arange(len(self))
535+
result = np.arange(len(self))
535536
else:
536-
return np.arange(len(self) - 1, -1, -1)
537+
result = np.arange(len(self) - 1, -1, -1)
538+
539+
if not ascending:
540+
result = result[::-1]
541+
return result
537542

538543
def factorize(
539544
self, sort: bool = False, na_sentinel: int | None = -1
@@ -920,7 +925,8 @@ def _arith_method(self, other, op):
920925
if op in [operator.mul, ops.rmul, operator.truediv, ops.rtruediv]:
921926
step = op
922927

923-
other = extract_array(other, extract_numpy=True)
928+
# TODO: if other is a RangeIndex we may have more efficient options
929+
other = extract_array(other, extract_numpy=True, extract_range=True)
924930
attrs = self._get_attributes_dict()
925931

926932
left, right = self, other

pandas/core/reshape/merge.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,8 +2080,9 @@ def _factorize_keys(
20802080
(array([0, 1, 2]), array([0, 1]), 3)
20812081
"""
20822082
# Some pre-processing for non-ndarray lk / rk
2083-
lk = extract_array(lk, extract_numpy=True)
2084-
rk = extract_array(rk, extract_numpy=True)
2083+
lk = extract_array(lk, extract_numpy=True, extract_range=True)
2084+
rk = extract_array(rk, extract_numpy=True, extract_range=True)
2085+
# TODO: if either is a RangeIndex, we can likely factorize more efficiently?
20852086

20862087
if is_datetime64tz_dtype(lk.dtype) and is_datetime64tz_dtype(rk.dtype):
20872088
# Extract the ndarray (UTC-localized) values

pandas/core/series.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,7 +5078,7 @@ def _cmp_method(self, other, op):
50785078
raise ValueError("Can only compare identically-labeled Series objects")
50795079

50805080
lvalues = self._values
5081-
rvalues = extract_array(other, extract_numpy=True)
5081+
rvalues = extract_array(other, extract_numpy=True, extract_range=True)
50825082

50835083
with np.errstate(all="ignore"):
50845084
res_values = ops.comparison_op(lvalues, rvalues, op)
@@ -5090,7 +5090,7 @@ def _logical_method(self, other, op):
50905090
self, other = ops.align_method_SERIES(self, other, align_asobject=True)
50915091

50925092
lvalues = self._values
5093-
rvalues = extract_array(other, extract_numpy=True)
5093+
rvalues = extract_array(other, extract_numpy=True, extract_range=True)
50945094

50955095
res_values = ops.logical_op(lvalues, rvalues, op)
50965096
return self._construct_result(res_values, name=res_name)
@@ -5100,7 +5100,8 @@ def _arith_method(self, other, op):
51005100
self, other = ops.align_method_SERIES(self, other)
51015101

51025102
lvalues = self._values
5103-
rvalues = extract_array(other, extract_numpy=True)
5103+
rvalues = extract_array(other, extract_numpy=True, extract_range=True)
5104+
51045105
with np.errstate(all="ignore"):
51055106
result = ops.arithmetic_op(lvalues, rvalues, op)
51065107

pandas/core/sorting.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
ensure_platform_int,
2626
is_extension_array_dtype,
2727
)
28-
from pandas.core.dtypes.generic import ABCMultiIndex
28+
from pandas.core.dtypes.generic import (
29+
ABCMultiIndex,
30+
ABCRangeIndex,
31+
)
2932
from pandas.core.dtypes.missing import isna
3033

3134
from pandas.core import algorithms
@@ -363,9 +366,12 @@ def nargsort(
363366
mask=mask,
364367
)
365368

366-
items = extract_array(items)
369+
if isinstance(items, ABCRangeIndex):
370+
return items.argsort(ascending=ascending) # TODO: test coverage with key?
371+
elif not isinstance(items, ABCMultiIndex):
372+
items = extract_array(items)
367373
if mask is None:
368-
mask = np.asarray(isna(items))
374+
mask = np.asarray(isna(items)) # TODO: does this exclude MultiIndex too?
369375

370376
if is_extension_array_dtype(items):
371377
return items.argsort(ascending=ascending, kind=kind, na_position=na_position)

pandas/tests/arithmetic/test_numeric.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ def test_add_sub_datetimelike_invalid(self, numeric_idx, other, box_with_array):
311311
"Concatenation operation is not implemented for NumPy arrays",
312312
# pd.array vs np.datetime64 case
313313
r"operand type\(s\) all returned NotImplemented from __array_ufunc__",
314+
"can only perform ops with numeric values",
314315
]
315316
)
316317
with pytest.raises(TypeError, match=msg):

pandas/tests/construction/__init__.py

Whitespace-only changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from pandas import Index
2+
import pandas._testing as tm
3+
from pandas.core.construction import extract_array
4+
5+
6+
def test_extract_array_rangeindex():
7+
ri = Index(range(5))
8+
9+
expected = ri._values
10+
res = extract_array(ri, extract_numpy=True, extract_range=True)
11+
tm.assert_numpy_array_equal(res, expected)
12+
res = extract_array(ri, extract_numpy=False, extract_range=True)
13+
tm.assert_numpy_array_equal(res, expected)
14+
15+
res = extract_array(ri, extract_numpy=True, extract_range=False)
16+
tm.assert_index_equal(res, ri)
17+
res = extract_array(ri, extract_numpy=False, extract_range=False)
18+
tm.assert_index_equal(res, ri)

0 commit comments

Comments
 (0)