-
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement interactive API for all data backends (#505)
- Loading branch information
1 parent
78639c4
commit a4fb538
Showing
7 changed files
with
825 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import xarray as xr\n", | ||
"import hvplot.xarray # noqa\n", | ||
"import hvplot.pandas # noqa\n", | ||
"import panel as pn\n", | ||
"import ipywidgets as ipw" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"An interactive REPL or notebook interfaces are incredibly powerful tool for quickly doing exploratory analysis, however they generally still require manually changing arguments to method and function calls to see their effect. To further ease exploratory workflows hvPlot ships with a so called `interactive` API, which mirrors the regular API of your favorite data analysis libraries like Pandas, Dask, and xarray but makes it possible to replace constant arguments with widgets that dynamically update the output of the method calls and which tranparently chain, behaving just like the object that is being wrapped.\n", | ||
"\n", | ||
"In this user guide we will explore how to use the interactive API on xarray and pandas objects:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"ds = xr.tutorial.load_dataset('air_temperature')\n", | ||
"ds" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Interactive widgets" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can supply both regular values, widgets and parameters as arguments to methods on the `interactive` accessor. The repr of the resulting object will contain a layout of the widget and a view of the resulting output:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"slider = pn.widgets.IntSlider(name='time', start=0, end=10)\n", | ||
"\n", | ||
"ds.air.interactive(width=800).isel(time=slider)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"ipywidgets are also supported as dynamic arguments:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"slider = ipw.IntSlider(description='time', min=0, max=10)\n", | ||
"\n", | ||
"ds.air.interactive(width=800).isel(time=slider)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can also let interactive automatically populate a widget, which is particularly useful when working with `DiscreteSlider` widgets:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"ds.air.interactive(width=800).sel(time=pn.widgets.DiscreteSlider)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Docstrings\n", | ||
"\n", | ||
"When accessing a method on the `interactive` accessor it will transparently mirror the docstring of the equivalent method in the underlying library being wrapped:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"print(ds.air.interactive.isel.__doc__)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Plotting\n", | ||
"\n", | ||
"One of the most useful aspects of the interactive API is to feed the output of chained method calls into a plot." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Matplotlib" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The output can be almost anything, the HTML repr or a matplotlib plot:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"ds.air.interactive.sel(time=pn.widgets.DiscreteSlider).plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can animate the output with a `Player` widget, and customize the location of the widget using the `loc` keyword argument to the `interactive` accessor:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"time = pn.widgets.Player(name='time', start=0, end=10, loop_policy='loop', interval=100)\n", | ||
"\n", | ||
"ds.air.interactive(loc='bottom').isel(time=time).plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### hvPlot" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can also make use of the `.hvplot` method to get interactive plots:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"slider = pn.widgets.FloatSlider(name='quantile', start=0, end=1)\n", | ||
"\n", | ||
"ds.air.interactive.quantile(slider, dim='time').hvplot(data_aspect=1)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"You can chain any number of methods, with as many widgets controlling steps in this pipeline as you wish:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"q = pn.widgets.FloatSlider(name='quantile', start=0, end=1)\n", | ||
"\n", | ||
"(ds\n", | ||
" .air\n", | ||
" .interactive(loc='left')\n", | ||
" .sel(time=pn.widgets.DiscreteSlider)\n", | ||
" .quantile(q=q, dim='lon')\n", | ||
" .hvplot(aspect=1)\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can also use a `RangeSlider` to select a slice and compute the mean:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"range_slider = pn.widgets.RangeSlider(start=0, end=len(ds.time), step=1)\n", | ||
"\n", | ||
"(ds\n", | ||
" .air\n", | ||
" .interactive\n", | ||
" .isel(time=range_slider)\n", | ||
" .mean('time')\n", | ||
" .hvplot()\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Note that `.interactive` can be chained arbitrarily, e.g. we can even convert to a dataframe using `to_dataframe` and then call pandas methods:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"lat = pn.widgets.DiscreteSlider(name='Latitude', options=sorted(ds.lat.values))\n", | ||
"\n", | ||
"ds.air.interactive.sel(lat=lat).to_dataframe().groupby('time').mean().hvplot('time', 'air')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Operators\n", | ||
"\n", | ||
"You can apply math operators on the interactive object:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"slider = pn.widgets.IntSlider(name='time', start=0, end=10)\n", | ||
"\n", | ||
"ds.air.interactive(width=800).isel(time=slider).mean().item() + 10" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"You can even do math with a widget:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"slider = pn.widgets.IntSlider(name='time', start=0, end=10)\n", | ||
"\n", | ||
"offset = pn.widgets.IntSlider(name='Offset', start=0, end=10)\n", | ||
"\n", | ||
"ds.air.interactive.isel(time=slider).mean().item() + offset" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"((ds.air.interactive.sel(time=pn.widgets.DiscreteSlider) - ds.air.mean('time'))\n", | ||
" .hvplot(cmap='RdBu_r', clim=(-20, 20))\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"kind = pn.widgets.Select(options=['contour', 'image', 'contourf'])\n", | ||
"\n", | ||
"pn.panel((ds.air.interactive.sel(time=pn.widgets.DiscreteSlider) - ds.air.mean('time'))\n", | ||
" .hvplot(cmap='RdBu_r', clim=(-20, 20), kind=kind).layout\n", | ||
")" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.7.5" | ||
}, | ||
"widgets": { | ||
"application/vnd.jupyter.widget-state+json": { | ||
"state": {}, | ||
"version_major": 2, | ||
"version_minor": 0 | ||
} | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
Oops, something went wrong.