Skip to content

Commit 1e2f3d0

Browse files
authored
Integration with Xarray NDPointIndex (#44)
* update pre-commit config Temporarily remove flake8 (will likely use ruff later). * Add TreeAdapter classes for xarray's NDPointIndex * typos + fixes * fix pre-commit * doc: pix Sphinx to version 8 xarray-contrib/sphinx-autosummary-accessors#165 * add deprecation (future) warnings * doc: update API reference * doc: update example notebooks * specify xarray min supported version * ci: bump min python version tested * update tests * api docstrings: add deprecation warnings * doc: update release notes
1 parent 6062bef commit 1e2f3d0

15 files changed

Lines changed: 390 additions & 275 deletions

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
fail-fast: false
2020
matrix:
2121
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
22-
python-version: ["3.10", "3.13"]
22+
python-version: ["3.11", "3.13"]
2323
steps:
2424
- name: Checkout
2525
uses: actions/checkout@v6

doc/api.rst

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,32 @@ API Reference
55

66
This page provides an auto-generated summary of Xoak's API.
77

8+
.. currentmodule:: xoak
9+
10+
Xarray NDPointIndex tree adapters
11+
---------------------------------
12+
13+
The following classes may be used with :py:class:`xarray.indexes.NDPointIndex`, they can
14+
be passed as ``tree_adapter_cls`` option value via :py:meth:`xarray.Dataset.set_xindex` or
15+
:py:meth:`xarray.DataArray.set_xindex`.
16+
17+
.. autosummary::
18+
:toctree: _api_generated/
19+
20+
S2PointTreeAdapter
21+
SklearnBallTreeAdapter
22+
SklearnGeoBallTreeAdapter
23+
SklearnKDTreeAdapter
24+
825
.. currentmodule:: xarray
926

1027
Dataset.xoak
1128
------------
1229

30+
.. warning::
31+
32+
This API is deprecated and will be removed in a future version of Xoak.
33+
1334
This accessor extends :py:class:`xarray.Dataset` with all the methods and
1435
properties listed below. Proper use of this accessor should be like:
1536

@@ -40,6 +61,10 @@ properties listed below. Proper use of this accessor should be like:
4061
DataArray.xoak
4162
--------------
4263

64+
.. warning::
65+
66+
This API is deprecated and will be removed in a future version of Xoak.
67+
4368
The accessor above is also registered for :py:class:`xarray.DataArray`.
4469

4570
**Properties**
@@ -58,39 +83,3 @@ The accessor above is also registered for :py:class:`xarray.DataArray`.
5883

5984
DataArray.xoak.set_index
6085
DataArray.xoak.sel
61-
62-
Indexes
63-
-------
64-
65-
.. currentmodule:: xoak
66-
67-
.. autosummary::
68-
:toctree: _api_generated/
69-
70-
IndexAdapter
71-
IndexRegistry
72-
73-
**Xoak's built-in index adapters**
74-
75-
.. currentmodule:: xoak.index.scipy_adapters
76-
77-
.. autosummary::
78-
:toctree: _api_generated/
79-
80-
ScipyKDTreeAdapter
81-
82-
.. currentmodule:: xoak.index.sklearn_adapters
83-
84-
.. autosummary::
85-
:toctree: _api_generated/
86-
87-
SklearnKDTreeAdapter
88-
SklearnBallTreeAdapter
89-
SklearnGeoBallTreeAdapter
90-
91-
.. currentmodule:: xoak.index.s2_adapters
92-
93-
.. autosummary::
94-
:toctree: _api_generated/
95-
96-
S2PointIndexAdapter

doc/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dependencies:
1010
- numpy
1111
- pys2index
1212
- scikit-learn
13-
- sphinx
13+
- sphinx=8.2.3
1414
- sphinx-autosummary-accessors
1515
- pydata-sphinx-theme=0.15.4
1616
- sphinx-book-theme=1.1.4

doc/examples/custom_indexes.ipynb

Lines changed: 29 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"source": [
77
"# Custom Indexes\n",
88
"\n",
9-
"While Xoak provides some built-in index adapters, it is easy to adapt and register new indexes. "
9+
"Xoak provides some built-in adapters for [xarray.indexes.NDPointIndex](https://docs.xarray.dev/en/stable/generated/xarray.indexes.NDPointIndex.html) ; it is easy to create custom ones."
1010
]
1111
},
1212
{
@@ -16,26 +16,7 @@
1616
"outputs": [],
1717
"source": [
1818
"import numpy as np\n",
19-
"import xarray as xr\n",
20-
"import xoak"
21-
]
22-
},
23-
{
24-
"cell_type": "markdown",
25-
"metadata": {},
26-
"source": [
27-
"An instance of `xoak.IndexRegistry` by default contains a collection of Xoak built-in index adapters:"
28-
]
29-
},
30-
{
31-
"cell_type": "code",
32-
"execution_count": null,
33-
"metadata": {},
34-
"outputs": [],
35-
"source": [
36-
"ireg = xoak.IndexRegistry()\n",
37-
"\n",
38-
"ireg"
19+
"import xarray as xr"
3920
]
4021
},
4122
{
@@ -44,9 +25,7 @@
4425
"source": [
4526
"## Example: add a brute-force \"index\"\n",
4627
"\n",
47-
"Every Xoak supported index is a subclass of `xoak.IndexAdapter` that must implement the `build` and `query` methods. The `IndexRegistry.register` decorator may be used to register a new index adpater.\n",
48-
"\n",
49-
"Let's create and register a new adapter, which simply performs brute-force nearest-neighbor lookup by computing the pairwise distances between all index and query points and finding the minimum distance. "
28+
"This example adapter simply performs brute-force nearest-neighbor lookup by computing the pairwise distances between all index and query points and finding the minimum distance. "
5029
]
5130
},
5231
{
@@ -55,43 +34,32 @@
5534
"metadata": {},
5635
"outputs": [],
5736
"source": [
37+
"from collections.abc import Mapping\n",
38+
"from typing import Any\n",
39+
"\n",
5840
"from sklearn.metrics.pairwise import pairwise_distances_argmin_min\n",
41+
"from xarray.indexes.nd_point_index import TreeAdapter\n",
5942
"\n",
6043
"\n",
61-
"@ireg.register('brute_force')\n",
62-
"class BruteForceIndex(xoak.IndexAdapter):\n",
44+
"class BruteForceTreeAdapter(TreeAdapter):\n",
6345
" \"\"\"Brute-force nearest neighbor lookup.\"\"\"\n",
6446
" \n",
65-
" def build(self, points):\n",
66-
" # there is no index to build here, just return the points\n",
67-
" return points\n",
68-
" \n",
69-
" def query(self, index, points):\n",
70-
" positions, distances = pairwise_distances_argmin_min(points, index)\n",
71-
" return distances, positions\n"
72-
]
73-
},
74-
{
75-
"cell_type": "markdown",
76-
"metadata": {},
77-
"source": [
78-
"This new index now appears in the registry:"
79-
]
80-
},
81-
{
82-
"cell_type": "code",
83-
"execution_count": null,
84-
"metadata": {},
85-
"outputs": [],
86-
"source": [
87-
"ireg"
47+
" def __init__(self, points: np.ndarray, options: Mapping[str, Any]):\n",
48+
" self._index_points = points\n",
49+
"\n",
50+
" def query(self, points: np.ndarray) -> tuple[np.ndarray, np.ndarray]:\n",
51+
" positions, distances = pairwise_distances_argmin_min(points, self._index_points)\n",
52+
" return distances, positions\n",
53+
"\n",
54+
" def equals(self, other: \"BruteForceTreeAdapter\") -> bool:\n",
55+
" return np.array_equal(self._index_points, other._index_points)\n"
8856
]
8957
},
9058
{
9159
"cell_type": "markdown",
9260
"metadata": {},
9361
"source": [
94-
"Let's use this index in the basic example below:"
62+
"Let's use this adapter in the basic example below:"
9563
]
9664
},
9765
{
@@ -113,7 +81,11 @@
11381
")\n",
11482
"\n",
11583
"# set the brute-force index (doesn't really build any index in this case)\n",
116-
"ds_mesh.xoak.set_index(['meshx', 'meshy'], ireg.brute_force)\n",
84+
"ds_mesh = ds_mesh.set_xindex(\n",
85+
" ['meshx', 'meshy'],\n",
86+
" xr.indexes.NDPointIndex,\n",
87+
" tree_adapter_cls=BruteForceTreeAdapter,\n",
88+
")\n",
11789
"\n",
11890
"# create trajectory points\n",
11991
"ds_trajectory = xr.Dataset({\n",
@@ -122,29 +94,23 @@
12294
"})\n",
12395
"\n",
12496
"# select mesh points\n",
125-
"ds_selection = ds_mesh.xoak.sel(\n",
97+
"ds_selection = ds_mesh.sel(\n",
12698
" meshx=ds_trajectory.trajx,\n",
127-
" meshy=ds_trajectory.trajy\n",
99+
" meshy=ds_trajectory.trajy,\n",
100+
" method=\"nearest\",\n",
128101
")\n",
129102
"\n",
130103
"# plot results\n",
131104
"ds_trajectory.plot.scatter(x='trajx', y='trajy', c='k', alpha=0.7);\n",
132105
"ds_selection.plot.scatter(x='meshx', y='meshy', hue='field', alpha=0.9);"
133106
]
134-
},
135-
{
136-
"cell_type": "code",
137-
"execution_count": null,
138-
"metadata": {},
139-
"outputs": [],
140-
"source": []
141107
}
142108
],
143109
"metadata": {
144110
"kernelspec": {
145-
"display_name": "Python [conda env:xoak_dev]",
111+
"display_name": "Python 3 (ipykernel)",
146112
"language": "python",
147-
"name": "conda-env-xoak_dev-py"
113+
"name": "python3"
148114
},
149115
"language_info": {
150116
"codemirror_mode": {
@@ -156,7 +122,7 @@
156122
"name": "python",
157123
"nbconvert_exporter": "python",
158124
"pygments_lexer": "ipython3",
159-
"version": "3.8.6"
125+
"version": "3.13.5"
160126
}
161127
},
162128
"nbformat": 4,

0 commit comments

Comments
 (0)