Creating interactive dashboards

In [ ]:
import numpy as np
import pandas as pd
import holoviews as hv
from bokeh.sampledata import stocks
from holoviews.operation.timeseries import rolling, rolling_outlier_std
from holoviews.streams import Stream

In the Data Processing Pipelines section we discovered how to declare a DynamicMap and control multiple processing steps with the use of custom streams as described in the Responding to Events guide. Here we will use the same example exploring a dataset of stock timeseries and build a small dashboard using the paramNB library, which allows us to declare easily declare custom widgets and link them to our streams. We will begin by once again declaring our function that loads the stock data:

In [ ]:
def load_symbol(symbol, variable='adj_close', **kwargs):
    df = pd.DataFrame(getattr(stocks, symbol))
    df['date'] ='datetime64[ns]')
    return hv.Curve(df, kdims=[('date', 'Date')],

dmap = hv.DynamicMap(load_symbol, kdims=['Symbol']).redim.values(Symbol=stocks.stocks)

Building dashboards

Controlling stream events manually from the Python prompt can be a bit cumbersome. However since you can now trigger events from Python we can easily bind any Python based widget framework to the stream. HoloViews itself is based on param and param has various UI toolkits that accompany it and allow you to quickly generate a set of widgets. Here we will use paramnb , which is based on ipywidgets to control our stream values.

To do so we will declare a StockExplorer class which inherits from Stream and defines two parameters, the rolling_window as an integer and the symbol as an ObjectSelector. Additionally we define a view method, which defines the DynamicMap and applies the two operations we have already played with, returning an overlay of the smoothed Curve and outlier Scatter .

In [ ]:
import param
import paramnb

class StockExplorer(hv.streams.Stream):
    rolling_window = param.Integer(default=10, bounds=(1, 365))
    symbol = param.ObjectSelector(default='AAPL', objects=stocks.stocks)
    def view(self):
        stocks = hv.DynamicMap(load_symbol, kdims=[], streams=[self])

        # Apply rolling mean
        smoothed = rolling(stocks, streams=[self])

        # Find outliers
        outliers = rolling_outlier_std(stocks, streams=[self])
        return smoothed * outliers

Now that we have defined this Parameterized class we can instantiate it and pass it to the paramnb.Widgets function, which will display the widgets. Additionally we call the StockExplorer.view method to display the DynamicMap.

In [ ]:
%opts Curve [width=600] {+framewise} Scatter (color='red' marker='triangle')
explorer = StockExplorer()
paramnb.Widgets(explorer, continuous_update=True, callback=explorer.event, on_init=True)

Replacing the output

In HoloViews you have to declare the type of data that you want to display using Elements. Changing the types returned by a DynamicMap is generally not supported. Therefore paramNB provides the ability to completely redraw the output by replacing the object that is being displayed. Here we will extend the class we created above to draw directly to an view parameter, which we can assign to. view parameters like HTML support a renderer argument, which converts whatever you assign to the object to the correct representation. Therefore we will define a quick function that returns the HTML representation of our HoloViews object and also computes the size:

In [ ]:
def render(obj):
    renderer = hv.renderer('bokeh')
    plot = renderer.get_plot(obj)
    size = renderer.get_size(plot)
    return renderer.figure_data(plot), size

Now we can extend the StockExplorer class from above with a custom event method. By default event is called on Stream instances to notify any subscribers. We can intercept this call when we want to redraw, here we will instead assign to our output parameter whenever the variable changes, which will trigger a full redraw:

In [ ]:
class AdvancedStockExplorer(StockExplorer):
    output = paramnb.view.HTML(renderer=render)
    variable = param.ObjectSelector(default='adj_close', objects=[c for c in stocks.AAPL.keys() if c!= 'date'])

    def event(self, **kwargs):
        if self.output is None or 'variable' in kwargs:
            self.output = self.view()
            super(AdvancedStockExplorer, self).event(**kwargs)
In [ ]:
explorer = AdvancedStockExplorer()
paramnb.Widgets(explorer, continuous_update=True, callback=explorer.event, on_init=True)

As you can see using streams we have bound the widgets to the streams letting us easily control the stream values and making it trivial to define complex dashboards. paramNB is only one widget framework we could use, we could also use paramBokeh to use bokeh widgets and deploy the dashboard on bokeh server or manually linked ipywidgets to our streams. For more information on how to deploy bokeh apps from HoloViews and build dashboards see the Deploying Bokeh Apps .