-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Flexible indexes refactoring notes #4979
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a good summary, @benbovy.
One thing I'm missing is duckarray support, though. Not sure if this is realistic, but I'm hoping to reduce the maintenance burden on duckarray support libraries (such as pint-xarray
) as much as possible: subclassing every new index class (or having the index provider explicitly add duckarray support) seems a bit too much work.
place: I think moving it into the documentation should be fine but a separate repository would probably be a bit cleaner (especially if we expect to write more design documents).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, thanks for writing this up!
I would suggest keeping this in the xarray repository rather than letting it get lost somewhere else (it is very much about xarray's code, after all). Perhaps we could make a new sub-directory design_notes
?
Thanks for your comments @keewis and @shoyer! I think it's better for now to keep having this discussion and these notes in the Xarray repository, for more visibility. We could still move this elsewhere later if this PR becomes too cluttered, as there are potentially many aspects we can discuss about. |
I haven't looked much at If you are referring to the issue that you describe in your comment #525 (comment), the refactoring should decouple indexes from the coordinates, leaving the latter "just" as if they were regular variables (thus with duckarray support). What is currently possible with non-index coordinates should be possible with all coordinates. Actually, I'm not sure that we'll need to keep Or maybe you're referring to unit-aware indexing (what @shoyer mentioned in #525 (comment))? In this case I'm not sure how we could do that without having specific index classes for that purpose. Maybe some pre/post indexing hooks in Xarray that could be used, e.g., to convert indexer units into the coordinate units? |
I was referring to both, but I agree that the first point would be fixed by the decoupling. The second point is just as important, though. We're currently adding wrapper methods to |
It's great to be able to follow along with the discussion here! I'm definitely interested in seeing where the duck array index support ends up. One use-case motivated question: the flexible indexes refactoring has also been pointed to as the resolution to #2233, where multidimensional coordinates have the same name as one of their dimensions. I wasn't quite able to tell through the narrative here if that has been addressed along the way yet or not ("A. only 1D coordinates with a name matching their dimension name" for implicit index creation does seem to get close though). So, would it be worth directly addressing #2233 here, or should that wait? |
Or similarly to class MyDuckArray:
...
def _sel_(self, indexer):
"""Prepare the label-based indexer to conform to this coordinate array."""
...
return new_indexer
... |
I think #2233 will be addressed by the index refactoring here. I don't see any issue with multidimensional coordinates having the same name as one of their dimensions once indexes are decoupled from dimensions/coordinates. I might still be missing something, though. |
One thing I did not see discussed is alignment (or did I miss this?). Currently alignment is based on the "indexes" or well I guess One (potential) edge case are import xarray as xr
da1 = xr.DataArray([1, 2, 3], dims="x", coords=dict(time=("x", [1, 2, 3]), exp=("x", ["a", "a", "b"])))
da2 = xr.DataArray([2, 3, 4], dims="x", coords=dict(time=("x", [1, 2, 3]), exp=("x", ["a", "a", "a"])))
da1 = da1.set_index(x=("time", "exp"))
da2 = da2.set_index(x=("time", "exp"))
da1 + da2 <xarray.DataArray (x: 2)>
array([3, 5])
Coordinates:
* x (x) MultiIndex
- time (x) int64 1 2
- exp (x) object 'a' 'a' |
Alignment hasn't been discussed yet here, but it should! Some quick thoughts:
|
There are also high-level methods that could use indexes in non-trivial ways. These methods become complicated when considering nD versions of
Another perhaps-unintended use-case is that various accessors will try to use An example is |
Thanks @dcherian for listing those methods here, that's something worth to keep in mind! I think that for now it would be reasonable to restrict those methods to the indexes that are currently available in Xarray instead of trying to extend the API of Xarray index wrappers in order to support those special cases. I guess it's ok for "default" or "common" xarray indexes to provide extra functionality that could not be implemented in 3rd party indexes, as well as it would be ok for 3rd-party indexes to provide non-standard, extra functionality that would be reused for methods implemented in DataArray/Dataset accessors.
Are the |
@rabernat @jbusecke (xgcm) @snowman2 @fmaussion @djhoese (crs) it would be interesting to have your thoughts here. What would be the pros and cons of:
A major advantage is that using a custom index, there's no need to encapsulate a Dataset/DataArray into a higher level structure (e.g., Are there any other challenges and/or opportunities? (sorry, it has probably been already discussed elsewhere. There's too many places to look for :-) ). |
For reference for how rioxarray does things: https://corteva.github.io/rioxarray/stable/getting_started/crs_management.html
|
I agree, this would be really nice. One challenge is that often it is not advisable to explicitly build the coordinate arrays that correspond to such grids For example, consider a satellite image: 2D lat/lon arrays could be as expensive to store as the image itself, even though the values can be computed on the fly with very cheap arithmetic. To fill this gap, we need first-class support for custom lazy arrays in xarray. If you read the documentation for the backend refactor (#4810), you'll see that we do have a minimal version of this internally in the form of |
Ah yes, making it more easily reusable would be welcome indeed. I guess that such lazy arrays will be already needed for the creation of coordinates from the levels of an existing |
Just wanted to mention in case it comes up later, this is true for some datasets and for others the lon/lats are not uniformly spaced so they can't be calculated (just based on the way the satellite instrument works). They have to be loaded from the original dataset (on-disk file). For a while in the Satpy library we were storing 2D dask arrays for the lon/lat coordinates until we realized xarray was sometimes computing them and we didn't want that. |
Fully agree. We should raise nice error messages when possible. I just wanted to raise awareness about this issue (i.e. methods that use indexes in non-trivial ways).
Good point! I hadn't thought of it that way.
Yes! I'm v. happy to see this discussion is happening :)
For |
That's good to know, thanks! Like it may create specific indexes for time coordinates, I could imagine Xarray's |
Thanks everyone for your comments so far!! They have been really helpful in improving the notes! This is now ready for another round of review. I've tried to include all the points raised in the discussion above. I also marked all the conversations as resolved even though it's still open for discussion! (it's just a way to "reset" them for more clarity). I'll move the notes into a With the last commits, I think that the notes now cover most of the aspects regarding the use of indexes in Xarray. The goal with these notes is not to settle every detail of the refactoring (decisions can be made while iterating on the implementation), but rather describe the big picture and outline the main opportunities and challenges. Referring to the notes will help throughout the implementation. Hopefully it will allow more Xarray users and devs sharing their point of views to make sure we're not missing anything important here. One thing that is not in the notes: to which acceptable extent this refactoring may introduce breaking changes? I think that it will be hard to avoid any breaking change. That said, as the index refactoring would rather bring internal data structures to the light I don't expect many things to break (at least, not the things that 90% of Xarray users often rely on). Hardest part will probably be to ensure a smooth transition while updating the API that is too specific to |
My opinion is that breaking changes are OK if done with care. The main thing to keep in mind is that users dealing with change typically will not directly benefit from it. So any breaking changes need to be intentional, and happen all at once in a major release. We also need to issue warnings when possible. Depending on the magnitude of these changes, this could be a good reason to declare "xarray 1.0" |
FLEXIBLE_INDEXES_NOTES.md
Outdated
- some indexes that can't be indexed could still be automatically (re)built in the new Dataset/DataArray | ||
- like for example building a new `KDTree` index from the selection of a subset of an initial collection of data points | ||
- this is not always desirable, though, as indexes may be expensive to build | ||
- a more reasonable option would be to explicitly re-build the index, e.g., using `.set_index()` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great @benbovy! My comments are mostly just clarification questions and small rephrasing suggestions. The discussion above caught most of the big stuff.
FLEXIBLE_INDEXES_NOTES.md
Outdated
- index equality might involve more than just the labels: for example a spatial index might be used to check if the coordinate system (CRS) is identical for two sets of coordinates | ||
- some indexes might implement inexact alignment, like in [#4489](https://github.com/pydata/xarray/pull/4489) or a `KDTree` index that selects nearest-neighbors within a given tolerance | ||
- alignment may be "multi-dimensional", i.e., the `KDTree` example above vs. dimensions aligned independently of each other | ||
- we need to decide what to do when one dimension has more than one index that supports alignment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and elsewhere it sounds like it could be useful to define an index precedence order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this could be a solution to all situations where the index to pick is ambiguous (although it is not yet clear to me how often such situation could happen). This would introduce more complexity, though:
- we need to extend the API so that users can define this order
- we need to ensure strict precedence order
- does it make sense to use the same precedence order for all operations? (e.g., alignment vs. selection)
FLEXIBLE_INDEXES_NOTES.md
Outdated
- `resample` (`CFTimeIndex` and a `DatetimeIntervalIndex`) | ||
- `DatetimeAccessor` & `TimedeltaAccessor` properties (`CFTimeIndex` and a `DatetimeIntervalIndex`) | ||
- `interp` & `interpolate_na`, | ||
- with `IntervalIndex`, these become regridding operations. Should we support hooks for these operations? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think indexes should optionally support interpolation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would certainly be useful.
Co-authored-by: Ryan Abernathey <ryan.abernathey@gmail.com>
Co-authored-by: Ryan Abernathey <ryan.abernathey@gmail.com>
Co-authored-by: Ryan Abernathey <ryan.abernathey@gmail.com>
Co-authored-by: Ryan Abernathey <ryan.abernathey@gmail.com>
Thanks for your reviews @shoyer and @rabernat! I've updated the notes according to your comments. From my point of view this is ready, but we can leave this PR open for another few days in case anyone else wants to add some comments (@pydata/xarray). Next week I'll start with the implementation (I'll take on the open PR in https://github.com/pydata/xarray/projects/1 and will do only internal refactoring making sure that all tests are passing). |
shall we merge? |
Yes! I wanted to wait for the bi-weekly dev meeting but I've just missed it (PST/PDT -> 🤦 -> sorry). Let's merge this and continue the discussion in follow-up issues/PRs. |
…indow * upstream/master: Fix regression in decoding large standard calendar times (pydata#5050) Fix sticky sidebar responsiveness on small screens (pydata#5039) Flexible indexes refactoring notes (pydata#4979) add a install xarray step to the upstream-dev CI (pydata#5044) Adds Dataset.query() method, analogous to pandas DataFrame.query() (pydata#4984) run tests on python 3.9 (pydata#5040) Add date attribute to datetime accessor (pydata#4994) 📚 New theme & rearrangement of the docs (pydata#4835) upgrade ci-trigger to the most recent version (pydata#5037) GH5005 fix documentation on open_rasterio (pydata#5021) GHA for automatically canceling previous CI runs (pydata#5025) Implement GroupBy.__getitem__ (pydata#3691) conventions: decode unsigned integers to signed if _Unsigned=false (pydata#4966) Added support for numpy.bool_ (pydata#4986) Add additional str accessor methods for DataArray (pydata#4622)
…-tasks * upstream/master: Fix regression in decoding large standard calendar times (pydata#5050) Fix sticky sidebar responsiveness on small screens (pydata#5039) Flexible indexes refactoring notes (pydata#4979) add a install xarray step to the upstream-dev CI (pydata#5044) Adds Dataset.query() method, analogous to pandas DataFrame.query() (pydata#4984) run tests on python 3.9 (pydata#5040) Add date attribute to datetime accessor (pydata#4994) 📚 New theme & rearrangement of the docs (pydata#4835) upgrade ci-trigger to the most recent version (pydata#5037) GH5005 fix documentation on open_rasterio (pydata#5021) GHA for automatically canceling previous CI runs (pydata#5025) Implement GroupBy.__getitem__ (pydata#3691) conventions: decode unsigned integers to signed if _Unsigned=false (pydata#4966) Added support for numpy.bool_ (pydata#4986) Add additional str accessor methods for DataArray (pydata#4622) add polyval to polyfit see also (pydata#5020) mention map_blocks in the docstring of apply_ufunc (pydata#5011) Switch backend API to v2 (pydata#4989) WIP: add new backend api documentation (pydata#4810) pin netCDF4=1.5.3 in min-all-deps (pydata#4982)
As a preliminary step before I take on the refactoring and implementation of flexible indexes in Xarray for the next few months, I reviewed the status of https://github.com/pydata/xarray/projects/1 and started compiling partially implemented or planned changes, thoughts, etc. into a single document that may serve as a basis for further discussion and implementation work.
It's still very much work in progress (I will update it regularly in the forthcoming days) and it is very open to discussion (we can use this PR for that)!
I'm not sure if Xarray's root folder is a good place for this document, though. We could move this into a new repository in
xarray-contrib
(that could also host other enhancement proposals) if that's necessary.I'm looking forward to getting started on this and to getting your thoughts/feedback!