# Creating interactive dashboards ¶

In [ ]:
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
hv.notebook_extension('bokeh')


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  paramBokeh  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 [ ]:
%%opts Curve {+framewise}

df = pd.DataFrame(getattr(stocks, symbol))
df['date'] = df.date.astype('datetime64[ns]')
return hv.Curve(df, ('date', 'Date'), variable)

stock_symbols = ['AAPL', 'IBM', 'FB', 'GOOG', 'MSFT']
dmap = hv.DynamicMap(load_symbol, kdims='Symbol').redim.values(Symbol=stock_symbols)
dmap


## 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  parambokeh  , which is based on bokeh 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 parambokeh

class StockExplorer(Stream):

rolling_window = param.Integer(default=10, bounds=(1, 365))

symbol = param.ObjectSelector(default='AAPL', objects=stock_symbols)

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()
parambokeh.Widgets(explorer, continuous_update=True, callback=explorer.event, on_init=True)
explorer.view()


## 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 a  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, view):
renderer = hv.renderer('bokeh')
return renderer.get_plot(obj).state


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 = parambokeh.view.Plot(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()
else:

explorer = AdvancedStockExplorer()

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.  paramBokeh  is only one widget framework we could use, we could also use  paramNB  to use ipywidgets or simply use ipywidgets. For more information on how to deploy bokeh apps from HoloViews and build dashboards see the Deploying Bokeh Apps .