Skip to content
Merged
4 changes: 2 additions & 2 deletions doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ Other enhancements
- Allow reading compressed SAS files with :func:`read_sas` (e.g., ``.sas7bdat.gz`` files)
- :meth:`DatetimeIndex.astype` now supports casting timezone-naive indexes to ``datetime64[s]``, ``datetime64[ms]``, and ``datetime64[us]``, and timezone-aware indexes to the corresponding ``datetime64[unit, tzname]`` dtypes (:issue:`47579`)
- :class:`Series` reducers (e.g. ``min``, ``max``, ``sum``, ``mean``) will now successfully operate when the dtype is numeric and ``numeric_only=True`` is provided; previously this would raise a ``NotImplementedError`` (:issue:`47500`)
-
- :meth:`RangeIndex.union` now can return a :class:`RangeIndex` instead of a :class:`Int64Index` if the resulting values are equally spaced (:issue:`47557`, :issue:`43885`)

.. ---------------------------------------------------------------------------
.. _whatsnew_150.notable_bug_fixes:
Expand Down Expand Up @@ -1008,7 +1008,7 @@ Reshaping
- Bug in :func:`concat` with identical key leads to error when indexing :class:`MultiIndex` (:issue:`46519`)
- Bug in :meth:`DataFrame.join` with a list when using suffixes to join DataFrames with duplicate column names (:issue:`46396`)
- Bug in :meth:`DataFrame.pivot_table` with ``sort=False`` results in sorted index (:issue:`17041`)
-
- Bug in :meth:`concat` when ``axis=1`` and ``sort=False`` where the resulting Index was a :class:`Int64Index` instead of a :class:`RangeIndex` (:issue:`46675`)

Sparse
^^^^^^
Expand Down
9 changes: 8 additions & 1 deletion pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
index as libindex,
lib,
)
from pandas._libs.algos import unique_deltas
from pandas._libs.lib import no_default
from pandas._typing import (
Dtype,
Expand Down Expand Up @@ -435,7 +436,13 @@ def _shallow_copy(self, values, name: Hashable = no_default):

if values.dtype.kind == "f":
return Float64Index(values, name=name)
return Int64Index._simple_new(values, name=name)
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)

def _view(self: RangeIndex) -> RangeIndex:
result = type(self)._simple_new(self._range, name=self._name)
Expand Down
37 changes: 19 additions & 18 deletions pandas/tests/indexes/ranges/test_setops.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ def test_union_noncomparable(self, sort):
expected = Index(np.concatenate((other, index)))
tm.assert_index_equal(result, expected)

@pytest.fixture(
params=[
@pytest.mark.parametrize(
"idx1, idx2, expected_sorted, expected_notsorted",
[
(
RangeIndex(0, 10, 1),
RangeIndex(0, 10, 1),
Expand All @@ -157,13 +158,13 @@ def test_union_noncomparable(self, sort):
RangeIndex(0, 10, 1),
RangeIndex(5, 20, 1),
RangeIndex(0, 20, 1),
Int64Index(range(20)),
RangeIndex(0, 20, 1),
),
(
RangeIndex(0, 10, 1),
RangeIndex(10, 20, 1),
RangeIndex(0, 20, 1),
Int64Index(range(20)),
RangeIndex(0, 20, 1),
),
(
RangeIndex(0, -10, -1),
Expand All @@ -175,7 +176,7 @@ def test_union_noncomparable(self, sort):
RangeIndex(0, -10, -1),
RangeIndex(-10, -20, -1),
RangeIndex(-19, 1, 1),
Int64Index(range(0, -20, -1)),
RangeIndex(0, -20, -1),
),
(
RangeIndex(0, 10, 2),
Expand Down Expand Up @@ -205,7 +206,7 @@ def test_union_noncomparable(self, sort):
RangeIndex(0, 100, 5),
RangeIndex(0, 100, 20),
RangeIndex(0, 100, 5),
Int64Index(range(0, 100, 5)),
RangeIndex(0, 100, 5),
),
(
RangeIndex(0, -100, -5),
Expand All @@ -230,7 +231,7 @@ def test_union_noncomparable(self, sort):
RangeIndex(0, 100, 2),
RangeIndex(100, 150, 200),
RangeIndex(0, 102, 2),
Int64Index(range(0, 102, 2)),
RangeIndex(0, 102, 2),
),
(
RangeIndex(0, -100, -2),
Expand All @@ -242,13 +243,13 @@ def test_union_noncomparable(self, sort):
RangeIndex(0, -100, -1),
RangeIndex(0, -50, -3),
RangeIndex(-99, 1, 1),
Int64Index(list(range(0, -100, -1))),
RangeIndex(0, -100, -1),
),
(
RangeIndex(0, 1, 1),
RangeIndex(5, 6, 10),
RangeIndex(0, 6, 5),
Int64Index([0, 5]),
RangeIndex(0, 10, 5),
),
(
RangeIndex(0, 10, 5),
Expand All @@ -274,16 +275,16 @@ def test_union_noncomparable(self, sort):
Int64Index([1, 5, 6]),
Int64Index([1, 5, 6]),
),
]
# GH 43885
(
RangeIndex(0, 10),
RangeIndex(0, 5),
RangeIndex(0, 10),
RangeIndex(0, 10),
),
],
)
def unions(self, request):
"""Inputs and expected outputs for RangeIndex.union tests"""
return request.param

def test_union_sorted(self, unions):

idx1, idx2, expected_sorted, expected_notsorted = unions

def test_union_sorted(self, idx1, idx2, expected_sorted, expected_notsorted):
res1 = idx1.union(idx2, sort=None)
tm.assert_index_equal(res1, expected_sorted, exact=True)

Expand Down
22 changes: 22 additions & 0 deletions pandas/tests/reshape/concat/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,25 @@ def test_concat_index_keep_dtype(self, dtype):
[[0, 1, 1.0], [0, 1, np.nan]], columns=Index([1, 2, 3], dtype=dtype)
)
tm.assert_frame_equal(result, expected)

def test_concat_axis_1_sort_false_rangeindex(self):
# GH 46675
s1 = Series(["a", "b", "c"])
s2 = Series(["a", "b"])
s3 = Series(["a", "b", "c", "d"])
s4 = Series([], dtype=object)
result = concat(
[s1, s2, s3, s4], sort=False, join="outer", ignore_index=False, axis=1
)
expected = DataFrame(
[
["a"] * 3 + [np.nan],
["b"] * 3 + [np.nan],
["c", np.nan] * 2,
[np.nan] * 2 + ["d"] + [np.nan],
],
dtype=object,
)
tm.assert_frame_equal(
result, expected, check_index_type=True, check_column_type=True
)