diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 73e4a51ca3e7c..0ff733ab51b85 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -47,11 +47,7 @@ from pandas.core.construction import extract_array import pandas.core.indexes.base as ibase from pandas.core.indexes.base import maybe_extract_name -from pandas.core.indexes.numeric import ( - Float64Index, - Int64Index, - NumericIndex, -) +from pandas.core.indexes.numeric import NumericIndex from pandas.core.ops.common import unpack_zerodim_and_defer if TYPE_CHECKING: @@ -64,8 +60,8 @@ class RangeIndex(NumericIndex): """ Immutable Index implementing a monotonic integer range. - RangeIndex is a memory-saving special case of Int64Index limited to - representing monotonic ranges. Using RangeIndex may in some instances + RangeIndex is a memory-saving special case of an Index limited to representing + monotonic ranges with a 64-bit dtype. Using RangeIndex may in some instances improve computing speed. This is the default index type used @@ -97,7 +93,6 @@ class RangeIndex(NumericIndex): See Also -------- Index : The base pandas Index type. - Int64Index : Index of int64 data. """ _typ = "rangeindex" @@ -185,7 +180,7 @@ def _simple_new( # type: ignore[override] # -------------------------------------------------------------------- - # error: Return type "Type[Int64Index]" of "_constructor" incompatible with return + # error: Return type "Type[NumericIndex]" of "_constructor" incompatible with return # type "Type[RangeIndex]" in supertype "Index" @cache_readonly def _constructor(self) -> type[NumericIndex]: # type: ignore[override] @@ -331,7 +326,7 @@ def inferred_type(self) -> str: # -------------------------------------------------------------------- # Indexing Methods - @doc(Int64Index.get_loc) + @doc(NumericIndex.get_loc) def get_loc(self, key): if is_integer(key) or (is_float(key) and key.is_integer()): new_key = int(key) @@ -377,32 +372,32 @@ def _get_indexer( def tolist(self) -> list[int]: return list(self._range) - @doc(Int64Index.__iter__) + @doc(NumericIndex.__iter__) def __iter__(self) -> Iterator[int]: yield from self._range - @doc(Int64Index._shallow_copy) + @doc(NumericIndex._shallow_copy) def _shallow_copy(self, values, name: Hashable = no_default): name = self.name if name is no_default else name if values.dtype.kind == "f": - return Float64Index(values, name=name) + return NumericIndex(values, name=name, dtype=np.float64) # GH 46675 & 43885: If values is equally spaced, return a - # more memory-compact RangeIndex instead of Int64Index + # more memory-compact RangeIndex instead of Index with 64-bit dtype unique_diffs = unique_deltas(values) if len(unique_diffs) == 1 and unique_diffs[0] != 0: diff = unique_diffs[0] new_range = range(values[0], values[-1] + diff, diff) return type(self)._simple_new(new_range, name=name) else: - return Int64Index._simple_new(values, name=name) + return NumericIndex._simple_new(values, name=name) def _view(self: RangeIndex) -> RangeIndex: result = type(self)._simple_new(self._range, name=self._name) result._cache = self._cache return result - @doc(Int64Index.copy) + @doc(NumericIndex.copy) def copy(self, name: Hashable = None, deep: bool = False): name = self._validate_names(name=name, deep=deep)[0] new_index = self._rename(name=name) @@ -517,7 +512,6 @@ def _intersection(self, other: Index, sort: bool = False): # caller is responsible for checking self and other are both non-empty if not isinstance(other, RangeIndex): - # Int64Index return super()._intersection(other, sort=sort) first = self._range[::-1] if self.step < 0 else self._range @@ -604,10 +598,10 @@ def _union(self, other: Index, sort): sort : False or None, default None Whether to sort (monotonically increasing) the resulting index. ``sort=None`` returns a ``RangeIndex`` if possible or a sorted - ``Int64Index`` if not. + ``Index`` with a int64 dtype if not. ``sort=False`` can return a ``RangeIndex`` if self is monotonically increasing and other is fully contained in self. Otherwise, returns - an unsorted ``Int64Index`` + an unsorted ``Index`` with an int64 dtype. Returns ------- @@ -819,9 +813,9 @@ def _concat(self, indexes: list[Index], name: Hashable) -> Index: Overriding parent method for the case of all RangeIndex instances. When all members of "indexes" are of type RangeIndex: result will be - RangeIndex if possible, Int64Index otherwise. E.g.: + RangeIndex if possible, Index with a int64 dtype otherwise. E.g.: indexes = [RangeIndex(3), RangeIndex(3, 6)] -> RangeIndex(6) - indexes = [RangeIndex(3), RangeIndex(4, 6)] -> Int64Index([0,1,2,4,5]) + indexes = [RangeIndex(3), RangeIndex(4, 6)] -> Index([0,1,2,4,5], dtype='int64') """ if not all(isinstance(x, RangeIndex) for x in indexes): return super()._concat(indexes, name) @@ -848,7 +842,7 @@ def _concat(self, indexes: list[Index], name: Hashable) -> Index: # First non-empty index had only one element if rng.start == start: values = np.concatenate([x._values for x in rng_indexes]) - result = Int64Index(values) + result = self._constructor(values) return result.rename(name) step = rng.start - start @@ -857,7 +851,9 @@ def _concat(self, indexes: list[Index], name: Hashable) -> Index: next_ is not None and rng.start != next_ ) if non_consecutive: - result = Int64Index(np.concatenate([x._values for x in rng_indexes])) + result = self._constructor( + np.concatenate([x._values for x in rng_indexes]) + ) return result.rename(name) if step is not None: @@ -905,7 +901,6 @@ def __getitem__(self, key): "and integer or boolean " "arrays are valid indices" ) - # fall back to Int64Index return super().__getitem__(key) def _getitem_slice(self: RangeIndex, slobj: slice) -> RangeIndex: @@ -1010,15 +1005,14 @@ def _arith_method(self, other, op): res_name = ops.get_op_result_name(self, other) result = type(self)(rstart, rstop, rstep, name=res_name) - # for compat with numpy / Int64Index + # for compat with numpy / Index with int64 dtype # even if we can represent as a RangeIndex, return - # as a Float64Index if we have float-like descriptors + # as a float64 Index if we have float-like descriptors if not all(is_integer(x) for x in [rstart, rstop, rstep]): result = result.astype("float64") return result except (ValueError, TypeError, ZeroDivisionError): - # Defer to Int64Index implementation # test_arithmetic_explicit_conversions return super()._arith_method(other, op) diff --git a/pandas/tests/indexes/ranges/test_join.py b/pandas/tests/indexes/ranges/test_join.py index c3c2560693d3d..958dd6aa9a563 100644 --- a/pandas/tests/indexes/ranges/test_join.py +++ b/pandas/tests/indexes/ranges/test_join.py @@ -1,24 +1,25 @@ import numpy as np +from pandas.core.dtypes.common import is_int64_dtype + from pandas import ( Index, RangeIndex, ) import pandas._testing as tm -from pandas.core.indexes.api import Int64Index class TestJoin: def test_join_outer(self): - # join with Int64Index + # join with Index[int64] index = RangeIndex(start=0, stop=20, step=2) - other = Int64Index(np.arange(25, 14, -1)) + other = Index(np.arange(25, 14, -1, dtype=np.int64)) res, lidx, ridx = index.join(other, how="outer", return_indexers=True) noidx_res = index.join(other, how="outer") tm.assert_index_equal(res, noidx_res) - eres = Int64Index( + eres = Index( [0, 2, 4, 6, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] ) elidx = np.array( @@ -30,9 +31,9 @@ def test_join_outer(self): dtype=np.intp, ) - assert isinstance(res, Int64Index) + assert isinstance(res, Index) and is_int64_dtype(res.dtype) assert not isinstance(res, RangeIndex) - tm.assert_index_equal(res, eres) + tm.assert_index_equal(res, eres, exact=True) tm.assert_numpy_array_equal(lidx, elidx) tm.assert_numpy_array_equal(ridx, eridx) @@ -43,7 +44,7 @@ def test_join_outer(self): noidx_res = index.join(other, how="outer") tm.assert_index_equal(res, noidx_res) - assert isinstance(res, Int64Index) + assert isinstance(res, Index) and res.dtype == np.int64 assert not isinstance(res, RangeIndex) tm.assert_index_equal(res, eres) tm.assert_numpy_array_equal(lidx, elidx) @@ -52,7 +53,7 @@ def test_join_outer(self): def test_join_inner(self): # Join with non-RangeIndex index = RangeIndex(start=0, stop=20, step=2) - other = Int64Index(np.arange(25, 14, -1)) + other = Index(np.arange(25, 14, -1, dtype=np.int64)) res, lidx, ridx = index.join(other, how="inner", return_indexers=True) @@ -62,7 +63,7 @@ def test_join_inner(self): lidx = lidx.take(ind) ridx = ridx.take(ind) - eres = Int64Index([16, 18]) + eres = Index([16, 18]) elidx = np.array([8, 9], dtype=np.intp) eridx = np.array([9, 7], dtype=np.intp) @@ -82,9 +83,9 @@ def test_join_inner(self): tm.assert_numpy_array_equal(ridx, eridx) def test_join_left(self): - # Join with Int64Index + # Join with Index[int64] index = RangeIndex(start=0, stop=20, step=2) - other = Int64Index(np.arange(25, 14, -1)) + other = Index(np.arange(25, 14, -1, dtype=np.int64)) res, lidx, ridx = index.join(other, how="left", return_indexers=True) eres = index @@ -96,7 +97,7 @@ def test_join_left(self): tm.assert_numpy_array_equal(ridx, eridx) # Join withRangeIndex - other = Int64Index(np.arange(25, 14, -1)) + other = Index(np.arange(25, 14, -1, dtype=np.int64)) res, lidx, ridx = index.join(other, how="left", return_indexers=True) @@ -106,15 +107,15 @@ def test_join_left(self): tm.assert_numpy_array_equal(ridx, eridx) def test_join_right(self): - # Join with Int64Index + # Join with Index[int64] index = RangeIndex(start=0, stop=20, step=2) - other = Int64Index(np.arange(25, 14, -1)) + other = Index(np.arange(25, 14, -1, dtype=np.int64)) res, lidx, ridx = index.join(other, how="right", return_indexers=True) eres = other elidx = np.array([-1, -1, -1, -1, -1, -1, -1, 9, -1, 8, -1], dtype=np.intp) - assert isinstance(other, Int64Index) + assert isinstance(other, Index) and other.dtype == np.int64 tm.assert_index_equal(res, eres) tm.assert_numpy_array_equal(lidx, elidx) assert ridx is None @@ -164,7 +165,7 @@ def test_join_non_unique(self): res, lidx, ridx = index.join(other, return_indexers=True) - eres = Int64Index([0, 2, 4, 4, 6, 8, 10, 12, 14, 16, 18]) + eres = Index([0, 2, 4, 4, 6, 8, 10, 12, 14, 16, 18]) elidx = np.array([0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.intp) eridx = np.array([-1, -1, 0, 1, -1, -1, -1, -1, -1, -1, -1], dtype=np.intp) diff --git a/pandas/tests/indexes/ranges/test_range.py b/pandas/tests/indexes/ranges/test_range.py index e534147c891d2..d255dc748b7dc 100644 --- a/pandas/tests/indexes/ranges/test_range.py +++ b/pandas/tests/indexes/ranges/test_range.py @@ -4,20 +4,15 @@ from pandas.core.dtypes.common import ensure_platform_int import pandas as pd -import pandas._testing as tm -from pandas.core.indexes.api import ( - Float64Index, +from pandas import ( Index, - Int64Index, RangeIndex, ) +import pandas._testing as tm from pandas.tests.indexes.common import NumericBase # aliases to make some tests easier to read RI = RangeIndex -I64 = Int64Index -F64 = Float64Index -OI = Index class TestRangeIndex(NumericBase): @@ -111,7 +106,7 @@ def test_insert(self): tm.assert_index_equal(idx[0:4], result.insert(0, idx[0]), exact="equiv") # GH 18295 (test missing) - expected = Float64Index([0, np.nan, 1, 2, 3, 4]) + expected = Index([0, np.nan, 1, 2, 3, 4], dtype=np.float64) for na in [np.nan, None, pd.NA]: result = RangeIndex(5).insert(1, na) tm.assert_index_equal(result, expected) @@ -379,7 +374,7 @@ def test_nbytes(self): # memory savings vs int index idx = RangeIndex(0, 1000) - assert idx.nbytes < Int64Index(idx._values).nbytes / 10 + assert idx.nbytes < Index(idx._values).nbytes / 10 # constant memory usage i2 = RangeIndex(0, 10) @@ -530,16 +525,16 @@ def test_len_specialised(self, step): ([RI(-4, -8), RI(-8, -12)], RI(0, 0)), ([RI(-4, -8), RI(3, -4)], RI(0, 0)), ([RI(-4, -8), RI(3, 5)], RI(3, 5)), - ([RI(-4, -2), RI(3, 5)], I64([-4, -3, 3, 4])), + ([RI(-4, -2), RI(3, 5)], Index([-4, -3, 3, 4])), ([RI(-2), RI(3, 5)], RI(3, 5)), - ([RI(2), RI(2)], I64([0, 1, 0, 1])), + ([RI(2), RI(2)], Index([0, 1, 0, 1])), ([RI(2), RI(2, 5), RI(5, 8, 4)], RI(0, 6)), - ([RI(2), RI(3, 5), RI(5, 8, 4)], I64([0, 1, 3, 4, 5])), + ([RI(2), RI(3, 5), RI(5, 8, 4)], Index([0, 1, 3, 4, 5])), ([RI(-2, 2), RI(2, 5), RI(5, 8, 4)], RI(-2, 6)), - ([RI(3), OI([-1, 3, 15])], OI([0, 1, 2, -1, 3, 15])), - ([RI(3), OI([-1, 3.1, 15.0])], OI([0, 1, 2, -1, 3.1, 15.0])), - ([RI(3), OI(["a", None, 14])], OI([0, 1, 2, "a", None, 14])), - ([RI(3, 1), OI(["a", None, 14])], OI(["a", None, 14])), + ([RI(3), Index([-1, 3, 15])], Index([0, 1, 2, -1, 3, 15])), + ([RI(3), Index([-1, 3.1, 15.0])], Index([0, 1, 2, -1, 3.1, 15.0])), + ([RI(3), Index(["a", None, 14])], Index([0, 1, 2, "a", None, 14])), + ([RI(3, 1), Index(["a", None, 14])], Index(["a", None, 14])), ] ) def appends(self, request): diff --git a/pandas/tests/indexes/ranges/test_setops.py b/pandas/tests/indexes/ranges/test_setops.py index 29ba5a91498db..d417b8b743dc5 100644 --- a/pandas/tests/indexes/ranges/test_setops.py +++ b/pandas/tests/indexes/ranges/test_setops.py @@ -11,12 +11,11 @@ import numpy as np import pytest -import pandas._testing as tm -from pandas.core.indexes.api import ( +from pandas import ( Index, - Int64Index, RangeIndex, ) +import pandas._testing as tm class TestRangeIndexSetOps: @@ -62,7 +61,7 @@ def test_intersection_empty(self, sort, names): tm.assert_index_equal(result, index[:0].rename(names[2]), exact=True) def test_intersection(self, sort): - # intersect with Int64Index + # intersect with Index with dtype int64 index = RangeIndex(start=0, stop=20, step=2) other = Index(np.arange(1, 6)) result = index.intersection(other, sort=sort) @@ -133,7 +132,7 @@ def test_intersection_non_overlapping_gcd(self, sort, names): tm.assert_index_equal(result, expected) def test_union_noncomparable(self, sort): - # corner case, non-Int64Index + # corner case, Index with non-int64 dtype index = RangeIndex(start=0, stop=20, step=2) other = Index([datetime.now() + timedelta(i) for i in range(4)], dtype=object) result = index.union(other, sort=sort) @@ -181,25 +180,25 @@ def test_union_noncomparable(self, sort): RangeIndex(0, 10, 2), RangeIndex(1, 10, 2), RangeIndex(0, 10, 1), - Int64Index(list(range(0, 10, 2)) + list(range(1, 10, 2))), + Index(list(range(0, 10, 2)) + list(range(1, 10, 2))), ), ( RangeIndex(0, 11, 2), RangeIndex(1, 12, 2), RangeIndex(0, 12, 1), - Int64Index(list(range(0, 11, 2)) + list(range(1, 12, 2))), + Index(list(range(0, 11, 2)) + list(range(1, 12, 2))), ), ( RangeIndex(0, 21, 4), RangeIndex(-2, 24, 4), RangeIndex(-2, 24, 2), - Int64Index(list(range(0, 21, 4)) + list(range(-2, 24, 4))), + Index(list(range(0, 21, 4)) + list(range(-2, 24, 4))), ), ( RangeIndex(0, -20, -2), RangeIndex(-1, -21, -2), RangeIndex(-19, 1, 1), - Int64Index(list(range(0, -20, -2)) + list(range(-1, -21, -2))), + Index(list(range(0, -20, -2)) + list(range(-1, -21, -2))), ), ( RangeIndex(0, 100, 5), @@ -211,13 +210,13 @@ def test_union_noncomparable(self, sort): RangeIndex(0, -100, -5), RangeIndex(5, -100, -20), RangeIndex(-95, 10, 5), - Int64Index(list(range(0, -100, -5)) + [5]), + Index(list(range(0, -100, -5)) + [5]), ), ( RangeIndex(0, -11, -1), RangeIndex(1, -12, -4), RangeIndex(-11, 2, 1), - Int64Index(list(range(0, -11, -1)) + [1, -11]), + Index(list(range(0, -11, -1)) + [1, -11]), ), (RangeIndex(0), RangeIndex(0), RangeIndex(0), RangeIndex(0)), ( @@ -236,7 +235,7 @@ def test_union_noncomparable(self, sort): RangeIndex(0, -100, -2), RangeIndex(-100, 50, 102), RangeIndex(-100, 4, 2), - Int64Index(list(range(0, -100, -2)) + [-100, 2]), + Index(list(range(0, -100, -2)) + [-100, 2]), ), ( RangeIndex(0, -100, -1), @@ -254,25 +253,25 @@ def test_union_noncomparable(self, sort): RangeIndex(0, 10, 5), RangeIndex(-5, -6, -20), RangeIndex(-5, 10, 5), - Int64Index([0, 5, -5]), + Index([0, 5, -5]), ), ( RangeIndex(0, 3, 1), RangeIndex(4, 5, 1), - Int64Index([0, 1, 2, 4]), - Int64Index([0, 1, 2, 4]), + Index([0, 1, 2, 4]), + Index([0, 1, 2, 4]), ), ( RangeIndex(0, 10, 1), - Int64Index([]), + Index([], dtype=np.int64), RangeIndex(0, 10, 1), RangeIndex(0, 10, 1), ), ( RangeIndex(0), - Int64Index([1, 5, 6]), - Int64Index([1, 5, 6]), - Int64Index([1, 5, 6]), + Index([1, 5, 6]), + Index([1, 5, 6]), + Index([1, 5, 6]), ), # GH 43885 ( @@ -292,7 +291,7 @@ def test_union_sorted(self, idx1, idx2, expected_sorted, expected_notsorted): tm.assert_index_equal(res1, expected_notsorted, exact=True) res2 = idx2.union(idx1, sort=None) - res3 = Int64Index(idx1._values, name=idx1.name).union(idx2, sort=None) + res3 = Index(idx1._values, name=idx1.name).union(idx2, sort=None) tm.assert_index_equal(res2, expected_sorted, exact=True) tm.assert_index_equal(res3, expected_sorted, exact="equiv") @@ -302,7 +301,7 @@ def test_union_same_step_misaligned(self): right = RangeIndex(range(1, 21, 4)) result = left.union(right) - expected = Int64Index([0, 1, 4, 5, 8, 9, 12, 13, 16, 17]) + expected = Index([0, 1, 4, 5, 8, 9, 12, 13, 16, 17]) tm.assert_index_equal(result, expected, exact=True) def test_difference(self): @@ -338,8 +337,8 @@ def test_difference(self): tm.assert_index_equal(result, obj[:-3][::-1], exact=True) result = obj.difference(obj[2:6]) - expected = Int64Index([1, 2, 7, 8, 9], name="foo") - tm.assert_index_equal(result, expected) + expected = Index([1, 2, 7, 8, 9], name="foo") + tm.assert_index_equal(result, expected, exact=True) def test_difference_sort(self): # GH#44085 ensure we respect the sort keyword @@ -402,25 +401,25 @@ def test_difference_interior_non_preserving(self): other = idx[3:4] result = idx.difference(other) - expected = Int64Index([0, 1, 2, 4, 5, 6, 7, 8, 9]) + expected = Index([0, 1, 2, 4, 5, 6, 7, 8, 9]) tm.assert_index_equal(result, expected, exact=True) # case with other.step / self.step > 2 other = idx[::3] result = idx.difference(other) - expected = Int64Index([1, 2, 4, 5, 7, 8]) + expected = Index([1, 2, 4, 5, 7, 8]) tm.assert_index_equal(result, expected, exact=True) # cases with only reaching one end of left obj = Index(range(20)) other = obj[:10:2] result = obj.difference(other) - expected = Int64Index([1, 3, 5, 7, 9] + list(range(10, 20))) + expected = Index([1, 3, 5, 7, 9] + list(range(10, 20))) tm.assert_index_equal(result, expected, exact=True) other = obj[1:11:2] result = obj.difference(other) - expected = Int64Index([0, 2, 4, 6, 8, 10] + list(range(11, 20))) + expected = Index([0, 2, 4, 6, 8, 10] + list(range(11, 20))) tm.assert_index_equal(result, expected, exact=True) def test_symmetric_difference(self): @@ -436,8 +435,8 @@ def test_symmetric_difference(self): tm.assert_index_equal(result, left.rename(None)) result = left[:-2].symmetric_difference(left[2:]) - expected = Int64Index([1, 2, 8, 9], name="foo") - tm.assert_index_equal(result, expected) + expected = Index([1, 2, 8, 9], name="foo") + tm.assert_index_equal(result, expected, exact=True) right = RangeIndex.from_range(range(10, 15)) @@ -446,8 +445,8 @@ def test_symmetric_difference(self): tm.assert_index_equal(result, expected) result = left.symmetric_difference(right[1:]) - expected = Int64Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]) - tm.assert_index_equal(result, expected) + expected = Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]) + tm.assert_index_equal(result, expected, exact=True) def assert_range_or_not_is_rangelike(index): @@ -470,7 +469,7 @@ def assert_range_or_not_is_rangelike(index): ) def test_range_difference(start1, stop1, step1, start2, stop2, step2): # test that - # a) we match Int64Index.difference and + # a) we match Index[int64].difference and # b) we return RangeIndex whenever it is possible to do so. assume(step1 != 0) assume(step2 != 0) @@ -481,11 +480,14 @@ def test_range_difference(start1, stop1, step1, start2, stop2, step2): result = left.difference(right, sort=None) assert_range_or_not_is_rangelike(result) - alt = Int64Index(left).difference(Int64Index(right), sort=None) + left_int64 = Index(left.to_numpy()) + right_int64 = Index(right.to_numpy()) + + alt = left_int64.difference(right_int64, sort=None) tm.assert_index_equal(result, alt, exact="equiv") result = left.difference(right, sort=False) assert_range_or_not_is_rangelike(result) - alt = Int64Index(left).difference(Int64Index(right), sort=False) + alt = left_int64.difference(right_int64, sort=False) tm.assert_index_equal(result, alt, exact="equiv")