HoloViews#
Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).
import holoviews as hv
import panel as pn
hv.extension("bokeh", "plotly")
HoloViews is a popular and powerful data visualization library supporting many data and plotting backends.
hvPlot (quick viz) and GeoViews (spatial viz) are built on top of HoloViews and produces HoloViews
objects.
Panel, HoloViews, hvPlot and GeoViews are all members of the HoloViz ecosystem and you can expect them to work perfectly together.
In this reference notebook we will assume a basic level of understanding of HoloViews and optionally hvPlot or Geoviews if you want to use them with Panel.
The HoloViews
pane renders HoloViews objects with one of the plotting backends supported by HoloViews. This includes objects produced by hvPlot and GeoViews.
The HoloViews
pane supports displaying interactive HoloMap
and DynamicMap
objects containing widgets. The HoloViews
pane even allows customizing the widget types and their position relative to the plot.
Parameters:#
For details on other options for customizing the component see the layout and styling how-to guides.
The main argument is the object
parameter
object
(object): The HoloViews object being displayed
You can control the way the object
is rendered and sharing axes with other plots via the parameters
backend
(str): Any of the supported HoloViews backends (‘bokeh’, ‘matplotlib’, or ‘plotly’). If none is specified defaults to the active holoviews renderer. This is by default ‘bokeh’.linked_axes
(boolean, default=True): Whether to link axes across plots in a panel layoutformat
(str, default=’png’): The output format to use when plotting a Matplotlib plot.renderer
: Explicit HoloViews Renderer instance to use for rendering the HoloViews plot. Overrides thebackend
parameter.theme
(str, Theme): Bokeh theme to apply to the HoloViews plot.
You can access the layout and (optional) widgets via
layout
(pn.layout.Panel): The layout containing the plot pane and (optionally) thewidget_box
layout.widget_box
(ListPanel): The layout containing the widgets
Layout and Widget Parameters#
The below parameters are used to control the layout and widgets when using pn.panel
or pn.pane.HoloViews(...).layout
.
center
(boolean, default=False): Whether to center the plot or not.widgets
(dict, argument): A mapping from dimension name to a widget class, instance, or dictionary of overrides to modify the default widgets. Provided as an argument.widget_location
(str): Where to lay out the widget relative to the plotwidget_layout
(ListPanel): The object to lay the widgets out in, one ofRow
,Column
orWidgetBox
widget_type
(str): Whether to generate individual widgets for each dimension, or to use a global linear scrubber with dimensions concatenated.
The pn.pane.HoloViews
pane will automatically convert any HoloViews
object into a displayable panel, while keeping all of its interactive features:
import numpy as np
data = {"group": np.random.randint(0, 10, 100), "value": np.random.randn(100)}
box = hv.Scatter(data, kdims="group", vdims="value").sort().opts()
hv_pane = pn.pane.HoloViews(box, height=300, sizing_mode="stretch_width")
hv_pane
By setting the pane’s object
the plot can be updated like all other pane objects:
hv_pane.object = hv.Violin(box).opts(violin_color='Group', responsive=True, height=300)
You can display hvPlot (and GeoViews) objects too because they are HoloViews
objects.
import pandas as pd
import hvplot.pandas
df = pd.DataFrame(data)
plot = df.hvplot.box(by="group", y="value", responsive=True, height=300)
pn.pane.HoloViews(plot, height=300, sizing_mode="stretch_width")
You can also display HoloMap
and DynamicMap
objects.
HoloViews (the framework) natively renders plots with widgets if a HoloMap
or DynamicMap declares any key dimensions. This approach efficiently updates just the data inside a plot instead of replacing the plot entirely.
import pandas as pd
import hvplot.pandas
import holoviews.plotting.bokeh
def sine(frequency=1.0, amplitude=1.0, function='sin'):
xs = np.arange(200)/200*20.0
ys = amplitude*getattr(np, function)(frequency*xs)
return pd.DataFrame(dict(y=ys), index=xs).hvplot(height=250, responsive=True)
dmap = hv.DynamicMap(sine, kdims=['frequency', 'amplitude', 'function']).redim.range(
frequency=(0.1, 10), amplitude=(1, 10)).redim.values(function=['sin', 'cos', 'tan'])
hv_panel = pn.pane.HoloViews(dmap)
hv_panel
The layout
parameter contains the HoloViews
pane and the widget_box
.
print(hv_panel.layout, "\n\n", hv_panel.widget_box)
Row(sizing_mode='stretch_width') [0] HoloViews(DynamicMap, height=250, sizing_mode='stretch_width') [1] WidgetBox(align=('end', 'start')) [0] FloatSlider(end=10, name='frequency', start=0.1, value=0.1) [1] IntSlider(end=10, name='amplitude', start=1, value=1) [2] Select(name='function', options=['sin', 'cos', 'tan'], value='sin') WidgetBox(align=('end', 'start')) [0] FloatSlider(end=10, name='frequency', start=0.1, value=0.1) [1] IntSlider(end=10, name='amplitude', start=1, value=1) [2] Select(name='function', options=['sin', 'cos', 'tan'], value='sin')
Backend#
The HoloViews
pane will default to the ‘bokeh’ plotting backend if no backend has been loaded via holoviews
, but you can change the backend to any of the ‘bokeh’, ‘matplotlib’ and ‘plotly’ backends as needed.
Bokeh#
Bokeh is the default plotting backend, so normally you don’t have to specify it. But lets do it here to show how it works.
plot = df.hvplot.scatter(x="group", y="value")
pn.pane.HoloViews(plot, backend='bokeh', sizing_mode="stretch_width", height=300)
Matplotlib#
The Matplotlib backend allows generating figures for print and publication. If you want to allow for responsive sizing you can set format='svg'
and then use the standard responsive sizing_mode
settings:
hvplot.extension("matplotlib")
plot = df.hvplot.scatter(x="group", y="value")
pn.pane.HoloViews(plot, backend='matplotlib', sizing_mode="stretch_both", format='svg', center=False)
Please note that in a server context you might have to set the matplotlib backend like below depending on your setup.
import matplotlib
matplotlib.use('agg')
Plotly#
To use the ‘plotly’ plotting backend you will need to run hv.extension("plotly")
to configure the ‘plotly’ backend.
If you are using hvPlot
you can use hvplot.extension("plotly")
instead.
hvplot.extension("plotly")
plot = df.hvplot.scatter(x="group", y="value", height=300, responsive=True)
pn.pane.HoloViews(plot, backend='plotly', height=300)
Lets change the default backend back to ‘bokeh’
hvplot.extension("bokeh")
Dynamic#
You can change the plotting backend dynamically via a widget too.
plot = df.hvplot.scatter(x="group", y="value", height=300, responsive=True, title="Try changing the backend")
plot_pane = pn.pane.HoloViews(plot, backend='bokeh', sizing_mode="stretch_width", height=300)
widget = pn.widgets.RadioButtonGroup.from_param(
plot_pane.param.backend, button_type="primary", button_style="outline"
)
pn.Column(widget, plot_pane)
Linked Axes#
By default the axes of plots with shared key or value dimensions are linked. You can remove the link by setting the linked_axes
parameter to False
.
not_linked_plot = df.hvplot.scatter(x="group", y="value", responsive=True, title="Not Linked Axes").opts(active_tools=['box_zoom'])
linked_plot = df.hvplot.scatter(x="group", y="value", responsive=True, title="Linked Axes").opts(active_tools=['box_zoom'])
pn.Column(
pn.pane.HoloViews(not_linked_plot, backend='bokeh', sizing_mode="stretch_width", height=200, linked_axes=False),
pn.pane.HoloViews(linked_plot, backend='bokeh', sizing_mode="stretch_width", height=200),
pn.pane.HoloViews(linked_plot, backend='bokeh', sizing_mode="stretch_width", height=200),
)
Theme#
You can change the theme
.
plot = df.hvplot.scatter(x="group", y="value", height=300, responsive=True)
pn.pane.HoloViews(plot, height=300, theme="night_sky")
Layout and Widget Parameters#
Please note that above we used the Holoviews
pane to display HoloViews objects.
Below we will be using pn.panel(...)
instead. pn.panel(...)
is the same as pn.pane.HoloViews(...).layout
Center#
You can center your plots via the center
parameter.
plot = df.hvplot.scatter(x="group", y="value", height=100, width=400)
pn.Column(
"`center=True`, `sizing_mode='fixed'`",
pn.panel(plot, center=True, sizing_mode="fixed"),
"`center=True`, `sizing_mode='stretch_width'`",
pn.panel(plot, center=True, sizing_mode="stretch_width"),
"`center=False`, `sizing_mode='fixed'`",
pn.panel(plot),
"`center=False`, `sizing_mode='stretch_width'`",
pn.panel(plot, sizing_mode="stretch_width"),
)
HoloMap and DynamicMap Widgets#
HoloViews (the framework) natively renders plots with widgets if a HoloMap
or DynamicMap declares any key dimensions. This approach efficiently updates just the data inside a plot instead of replacing it entirely.
When rendering a HoloMap
or DynamicMap
object with pn.panel
, then the pn.pane.HoloViews(...).layout
will be displayed. Not pn.pane.HoloViews(...)
.
import pandas as pd
import hvplot.pandas
import holoviews.plotting.bokeh
def sine(frequency=1.0, amplitude=1.0, function='sin'):
xs = np.arange(200)/200*20.0
ys = amplitude*getattr(np, function)(frequency*xs)
return pd.DataFrame(dict(y=ys), index=xs).hvplot(height=250, responsive=True)
dmap = hv.DynamicMap(sine, kdims=['frequency', 'amplitude', 'function']).redim.range(
frequency=(0.1, 10), amplitude=(1, 10)).redim.values(function=['sin', 'cos', 'tan'])
dmap_panel = pn.panel(dmap, height=400, sizing_mode="stretch_width")
dmap_panel
Lets inspect the dmap_panel
print(dmap_panel)
Row(sizing_mode='stretch_width') [0] HoloViews(DynamicMap, height=400, sizing_mode='stretch_width') [1] WidgetBox(align=('end', 'start')) [0] FloatSlider(end=10, name='frequency', start=0.1, value=0.1) [1] IntSlider(end=10, name='amplitude', start=1, value=1) [2] Select(name='function', options=['sin', 'cos', 'tan'], value='sin')
Lets try to modify the FloatSlider
before we render it:
float_slider = hv_panel[1][0]
float_slider.styles = {'border': '2px solid red', 'padding': '10px', 'border-radius': '5px'}
float_slider
Lets try to reorganize the layout
widgets = hv_panel[1]
pn.Row(pn.Column(*widgets), hv_panel[0])
The widgets in the .layout
can be customized via the widgets
, widget_location
, widget_layout
and widget_type
parameters.
widget_location
#
The HoloViews pane offers options to lay out the plot and widgets in a number of preconfigured arrangements using the center
and widget_location
parameters.
The widget_location
parameter accepts all of the following options:
['left', 'bottom', 'right', 'top', 'top_left', 'top_right', 'bottom_left',
'bottom_right', 'left_top', 'left_bottom', 'right_top', 'right_bottom']
pn.panel(dmap, center=True, widget_location='left')
widget_layout
#
Lets change the widget layout to a Row
.
pn.panel(dmap, center=True, widget_layout=pn.Row, widget_location='top_left')
widgets
#
As we saw above, the HoloViews pane will automatically try to generate appropriate widgets for the type of data, usually defaulting to DiscreteSlider
and Select
widgets. This behavior can be modified by providing a dictionary of widgets
by dimension name. The values of this dictionary can override the default widget in one of three ways:
Supplying a
Widget
instanceSupplying a compatible
Widget
typeSupplying a dictionary of
Widget
parameter overrides
Widget
instances will be used as they are supplied and are expected to provide values matching compatible with the values defined on HoloMap/DynamicMap. Similarly if a Widget
type is supplied it should be discrete if the parameter space defines a discrete set of values. If the defined parameter space is continuous, on the other hand, it may supply any valid value.
In the example below the ‘amplitude’ dimension is overridden with an explicit Widget
instance, the ‘function’ dimension is overridden with a RadioButtonGroup letting us toggle between the different functions, and lastly the ‘value’ parameter on the ‘frequency’ widget is overridden to change the initial value:
hv_panel = pn.pane.HoloViews(dmap, widgets={
'amplitude': pn.widgets.LiteralInput(value=1., type=(float, int)),
'function': pn.widgets.RadioButtonGroup,
'frequency': {'value': 5}
}).layout
hv_panel
Bound Functions and DynamicMaps#
When working with reactive references and functions (such as those returned by .bind
, .depends
or .rx
) that return HoloViews elements and overlays it is recommended that you wrap them in a DynamicMap
. This allows us to construct plots that depend on the values of widgets or other parameters or expressions in a straightforward way. Note that this differs from auto-generating widgets from dimensions as we’ve seen so far. Internally HoloViews will inspect the function and create a so called Stream
that updates the DynamicMap
when the dependencies change. This also means that any widget you depend on will not appear alongside the other widgets, derived from a dimension.
Instead of re-rendering the entire plot each time a parameter changes this will delegate the update to HoloViews and update the data inplace:
def get_plot(n):
return hv.Scatter(range(n), kdims="x", vdims="y").opts(
height=300, responsive=True, xlim=(0, 100), ylim=(0, 100)
)
widget = pn.widgets.IntSlider(value=50, start=1, end=100, name="Number of points")
plot = hv.DynamicMap(pn.bind(get_plot, widget))
pn.Column(widget, plot)
Please note the bound function may not return Layout
s, NdLayout
s, GridSpace
s, DynamicMap
s or HoloMaps
as these are not supported by the DynamicMap
.
Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).