import xarray as xr
import hvplot.xarray # noqa
import hvplot.pandas # noqa
import panel as pn
import panel.widgets as pnw
import ipywidgets as ipw
Interactive command-line or notebook interfaces are incredibly powerful tools for quickly doing exploratory analysis, letting you supply arguments to Python methods and functions and see the results immediately. However, this process of exploration can be slow and awkward for large parameter spaces because it requires manually typing each argument value. To further ease exploratory workflows, hvPlot ships with a convenient .interactive
API, which mirrors the regular API of your favorite data analysis libraries like Pandas, Dask, and xarray but makes it possible to pass in widgets for each argument value, not just a constant. When the widgets are used, the output will dynamically update the full pipeline of method calls so that it works just as if that particular value had been specified in the call being wrapped.
In this user guide we will explore how to use the .interactive API on xarray and pandas objects:
ds = xr.tutorial.load_dataset('air_temperature')
ds
We can supply both regular values, widgets and parameters as arguments to methods on the .interactive
accessor. Here, we'll use widgets from the Panel library. The repr of the resulting object will contain a layout of the widget and a view of the resulting output:
slider = pnw.IntSlider(name='time', start=0, end=10)
ds.air.interactive(width=800).isel(time=slider)
You can also use widgets from the ipywidgets library:
slider = ipw.IntSlider(description='time', min=0, max=10)
ds.air.interactive(width=800).isel(time=slider)
For Panel widgets, we can let .interactive automatically configure the widget, which is particularly convenient when working with DiscreteSlider
widgets:
ds.air.interactive(width=800).sel(time=pnw.DiscreteSlider)
When accessing a method on the .interactive
accessor it will transparently mirror the docstring of the equivalent method in the underlying library being wrapped:
print(ds.air.interactive.isel.__doc__)
One of the most useful aspects of the .interactive API is to feed the output of chained method calls into a plot.
The output can be almost anything, such as the HTML repr (above) or a matplotlib plot:
ds.air.interactive.sel(time=pnw.DiscreteSlider).plot()
If we like, we can animate the output with a Player
widget, and customize the location of the widget using the loc
keyword argument to .interactive
:
time = pnw.Player(name='time', start=0, end=10, loop_policy='loop', interval=100)
ds.air.interactive(loc='bottom').isel(time=time).plot()
We can also make use of the .hvplot
method to get fully interactive Bokeh-based plots:
slider = pnw.FloatSlider(name='quantile', start=0, end=1)
ds.air.interactive.quantile(slider, dim='time').hvplot(data_aspect=1)
You can chain any number of methods, with as many widgets controlling steps in this pipeline as you wish:
q = pnw.FloatSlider(name='quantile', start=0, end=1)
(ds.air.interactive(loc='left')
.sel(time=pnw.DiscreteSlider)
.quantile(q=q, dim='lon')
.hvplot(aspect=1))
We can also use a RangeSlider
to select a slice and compute the mean over that range instead of selecting a specific time:
range_slider = pnw.IntRangeSlider
(ds.air.interactive
.isel(time=range_slider)
.mean('time')
.hvplot())
.interactive
supports arbitrary chains of method calls, including anything that is supported by your data object. For instance, you can even convert your xarray object into a dataframe using .to_dataframe
, then call pandas methods:
ds.air.interactive.sel(lat=pnw.DiscreteSlider).to_dataframe().groupby('time').mean().hvplot('time', 'air')
You can further transform your output, if desired, by applying math operators on the interactive object:
slider = pnw.IntSlider(name='time', start=0, end=10)
baseline = ds.air.mean().item()
baseline
ds.air.interactive(width=800).isel(time=slider).mean().item() - baseline
You can even do math with a widget:
slider = pnw.IntSlider(name='time', start=0, end=10)
offset = pnw.IntSlider(name='offset', start=0, end=500)
ds.air.interactive.isel(time=slider).mean().item() + offset
Math operators work with array data as well, such as the time-averaged value of each array value:
diff = ds.air.interactive.sel(time=pnw.DiscreteSlider) - ds.air.mean('time')
kind = pnw.Select(options=['contour', 'contourf', 'image'])
diff.hvplot(cmap='RdBu_r', clim=(-20, 20), kind=kind)
If you want more control over the layout, you can use any of the features from Panel:
diff = ds.air.interactive.sel(time=pnw.DiscreteSlider) - ds.air.mean('time')
kind = pnw.Select(options=['contourf', 'contour', 'image'], value='image')
interactive = diff.hvplot(cmap='RdBu_r', clim=(-20, 20), kind=kind)
pn.Column(
pn.Row(
pn.panel("https://hvplot.holoviz.org/assets/hvplot-wm.png", width=100),
pn.Spacer(width=20),
pn.Column(
pn.panel("## Select a time and type of plot", width=400),
interactive.widgets()
),
pn.panel("https://panel.holoviz.org/_static/logo_stacked.png", width=100)
),
interactive.panel()
).servable()
As you can see, the .interactive
functionality makes it simple to work interactively with your data, letting you use widgets about as easily as any other method argument! See the Panel or ipwidgets docs for the various widgets and other functionality available.