Skip to content

Add RangeIndex #10076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dcc8f5b
add RangeIndex
benbovy Feb 25, 2025
04827db
Merge branch 'main' into range-index
benbovy Mar 20, 2025
adbd3d4
refactor RangeIndex
benbovy Mar 21, 2025
e7f6476
fix error raised during coords diff formatting
benbovy Mar 21, 2025
fb1b10b
assert_allclose: add support for Coordinates
benbovy Mar 21, 2025
76f58c0
add tests (wip)
benbovy Mar 21, 2025
53a02c8
assert invariants: skip check IndexVariable ...
benbovy Mar 14, 2025
9eaa530
more tests and fixes
benbovy Mar 21, 2025
e6709a1
no support for set_xindex (error msg)
benbovy Mar 21, 2025
e18725c
add public API documentation
benbovy Mar 21, 2025
cc3601f
fix doctests
benbovy Mar 21, 2025
b5c5207
add docstring examples
benbovy Mar 21, 2025
b48ed9d
[skip-ci] update whats new
benbovy Mar 21, 2025
2a27198
[skip-ci] doc: add RangeIndex factories to API (hidden)
benbovy Mar 21, 2025
f819846
add repr + start, stop, step properties
benbovy Mar 21, 2025
cd0a396
add support for rename vars and/or dims
benbovy Mar 21, 2025
4655934
step: attr -> property
benbovy Mar 24, 2025
ee11665
Merge branch 'main' into range-index
benbovy Mar 27, 2025
212a9ae
doc: list RangeIndex constructors in API ref
benbovy Mar 31, 2025
8ad8349
CoordinateTransformIndex.rename: deep copy transform
benbovy Mar 31, 2025
efe3e81
update arange and linspace signatures
benbovy Mar 31, 2025
38c8a6c
RangeIndex repr: show size, coord_name and dim props
benbovy Mar 31, 2025
24c6b55
fix doctests
benbovy Mar 31, 2025
911d76c
Merge branch 'main' into range-index
benbovy Mar 31, 2025
4a128d0
RangeIndex.arange: mimic pandas/numpy API
benbovy Apr 16, 2025
6541c98
Merge branch 'main' into range-index
benbovy Apr 16, 2025
78d0490
Merge branch 'main' into range-index
dcherian Apr 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions xarray/indexes/range_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,45 @@ def __init__(self, transform: RangeCoordinateTransform):
@classmethod
def arange(
cls,
start: float = 0.0,
stop: float = 1.0,
step: float = 1.0,
start: float | None = None,
stop: float | None = None,
step: float | None = None,
*,
coord_name: Hashable | None = None,
dim: str,
dtype: Any = None,
) -> "RangeIndex":
"""Create a new RangeIndex from given start, stop and step values.

``RangeIndex.arange`` can be called with a varying number of positional arguments:

- ``RangeIndex.arange(stop)``: the index is within the half-open interval [0, stop)
(in other words, the interval including start but excluding stop).

- ``RangeIndex.arange(start, stop)``: the index is within the half-open interval
[start, stop).

- ``RangeIndex.arange(start, stop, step)``: the index is within the half-open interval
[start, stop), with spacing between values given by step.

.. note::
When using a non-integer step, such as 0.1, it is often better to use
:py:meth:`~xarray.indexes.RangeIndex.linspace`.

.. note::
``RangeIndex.arange(start=4.0)`` returns a range index in the [0.0, 4.0)
interval, i.e., ``start`` is interpreted as ``stop`` even when it is given
as a unique keyword argument.

Parameters
----------
start : float, optional
Start of interval (default: 0.0). The interval includes this value.
stop : float, optional
End of interval (default: 1.0). In general the interval does not
include this value, except floating point round-off affects the
size of the dimension.
Start of interval. The interval includes this value. The default start
value is 0. If ``stop`` is not given, the value given here is interpreted
as the end of the interval.
stop : float
End of interval. In general the interval does not include this value,
except floating point round-off affects the size of the dimension.
step : float, optional
Spacing between values (default: 1.0).
coord_name : Hashable, optional
Expand Down Expand Up @@ -163,6 +184,18 @@ def arange(
x RangeIndex (start=0, stop=1, step=0.2)

"""
if stop is None:
if start is None:
raise TypeError("RangeIndex.arange() requires stop to be specified")
else:
stop = start
start = None
if start is None:
start = 0.0

if step is None:
step = 1.0

if coord_name is None:
coord_name = dim

Expand Down
46 changes: 43 additions & 3 deletions xarray/tests/test_range_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,51 @@ def create_dataset_arange(
return xr.Dataset(coords=xr.Coordinates.from_xindex(index))


def test_range_index_arange() -> None:
index = RangeIndex.arange(0.0, 1.0, 0.1, dim="x")
@pytest.mark.parametrize(
"args,kwargs",
[
((10.0,), {}),
((), {"stop": 10.0}),
(
(
2.0,
10.0,
),
{},
),
((2.0,), {"stop": 10.0}),
((), {"start": 2.0, "stop": 10.0}),
((2.0, 10.0, 2.0), {}),
((), {"start": 2.0, "stop": 10.0, "step": 2.0}),
],
)
def test_range_index_arange(args, kwargs) -> None:
index = RangeIndex.arange(*args, **kwargs, dim="x")
actual = xr.Coordinates.from_xindex(index)
expected = xr.Coordinates({"x": np.arange(*args, **kwargs)})
assert_equal(actual, expected, check_default_indexes=False)


def test_range_index_arange_error() -> None:
with pytest.raises(TypeError, match=".*requires stop to be specified"):
RangeIndex.arange(dim="x")


def test_range_index_arange_start_as_stop() -> None:
# Weird although probably very unlikely case where only `start` is given
# as keyword argument, which is interpreted as `stop`.
# This has been fixed in numpy (https://github.com/numpy/numpy/pull/17878)
# using Python C API. In pure Python it's more tricky as there's no easy way to know
# whether a value has been passed as positional or keyword argument.
# Note: `pandas.RangeIndex` constructor still has this weird behavior.
index = RangeIndex.arange(start=10.0, dim="x")
actual = xr.Coordinates.from_xindex(index)
expected = xr.Coordinates({"x": np.arange(0.0, 1.0, 0.1)})
expected = xr.Coordinates({"x": np.arange(10.0)})
assert_equal(actual, expected, check_default_indexes=False)


def test_range_index_arange_properties() -> None:
index = RangeIndex.arange(0.0, 1.0, 0.1, dim="x")
assert index.start == 0.0
assert index.stop == 1.0
assert index.step == 0.1
Expand Down
Loading