Skip to content

Feature Proposal: xarray.interactive module #3709

Closed

Description

@TomNicholas

Feature proposal: xarray.interactive module

I've been experimenting with ipython widgets in jupyter notebooks, and I've been working on how we might use them to make xarray more interactive.

Motivation:

For most users who are exploring their data, it will be common to find themselves rerunning the same cells repeatedly but with slightly different values.
In xarray's case that will often be in an .isel() or .sel() call, or selecting variables from a dataset.
IPython widgets allow you to interact with your functions in a very intuitive way, which we could exploit.
There are lots of tutorials on how to interact with pandas data (e.g. this great one), but I haven't seen any for interacting with xarray objects.

Relationship to other libraries:

Some downstream plotting libaries (such as @hvplot) already use widgets when interactively plotting xarray-derived data structures, but they don't seem to go the full N dimensions.
This also isn't something that should be confined to plotting functions - you often choose slices or variables at the start of analysis, not just at the end.
I'll come back to this idea later.

The default ipython widgets are pretty good, but we could write an xarray.interactive module in such a way that downstream developers can easily replace them with their own widgets.

Usage examples:

# imports
import ipywidgets as widgets
import xarray.plot as xplot
import xarray.interactive as interactive

# Load tutorial data
ds = xr.tutorial.open_dataset('air_temperature')['air']

Plotting against multiple dimensions interactively

interactive.isel(da, xplot.plot, lat=10, lon=50)

isel_lat_and_lon

Interactively select a range from a dimension

def plot_mean_over_time(da):
    da.mean(dim=time)
interactive.isel(da, plot_mean_over_time, time=slice(100, 500))

mean_over_time_slice

Animate over one dimension

from ipywidgets import Play
interactive.isel(da, xplot.plot, time=Play())

Play

API ideas:

We can write a function like this

interactive.isel(da, func=xplot.plot, time=10)

which could also be used as a decorator something like this

@interactive.isel(da, time=10)
def plot(da)
    return xplot.plot(da)

It would be nicer to be able to do this

@Interactive(da).isel(time=10)
def plot(da)
    return xplot.plot(da)

but Guido forbade it.

But we can attach these functions to an accessor to get

da.interactive.isel(xplot.plot, time=10)

Other ideas

Select variables from datasets

@interactive.data_vars(da1=ds['n'], da2=ds['T'], ...)
def correlation(da1, da2, ...)
    ...

# Would produce a dropdown list of variables for each dataset

Choose dimensions to apply functions over

@interactive.dims(dim='time')
def mean(da, dim)
    ...
    
# Would produce a dropdown list of dimensions in the dataarray

General interactive.explore() method to see variation over any number of dimensions, the default being all of them.

What do people think about this? Is it something that makes sense to include within xarray itself? (Dependencies aren't a problem because it's fine to have ipywidgets as an optional dependency just for this module.)

Activity

  1. TomNicholas commented on Jan 20, 2020

    @TomNicholas
    MemberAuthor

    Difficulties with method chaining

    Arbitraily long method chaining would be great, i.e.

    da.interactive.isel(time=10).mean('time').plot()

    but I think it will be considerably more complicated.

    The problem is that the way the ipywidgets.interactive() function works means that each time a widget value is altered (e.g. a slider dragged to a new position), then the function wrapped by interactive must be recomputed.
    For single functions that's fine, but for method chaining it means the final .plot() method has to know about all the previous methods back up to the .interactive input.

    I've found a way to get around this, but I'd like some feedback on the approach because it might be needlessly complicated.

    I would like to do it by subclassing to create an InteractiveDataArray, which you could create with an interactive accessor method like

    ida = da.interactive.isel(time=10)

    This class would store the widgets and decorate it's inherited methods to either propagate them (e.g. through ida.reduce()) or display them (e.g. after ida.plot()).
    It would define the _ipython_display_() method so that calling display(ida) revealed the widgets.

    To allow for the final method to recompute all the previous steps, each inherited computation method would be wrapped by a decorator which records the function used and it's arguments.
    That way the final method (which really you know will either be .plot(), or __print__()) can revaluate it's whole history when the slider tells it to recompute.

    I've got a very rough example of this working, but as I said there might be a much easier way...

    method_chaining

  2. TomNicholas commented on Jan 20, 2020

    @TomNicholas
    MemberAuthor

    (also I realise that the suggestion at the end is similar to a task graph of dask.delayed objects, but I assume something will go wrong if I try to wrap dask arrays with xarray dataarrays with dask delayed?)

  3. benbovy commented on Jan 21, 2020

    @benbovy
    Member

    This looks fantastic @TomNicholas!!

    IMHO, I would rather see this maintained in a separate project (something like ipyxarray ? or xarray-interactive as you already suggests). Adding an optional dependency is not really a problem indeed, but it's more about trying to avoid adding too much maintenance burden to this repository (issues/PRs list, CI, etc.).

  4. mathause commented on Jan 21, 2020

    @mathause
    Collaborator

    related: #2034

  5. TomNicholas commented on Jan 21, 2020

    @TomNicholas
    MemberAuthor

    IMHO, I would rather see this maintained in a separate project

    Yeah that's a fair point. I think this is another case where the ecosystem of packages orbiting xarray could do with being more explicitly organised.

    Reasons for direct integration in xarray:

    • Availability to all users: Functionality should be of general interest to anyone using xarray with jupyter, it's not domain-specific at all,
    • Makes writing robust code a bit easier because can then rely on private xarray methods for parsing indexers and so on

    Reasons for a separate xarray-interactive repository:

    • Keeps developer maintenance / issue tracking separate
    • If plotting library-specific interfaces are desired they can be adding without cluttering main repo

    I guess either way I could just write it in a separate repo and if in future we decided to include it in xarray master then move it.

    @philippjfr @rabernat would be interested in your perspectives as developers/users of these downstream libraries? Would this be useful or not really?

  6. philippjfr commented on Jan 21, 2020

    @philippjfr

    This looks really cool and I like the API! I'll have to give it a try to give more detailed feedback. Note that I'm not a core developer of xarray but I also think this is best managed as an external project.

    Just wanted to ask some clarification on some of your comments.

    Some downstream plotting libaries (such as @hvplot) already use widgets when interactively plotting xarray-derived data structures, but they don't seem to go the full N dimensions.

    What do you mean by this? hvPlot does let you explore n-dimensional data using widgets, what is the limitation you were seeing there?

    This also isn't something that should be confined to plotting functions - you often choose slices or variables at the start of analysis, not just at the end.

    This is a good point, but I guess I'm not yet entirely clear on how your proposed APIs would deal with this.

  7. TomNicholas commented on Jan 21, 2020

    @TomNicholas
    MemberAuthor

    This looks really cool and I like the API!

    Great!

    I'll have to give it a try to give more detailed feedback.

    Thanks, but it's definitely not ready for that yet, I'll post here and tag you when it is.

    What do you mean by this? hvPlot does let you explore n-dimensional data using widgets, what is the limitation you were seeing there?

    I had a go with hvPlot's gridded data classes and although it worked well for plotting variation along one dimension with a single slider, I got some errors when I tried to plot N-D data with multiple slider widgets along more than one dimension. It looks like that might have been user error though... I'll compare more closely and raise issues if necessary.

    I'm not yet entirely clear on how your proposed APIs would deal with this.

    I'm referring to the discussion on method chaining: that proposed API (using an InteractiveDataArray) would allow you to interactively select a subset of data

    ida = da.interactive.isel(lat=50, lon=60)

    before specifying the analysis to perform on it

    ida = (ida - ida.mean('time')).std(dim='time')

    and an ida.plot() or compute call on the same object later would still be tied to the original sliders. That's quite different to only being able to create the sliders in the final call.

  8. dcherian commented on Jan 21, 2020

    @dcherian
    Contributor
  9. TomNicholas commented on Jan 21, 2020

    @TomNicholas
    MemberAuthor

    Thanks @dcherian , I hadn't seen those.

    I think the difference between what I'm proposing here and what already exists (e.g. in holoviews, xrviz, etc.) is considering interactivity as something that is useful independent of plotting.

    The aim would be to allow interactive parameterization of arbitrary functions, which could (and often would) be plotting functions, but could actually be anything. That way analysis can be interactively parameterized, and the plotting can be handled by any library. (Plotting libraries could also choose to reuse these interactivity functions, but wouldn't have to.) I think that approach would integrate well with being able to change plotting backends too (#3553).

  10. benbovy commented on Jan 22, 2020

    @benbovy
    Member

    The aim would be to allow interactive parameterization of arbitrary functions, which could (and often would) be plotting functions, but could actually be anything.

    That would be awesome! I have a strong interest in that with xarray-simlab, i.e., setting-up model parameters and running simulations interactively.

  11. jbednar commented on Jan 22, 2020

    @jbednar
    Contributor

    I think the difference between what I'm proposing here and what already exists (e.g. in holoviews, xrviz, etc.) is considering interactivity as something that is useful independent of plotting.

    The interactive widgets in holoviews and xrviz are obtained from Panel, which is a separate library that is already explicitly designed for specifying and constructing interactivity independent of plotting. E.g. we often use Panel widgets with no plotting to set up simulations or analyses interactively, then run whatever we specified. The interactive function in Panel already works much like what you laid out above, unless I'm missing something.

    It sounds like you're hoping for something that is independent of plotting (like Panel) and provides interactive widgets (like Panel) but also has specific support for multidimensional arrays (like HoloViews)? I don't think that's much code, but it could be useful to provide for Xarray in a convenient API.

  12. philippjfr commented on Jan 22, 2020

    @philippjfr

    I think the real power in this proposal is in the ability to chain operations on interactive components using an API that will be familiar to xarray users. We have a similar concept in HoloViews which allows you to build complex processing and visualization pipelines. I'll work through some examples in HoloViz ecosystem to show what is possible there and maybe provide some ideas or approaches that might work here.

    Let's work with a relatively contrived but simple example and load the air_temperature sample dataset:

    airtemps = xr.tutorial.open_dataset('air_temperature')
    ds = hv.Dataset(airtemps)

    In this example you explode your dataset into individual chunks for each longitude, then apply a reduction along the latitude and finally cast the output to a Curve giving us a Curve of the mean temperature at each longitude:

    curves = ds.groupby('lon', dynamic=True).apply.reduce(lat=np.mean).apply(hv.Curve).opts(width=600, framewise=True)

    Screen Shot 2020-01-23 at 12 15 27 AM

    Now we decide we want to resample the data too, so we import the resample operation and apply it to our existing pipeline:

    from holoviews.operation.timeseries import resample
    resample(curves, rule='7d')
    

    Screen Shot 2020-01-23 at 12 15 03 AM

    But really we don't just want to compute the mean we want to pick the reduce function and we also want to be able to set the resampling frequency and pick a color. By combining Panel and HoloViews you can inject widget parameters at every stage:

    function = pn.widgets.Select(name='Function', options={'mean': np.mean, 'min': np.min, 'max': np.max})
    color = pn.widgets.ColorPicker(name='Color', value='#000000')
    rule = pn.widgets.TextInput(name='Rule', value='7d')
    
    obj = (ds.groupby('lon', dynamic=True)
     .apply.reduce(lat=function)
     .apply(hv.Curve)
     .apply.opts(width=600, color=color, framewise=True)
     .apply(resample, rule=rule)
    )
    
    hv_pane = pn.pane.HoloViews(obj)
    
    pn.Row(
        hv_pane[0],
        pn.Column(*hv_pane[1][0], function, color, rule)
    )

    Screen Shot 2020-01-23 at 12 17 13 AM

    So this shows pretty clearly how useful this kind of chaining/pipeline building can be, especially when built on top of an API like xarray which allows for very powerful data manipulation. I don't have enough of a perspective to say how feasible it would be to implement something like this that comprehensively wraps xarray's API but I'd certainly love to see it. Whether it is built on Panel (which I am of course partial to as the author) or ipywidgets or even supporting both.

    My main comments therefore are about the API, it is not clear to me based on what you have said so far which parts of the API are actually interactive, e.g. in this case:

    ida = da.interactive.isel(lat=50, lon=60)
    ida = (ida - ida.mean('time')).std(dim='time')

    Is only sel/isel ever interactive or can other methods also be interactively set? If the answer is no then that's all clear enough and the scope relatively narrow but well defined. If however you intend the entire API (or at least some well defined subset of it) to be interactive then I think there should be some explicit way to declare which parts are interactive and where the values are coming from (and what the values should be if they can't be automatically determined). In the HoloViews example I showed above you explicitly supply widgets but if you don't want users to deal with manually laying things out then you could also just let the user supply the specification of the valid values. Something like in your first example:

    interactive.isel(da, plot_mean_over_time, time=slice(100, 500))

    but expanded to include support for discrete lists of items, explicit widgets, and so on.

    Hope that's at all helpful! I think the idea is really neat and it could be very powerful indeed.

  13. 10 remaining items

  14. max-sixty commented on Apr 26, 2020

    @max-sixty
    Collaborator

    This is very cool, nice work @philippjfr !

  15. StanczakDominik commented on Apr 27, 2020

    @StanczakDominik
    Contributor

    That's amazing. This would single-handedly turn xarray from "nice to have, pretty useful" to "I recommend it to all my friends". I would absolutely love to be able to use it.

  16. jbednar commented on Dec 16, 2020

    @jbednar
    Contributor

    hvPlot's .interactive() support for xarray and pandas was released in in hvPlot 0.7.0 (installable with conda install hvplot=0.7) and is now documented on the website.

    There are a few things I think we can still improve (listed at holoviz/panel#1824, holoviz/panel#1826, holoviz/hvplot#531, holoviz/hvplot#533), but it's already really fun to use -- just take your xarray or pandas pipeline da.method1(val1=arg1).method2(val2=arg2,val3=arg3).plot(), add .interactive, and then substitute a Panel widget or ipywidget for any of the arguments: da.interactive.method1(val1=widget1).method2(val2=arg2,val3=widget2).plot()

    You can use this with the native .plot() plotting, interactive .hvplot() plots, or pretty much anything you can get out of such a pipeline (table, text, etc.). Try it out and let us know how it goes (here, on one of the issues linked above, or in a new issue at https://github.com/holoviz/hvplot/issues)! Thanks for all the suggestions and ideas here...

  17. jbednar commented on Jul 28, 2021

    @jbednar
    Contributor

    Update: hvPlot's .interactive support has been greatly improved and expanded in the new hvPlot 0.7.3 release. It is now showcased at holoviz.org, which introduces how to use hvPlot to build plots, then how to use xarray .interactive and pandas .interactive to add widgets (whether to hvPlot plots or to anything else, including .plot output or tables or xarray reprs). There are still plenty of improvements to make, but apart from documenting .interactive in xarray's docs, I would think this issue can now be closed.

  18. TomNicholas commented on Jul 29, 2021

    @TomNicholas
    MemberAuthor

    @jbednar that all looks amazing! Can't wait to properly try it out.

    Given that much of what I imagined is now available in holoviews, I will close this issue now. But if you would like to raise a PR pointing towards this functionality somewhere in xarray's docs (maybe either as a more detailed description in the Ecosystem page or as a note in the plotting page of the user guide) then that would be welcome!

  19. MarcSkovMadsen commented on Nov 5, 2021

    @MarcSkovMadsen

    Just for completeness. You can find @philippjfr PyData 2021 .interactive talk here https://pydata.org/global2021/schedule/presentation/51/build-polished-data-driven-applications-directly-from-your-pandas-or-xarray-pipelines/. Quite powerful.

    image

    Inspired by that I've created a gist here https://gist.github.com/MarcSkovMadsen/e666503df2aa1d8d047dcb9555b5da6d. It's for a pandas DataFrame. But the principle is the same for xarray.

    hvplot-interactive-speedup15.mp4
  20. TomNicholas commented on Nov 5, 2021

    @TomNicholas
    MemberAuthor

    Just for completeness. You can find @philippjfr PyData 2021 .interactive talk here

    Oh awesome! Can I watch this talk anywhere? That link just seems to have a summary.

  21. jbednar commented on Nov 5, 2021

    @jbednar
    Contributor
  22. MarcSkovMadsen commented on Nov 5, 2021

    @MarcSkovMadsen
  23. MarcSkovMadsen commented on Jan 19, 2022

    @MarcSkovMadsen

    Sophia Yang and I wrote a blog post about hvplot interactive. It's based on Pandas dataframes but it works the same way for Xarray. Check it out https://towardsdatascience.com/the-easiest-way-to-create-an-interactive-dashboard-in-python-77440f2511d1

    the-easiest-way

    You can also find the repo and links to binder+colab here https://github.com/sophiamyang/hvplot_interactive

    hvplot-interactive-binder (2)

  24. nvaytet commented on May 30, 2022

    @nvaytet

    Just been sent a link to this discussion after having worked on something very similar for our project (which resembles Xarray in many ways): scipp/scipp#2573
    I am now wondering if we could somehow use the .interactive approach for our needs instead.

    @philippjfr how much work would it be to implement an .interactive method for our own classes? Our DataArray is slightly different from Xarray's. Thanks!

  25. philippjfr commented on May 30, 2022

    @philippjfr

    We'd probably have to write a so called HoloViews DataInterface for scipp. See the equivalent xarray implementation: https://github.com/holoviz/holoviews/blob/master/holoviews/core/data/xarray.py

  26. nvaytet commented on May 31, 2022

    @nvaytet

    Great, I'll look at that implementation. Thanks!

  27. MarcSkovMadsen commented on Oct 27, 2023

    @MarcSkovMadsen

    FYI. This has concept has now been generalized further by @philippjfr into Reactive Expressions which is now a part of Param. See https://param.holoviz.org/user_guide/Reactive_Expressions.html

    Here are a couple of examples with Panel

    reactive-expressions-basic

    reactive-expressions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions