home / github / issue_comments

Menu
  • Search all tables
  • GraphQL API

issue_comments: 1248876262

This data as json

html_url issue_url id node_id user created_at updated_at author_association body reactions performed_via_github_app issue
https://github.com/pydata/xarray/issues/7031#issuecomment-1248876262 https://api.github.com/repos/pydata/xarray/issues/7031 1248876262 IC_kwDOAMm_X85KcFbm 35968931 2022-09-16T03:24:59Z 2022-09-16T03:31:50Z MEMBER

I think this version does something sensible for all slice cases

```python from xarray.core.indexes import ( PandasIndex, IndexSelResult, _query_slice ) from xarray.core.indexing import _expand_slice

class PeriodicBoundaryIndex(PandasIndex): """ An index representing any 1D periodic numberline.

Implementation subclasses a normal xarray PandasIndex object but intercepts indexer queries.
"""
period: float
_min: float
_max: float

__slots__ = ("index", "dim", "coord_dtype", "period", "_max", "_min")

def __init__(self, *args, period=360, **kwargs):
    super().__init__(*args, **kwargs)
    self.period = period
    self._min = self.index.min()
    self._max = self.index.max()

@classmethod
def from_variables(self, variables, options):
    obj = super().from_variables(variables, options={})
    obj.period = options.get("period", obj.period)
    return obj

def _wrap_periodically(self, label_value: float) -> float:
    return self._min + (label_value - self._max) % self.period

def _split_slice_across_boundary(self, label: slice) -> np.ndarray:
    """
    Splits a slice into two slices, one either side of the boundary,
    finds the corresponding indices, concatenates them, and returns them,
    ready to be passed to .isel().
    """
    first_slice = slice(label.start, self._max, label.step)
    second_slice = slice(self._min, label.stop, label.step)

    first_as_index_slice = _query_slice(self.index, first_slice)
    second_as_index_slice = _query_slice(self.index, second_slice)

    first_as_indices = _expand_slice(first_as_index_slice, self.index.size)
    second_as_indices = _expand_slice(second_as_index_slice, self.index.size)

    wrapped_indices = np.concatenate([first_as_indices, second_as_indices])
    return wrapped_indices

def sel(
    self, labels: dict[Any, Any], method=None, tolerance=None
) -> IndexSelResult:
    """Remaps labels outside of the indexes' range back to integer indices inside the range."""

    assert len(labels) == 1
    coord_name, label = next(iter(labels.items()))

    if isinstance(label, slice):
        start, stop, step = label.start, label.stop, label.step
        if stop < start:
            return super().sel({coord_name: []})

        assert self._min < self._max

        wrapped_start = self._wrap_periodically(label.start)
        wrapped_stop = self._wrap_periodically(label.stop)
        wrapped_label = slice(wrapped_start, wrapped_stop, step)

        if wrapped_start < wrapped_stop:
            # simple case of slice not crossing boundary
            return super().sel({coord_name: wrapped_label})
        else:  # wrapped_stop < wrapped_start:
            # nasty case of slice crossing boundary
            wrapped_indices = self._split_slice_across_boundary(wrapped_label)
            return IndexSelResult({self.dim: wrapped_indices})

    else:
        # just a scalar / array of scalars
        wrapped_label = self._wrap_periodically(label)
        return super().sel({coord_name: wrapped_label}, method=method, tolerance=tolerance)

def __repr__(self) -> str:
    return f"PeriodicBoundaryIndex(period={self.period})"

```

```python lon_coord = xr.DataArray(data=np.linspace(-180, 180, 19), dims="lon") da = xr.DataArray(data=np.sin(180*lon_coord), dims="lon", coords={"lon": lon_coord})

world = da.drop_indexes("lon").set_xindex("lon", index_cls=PeriodicBoundaryIndex, period=360) ```

```python world.sel(lon=slice(60, 120), method="nearest")

<xarray.DataArray (lon: 4)>

array([-0.71424378, -0.87270922, -0.9701637 , -0.99979417])

Coordinates:

* lon (lon) float64 60.0 80.0 100.0 120.0

```

```python world.sel(lon=slice(160, 210), method="nearest")

<xarray.DataArray (lon: 4)>

array([-0.85218366, -0.68526211, 0.68526211, 0.85218366])

Coordinates:

* lon (lon) float64 160.0 180.0 -180.0 -160.0

```

```python world.sel(lon=slice(-210, -160), method="nearest")

<xarray.DataArray (lon: 4)>

array([-0.85218366, -0.68526211, 0.68526211, 0.85218366])

Coordinates:

* lon (lon) float64 160.0 180.0 -180.0 -160.0

```

Unsure as to whether this next one counts as an "intuitive" result or not

```python world.sel(lon=slice(-210, 210), method="nearest")

<xarray.DataArray (lon: 4)>

array([-0.85218366, -0.68526211, 0.68526211, 0.85218366])

Coordinates:

* lon (lon) float64 160.0 180.0 -180.0 -160.0

```

```python world.sel(lon=slice(120, 60), method="nearest")

<xarray.DataArray (lon: 0)>

array([], dtype=float64)

Coordinates:

* lon (lon) float64

```

{
    "total_count": 1,
    "+1": 1,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
  1372035441
Powered by Datasette · Queries took 0.728ms · About: xarray-datasette