Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • grosch1/jupyter-dashboarding
  • petrova1/jupyter-dashboarding
2 results
Select Git revision
Show changes
%% Cell type:markdown id: tags:
# Usage
Start voila with the gridstack template. If `show_handles` is set to True, you can drag the individual dashboard items around.
```
voila --template=gridstack --VoilaConfiguration.resources='{"gridstack": {"show_handles": True}}' ./dashboards/gridstack-example.ipynb
```
%% Cell type:markdown id: tags:
# Demo
This demo uses voila to render a notebook to a custom HTML page using gridstack.js for the layout of each output. In the cell metadata you can change the default cell with and height (in grid units between 1 and 12) by specifying.
* `grid_row`
* `grid_columns`
%% Cell type:code id: tags:
``` python
import numpy as np
n = 200
x = np.linspace(0.0, 10.0, n)
y = np.cumsum(np.random.randn(n)*10).astype(int)
```
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
```
%% Cell type:code id: tags:
``` python
label_selected = widgets.Label(value="Selected: 0")
label_selected
```
%% Output
%% Cell type:code id: tags:
``` python
import numpy as np
from bqplot import pyplot as plt
import bqplot
fig = plt.figure( title='Histogram')
np.random.seed(0)
hist = plt.hist(y, bins=25)
hist.scales['sample'].min = float(y.min())
hist.scales['sample'].max = float(y.max())
display(fig)
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig.layout.flex = '1'
```
%% Output
%% Cell type:code id: tags:
``` python
import numpy as np
from bqplot import pyplot as plt
import bqplot
fig = plt.figure( title='Line Chart')
np.random.seed(0)
n = 200
p = plt.plot(x, y)
fig
```
%% Output
%% Cell type:code id: tags:
``` python
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig.layout.flex = '1'
```
%% Cell type:code id: tags:
``` python
brushintsel = bqplot.interacts.BrushIntervalSelector(scale=p.scales['x'])
```
%% Cell type:code id: tags:
``` python
def update_range(*args):
label_selected.value = "Selected range {}".format(brushintsel.selected)
mask = (x > brushintsel.selected[0]) & (x < brushintsel.selected[1])
hist.sample = y[mask]
brushintsel.observe(update_range, 'selected')
fig.interaction = brushintsel
```
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
# Usage
Start voila with the gridstack template. If `show_handles` is set to True, you can drag the individual dashboard items around.
```
voila --template=gridstack --VoilaConfiguration.resources='{"gridstack": {"show_handles": True}}' ./dashboards/gridstack-scotch.ipynb
```
%% Cell type:markdown id: tags:
# Got Scotch?
%% Cell type:markdown id: tags:
In this notebook, we're going to create a dashboard that recommends scotches based on their taste profiles.
%% Cell type:code id: tags:
``` python
%matplotlib widget
```
%% Cell type:code id: tags:
``` python
import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import os
```
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
from traitlets import Unicode, List, Instance, link, HasTraits
from IPython.display import display, clear_output, HTML, Javascript
```
%% Cell type:code id: tags:
``` python
display(widgets.Button())
```
%% Output
%% Cell type:markdown id: tags:
## Load Data <span style="float: right; font-size: 0.5em"><a href="#Got-Scotch?">Top</a></span>
%% Cell type:code id: tags:
``` python
features = [[2, 2, 2, 0, 0, 2, 1, 2, 2, 2, 2, 2],
[3, 3, 1, 0, 0, 4, 3, 2, 2, 3, 3, 2],
[1, 3, 2, 0, 0, 2, 0, 0, 2, 2, 3, 1],
[4, 1, 4, 4, 0, 0, 2, 0, 1, 2, 1, 0],
[2, 2, 2, 0, 0, 1, 1, 1, 2, 3, 1, 3],
[2, 3, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
[0, 2, 0, 0, 0, 1, 1, 0, 2, 2, 3, 1],
[2, 3, 1, 0, 0, 2, 1, 2, 2, 2, 2, 2],
[2, 2, 1, 0, 0, 1, 0, 0, 2, 2, 2, 1],
[2, 3, 2, 1, 0, 0, 2, 0, 2, 1, 2, 3],
[4, 3, 2, 0, 0, 2, 1, 3, 3, 0, 1, 2],
[3, 2, 1, 0, 0, 3, 2, 1, 0, 2, 2, 2],
[4, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 2],
[2, 2, 1, 0, 0, 2, 2, 0, 0, 2, 3, 1],
[3, 2, 2, 0, 0, 3, 1, 1, 2, 3, 2, 2],
[2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 2],
[1, 2, 1, 0, 0, 0, 1, 1, 0, 2, 2, 1],
[2, 2, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2],
[2, 2, 3, 1, 0, 2, 2, 1, 1, 1, 1, 3],
[1, 1, 2, 2, 0, 2, 2, 1, 2, 2, 2, 3],
[1, 2, 1, 1, 0, 1, 1, 1, 1, 2, 2, 1],
[3, 1, 4, 2, 1, 0, 2, 0, 2, 1, 1, 0],
[1, 3, 1, 0, 0, 1, 1, 0, 2, 2, 2, 1],
[3, 2, 3, 3, 1, 0, 2, 0, 1, 1, 2, 0],
[2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 1, 2],
[2, 3, 2, 1, 0, 0, 1, 0, 2, 2, 2, 1],
[4, 2, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2],
[3, 2, 2, 1, 0, 1, 2, 2, 1, 2, 3, 2],
[2, 2, 2, 0, 0, 2, 1, 0, 1, 2, 2, 1],
[2, 2, 1, 0, 0, 2, 1, 1, 1, 3, 2, 2],
[2, 3, 1, 1, 0, 0, 0, 0, 1, 2, 2, 1],
[2, 3, 1, 0, 0, 2, 1, 1, 4, 2, 2, 2],
[2, 3, 1, 1, 1, 1, 1, 2, 0, 2, 0, 3],
[2, 3, 1, 0, 0, 2, 1, 1, 1, 1, 2, 1],
[2, 1, 3, 0, 0, 0, 3, 1, 0, 2, 2, 3],
[1, 2, 0, 0, 0, 1, 0, 1, 2, 1, 2, 1],
[2, 3, 1, 0, 0, 1, 2, 1, 2, 1, 2, 2],
[1, 2, 1, 0, 0, 1, 2, 1, 2, 2, 2, 1],
[3, 2, 1, 0, 0, 1, 2, 1, 1, 2, 2, 2],
[2, 2, 2, 2, 0, 1, 0, 1, 2, 2, 1, 3],
[1, 3, 1, 0, 0, 0, 1, 1, 1, 2, 0, 1],
[1, 3, 1, 0, 0, 1, 1, 0, 1, 2, 2, 1],
[4, 2, 2, 0, 0, 2, 1, 4, 2, 2, 2, 2],
[3, 2, 1, 0, 0, 2, 1, 2, 1, 2, 3, 2],
[2, 4, 1, 0, 0, 1, 2, 3, 2, 3, 2, 2],
[1, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 1],
[1, 2, 0, 0, 0, 1, 1, 1, 2, 2, 3, 1],
[1, 2, 1, 0, 0, 1, 2, 0, 0, 2, 2, 1],
[2, 3, 1, 0, 0, 2, 2, 2, 1, 2, 2, 2],
[1, 2, 1, 0, 0, 1, 2, 0, 1, 2, 2, 1],
[2, 2, 1, 1, 0, 1, 2, 0, 2, 1, 2, 1],
[2, 3, 1, 0, 0, 1, 1, 2, 1, 2, 2, 2],
[2, 3, 1, 0, 0, 2, 2, 2, 2, 2, 1, 2],
[2, 2, 3, 1, 0, 2, 1, 1, 1, 2, 1, 3],
[1, 3, 1, 1, 0, 2, 2, 0, 1, 2, 1, 1],
[2, 1, 2, 2, 0, 1, 1, 0, 2, 1, 1, 3],
[2, 3, 1, 0, 0, 2, 2, 1, 2, 1, 2, 2],
[4, 1, 4, 4, 1, 0, 1, 2, 1, 1, 1, 0],
[4, 2, 4, 4, 1, 0, 0, 1, 1, 1, 0, 0],
[2, 3, 1, 0, 0, 1, 1, 2, 0, 1, 3, 1],
[1, 1, 1, 1, 0, 1, 1, 0, 1, 2, 1, 1],
[3, 2, 1, 0, 0, 1, 1, 1, 3, 3, 2, 2],
[4, 3, 1, 0, 0, 2, 1, 4, 2, 2, 3, 2],
[2, 1, 1, 0, 0, 1, 1, 1, 2, 1, 2, 1],
[2, 4, 1, 0, 0, 1, 0, 0, 2, 1, 1, 1],
[3, 2, 2, 0, 0, 2, 3, 3, 2, 1, 2, 2],
[2, 2, 2, 2, 0, 0, 2, 0, 2, 2, 2, 3],
[1, 2, 2, 0, 1, 2, 2, 1, 2, 3, 1, 3],
[2, 1, 2, 2, 1, 0, 1, 1, 2, 2, 2, 3],
[2, 3, 2, 1, 1, 1, 2, 1, 0, 2, 3, 1],
[3, 2, 2, 0, 0, 2, 2, 2, 2, 2, 3, 2],
[2, 2, 1, 1, 0, 2, 1, 1, 2, 2, 2, 2],
[2, 4, 1, 0, 0, 2, 1, 0, 0, 2, 1, 1],
[2, 2, 1, 0, 0, 1, 0, 1, 2, 2, 2, 1],
[2, 2, 2, 2, 0, 2, 2, 1, 2, 1, 0, 3],
[2, 2, 1, 0, 0, 2, 2, 2, 3, 3, 3, 2],
[2, 3, 1, 0, 0, 0, 2, 0, 2, 1, 3, 1],
[4, 2, 3, 3, 0, 1, 3, 0, 1, 2, 2, 0],
[1, 2, 1, 0, 0, 2, 0, 1, 1, 2, 2, 1],
[1, 3, 2, 0, 0, 0, 2, 0, 2, 1, 2, 1],
[2, 2, 2, 1, 0, 0, 2, 0, 0, 0, 2, 3],
[1, 1, 1, 0, 0, 1, 0, 0, 1, 2, 2, 1],
[2, 3, 2, 0, 0, 2, 2, 1, 1, 2, 0, 3],
[0, 3, 1, 0, 0, 2, 2, 1, 1, 2, 1, 1],
[2, 2, 1, 0, 0, 1, 0, 1, 2, 1, 0, 3],
[2, 3, 0, 0, 1, 0, 2, 1, 1, 2, 2, 1]]
feature_names = ['Body', 'Sweetness', 'Smoky',
'Medicinal', 'Tobacco', 'Honey',
'Spicy', 'Winey', 'Nutty',
'Malty', 'Fruity', 'cluster']
brand_names = ['Aberfeldy',
'Aberlour',
'AnCnoc',
'Ardbeg',
'Ardmore',
'ArranIsleOf',
'Auchentoshan',
'Auchroisk',
'Aultmore',
'Balblair',
'Balmenach',
'Belvenie',
'BenNevis',
'Benriach',
'Benrinnes',
'Benromach',
'Bladnoch',
'BlairAthol',
'Bowmore',
'Bruichladdich',
'Bunnahabhain',
'Caol Ila',
'Cardhu',
'Clynelish',
'Craigallechie',
'Craigganmore',
'Dailuaine',
'Dalmore',
'Dalwhinnie',
'Deanston',
'Dufftown',
'Edradour',
'GlenDeveronMacduff',
'GlenElgin',
'GlenGarioch',
'GlenGrant',
'GlenKeith',
'GlenMoray',
'GlenOrd',
'GlenScotia',
'GlenSpey',
'Glenallachie',
'Glendronach',
'Glendullan',
'Glenfarclas',
'Glenfiddich',
'Glengoyne',
'Glenkinchie',
'Glenlivet',
'Glenlossie',
'Glenmorangie',
'Glenrothes',
'Glenturret',
'Highland Park',
'Inchgower',
'Isle of Jura',
'Knochando',
'Lagavulin',
'Laphroig',
'Linkwood',
'Loch Lomond',
'Longmorn',
'Macallan',
'Mannochmore',
'Miltonduff',
'Mortlach',
'Oban',
'OldFettercairn',
'OldPulteney',
'RoyalBrackla',
'RoyalLochnagar',
'Scapa',
'Speyburn',
'Speyside',
'Springbank',
'Strathisla',
'Strathmill',
'Talisker',
'Tamdhu',
'Tamnavulin',
'Teaninich',
'Tobermory',
'Tomatin',
'Tomintoul',
'Tormore',
'Tullibardine']
```
%% Cell type:code id: tags:
``` python
features_df = pd.DataFrame(features, columns=feature_names, index=brand_names)
features_df = features_df.drop('cluster', axis=1)
```
%% Cell type:code id: tags:
``` python
norm = (features_df ** 2).sum(axis=1).apply('sqrt')
normed_df = features_df.divide(norm, axis=0)
sim_df = normed_df.dot(normed_df.T)
```
%% Cell type:code id: tags:
``` python
def radar(df, ax=None):
# calculate evenly-spaced axis angles
num_vars = len(df.columns)
theta = 2*np.pi * np.linspace(0, 1-1./num_vars, num_vars)
# rotate theta such that the first axis is at the top
theta += np.pi/2
if not ax:
fig = plt.figure(figsize=(4, 4))
ax = fig.add_subplot(1,1,1, projection='polar')
else:
ax.clear()
for d, color in zip(df.itertuples(), sns.color_palette()):
ax.plot(theta, d[1:], color=color, alpha=0.7)
ax.fill(theta, d[1:], facecolor=color, alpha=0.5)
ax.set_xticklabels(df.columns)
legend = ax.legend(df.index, loc=(0.9, .95))
return ax
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
class RadarWidget(HasTraits):
factors_keys = List(['Aberfeldy'])
def __init__(self, df, **kwargs):
self.df = df
super(RadarWidget, self).__init__(**kwargs)
self.ax = None
self.factors_keys_changed()
def factors_keys_changed(self):
new_value = self.factors_keys
if self.ax:
self.ax.clear()
self.ax = radar(self.df.loc[new_value], self.ax)
```
%% Cell type:markdown id: tags:
We now define a *get_similar( )* function to return the data of the top n similar scotches to a given scotch.
%% Cell type:code id: tags:
``` python
def get_similar(name, n, top=True):
a = sim_df[name].sort_values(ascending=False)
a.name = 'Similarity'
df = pd.DataFrame(a) #.join(features_df).iloc[start:end]
return df.head(n) if top else df.tail(n)
```
%% Cell type:markdown id: tags:
We also need a function *on_pick_scotch* that will display a table of the top 5 similar scotches that Radar View watches, based on a given selected Scotch.
%% Cell type:code id: tags:
``` python
def on_pick_scotch(Scotch):
name = Scotch
# Get top 6 similar whiskeys, and remove this one
top_df = get_similar(name, 6).iloc[1:]
# Get bottom 5 similar whiskeys
df = top_df
# Make table index a set of links that the radar widget will watch
df.index = ['''<a class="scotch" href="#" data-factors_keys='["{}","{}"]'>{}</a>'''.format(name, i, i) for i in df.index]
tmpl = f'''<p>If you like {name} you might want to try these five brands. Click one to see how its taste profile compares.</p>'''
prompt_w.value = tmpl
table.value = df.to_html(escape=False)
radar_w.factors_keys = [name]
plot = radar_w.factors_keys_changed()
```
%% Cell type:code id: tags:
``` python
prompt_w = widgets.HTML(value='Aberfeldy')
display(prompt_w)
```
%% Output
%% Cell type:code id: tags:
``` python
table = widgets.HTML(
value="Hello <b>World</b>"
)
display(table)
```
%% Output
%% Cell type:code id: tags:
``` python
radar_w = RadarWidget(df=features_df)
```
%% Output
%% Cell type:code id: tags:
``` python
picker_w = widgets.interact(on_pick_scotch, Scotch=list(sim_df.index))
```
%% Output
%% Cell type:code id: tags:
``` python
radar_w.factors_keys
```
%% Output
['Aberfeldy']
%% Cell type:markdown id: tags:
Powered by data from https://www.mathstat.strath.ac.uk/outreach/nessie/nessie_whisky.html and inspired by analysis from http://blog.revolutionanalytics.com/2013/12/k-means-clustering-86-single-malt-scotch-whiskies.html. This dashboard originated as a Jupyter Notebook.
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
# Usage
```bash
voila -template vuetify-default ./dashboards/vuetify-bqplot.ipynb
```
%% Cell type:code id: tags:
``` python
import ipyvuetify as v
```
%% Cell type:markdown id: tags:
# First histogram plot
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
import numpy as np
from bqplot import pyplot as plt
import bqplot
n = 200
x = np.linspace(0.0, 10.0, n)
y = np.cumsum(np.random.randn(n)*10).astype(int)
fig = plt.figure( title='Histogram')
np.random.seed(0)
hist = plt.hist(y, bins=25)
hist.scales['sample'].min = float(y.min())
hist.scales['sample'].max = float(y.max())
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig
```
%% Output
%% Cell type:code id: tags:
``` python
slider = v.Slider(thumb_label='always', class_="px-4", v_model=30)
widgets.link((slider, 'v_model'), (hist, 'bins'))
slider
```
%% Output
%% Cell type:markdown id: tags:
# Line chart
%% Cell type:code id: tags:
``` python
fig2 = plt.figure( title='Line Chart')
np.random.seed(0)
p = plt.plot(x, y)
fig2.layout.width = 'auto'
fig2.layout.height = 'auto'
fig2.layout.min_height = '300px' # so it shows nicely in the notebook
fig2
```
%% Output
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
brushintsel = bqplot.interacts.BrushIntervalSelector(scale=p.scales['x'])
def update_range(*args):
if brushintsel.selected is not None and brushintsel.selected.shape == (2,):
mask = (x > brushintsel.selected[0]) & (x < brushintsel.selected[1])
hist.sample = y[mask]
brushintsel.observe(update_range, 'selected')
fig2.interaction = brushintsel
```
%% Cell type:markdown id: tags:
# Second histogram plot
%% Cell type:code id: tags:
``` python
n2 = 200
x2 = np.linspace(0.0, 10.0, n)
y2 = np.cumsum(np.random.randn(n)*10).astype(int)
figHist2 = plt.figure( title='Histogram 2')
np.random.seed(0)
hist2 = plt.hist(y2, bins=25)
hist2.scales['sample'].min = float(y2.min())
hist2.scales['sample'].max = float(y2.max())
figHist2.layout.width = 'auto'
figHist2.layout.height = 'auto'
figHist2.layout.min_height = '300px' # so it shows nicely in the notebook
sliderHist2 = v.Slider(_metadata={'mount_id': 'histogram_bins2'}, thumb_label='always', class_='px-4', v_model=5)
from traitlets import link
link((sliderHist2, 'v_model'), (hist2, 'bins'))
display(figHist2)
display(sliderHist2)
```
%% Output
%% Cell type:markdown id: tags:
# Set up voila vuetify layout
The voila vuetify template does not render output from the notebook, it only shows widget with the mount_id metadata.
%% Cell type:code id: tags:
``` python
v.Tabs(_metadata={'mount_id': 'content-main'}, children=[
v.Tab(children=['Tab1']),
v.Tab(children=['Tab2']),
v.TabItem(children=[
v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, lg6=True, xl4=True, children=[
fig, slider
]),
v.Flex(xs12=True, lg6=True, xl4=True, children=[
figHist2, sliderHist2
]),
v.Flex(xs12=True, xl4=True, children=[
fig2
]),
])
]),
v.TabItem(children=[
v.Container(children=['Lorum ipsum'])
])
])
```
%% Output
%% Cell type:markdown id: tags:
## Optional
# Using Jupyter Server Proxy
Alternatively, if wanting to access over Jupyter Server Proxy on jupyter-jsc:
> No ssh tunnel needed, but ugly start command.
> Might need to change path to notebook!
%% Cell type:code id: tags:
``` python
port = 8866
```
%% Cell type:code id: tags:
``` python
import os
print("""
voila --template vuetify-default \
--server_url=/ --base_url={prefix}proxy/{port}/ --VoilaConfiguration.file_whitelist="['.*']" \
./jupyter-dashboarding/dashboards/vuetify-bqplot.ipynb
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=port))
```
%% Output
voila --template vuetify-default --server_url=/ --base_url=/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866/ --VoilaConfiguration.file_whitelist="['.*']" ./jupyter-dashboarding/dashboards/vuetify-bqplot.ipynb
%% Cell type:markdown id: tags:
Dashboard now runs on
%% Cell type:code id: tags:
``` python
print("""
https://jupyter-jsc.fz-juelich.de{prefix}proxy/{port}
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=port))
```
%% Output
https://jupyter-jsc.fz-juelich.de/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866
......
%% Cell type:code id: tags:
``` python
import ipyvuetify as v
```
%% Cell type:markdown id: tags:
# Documentation
%% Cell type:markdown id: tags:
### Ipyvuetify
### ipyvuetify
https://github.com/mariobuikhuizen/ipyvuetify
### Vuetify Documentation
https://vuetifyjs.com/en/components/buttons#buttons
### Material UI Icons
https://www.materialui.co/icons
%% Cell type:markdown id: tags:
# Create a Custom Template
%% Cell type:markdown id: tags:
# Creating your app
1. create `conf.json` with
The *voila vuetify* template **does not** render output from the notebook, it only shows widgets with the mount_id metadata.
By default, the mount points
1. `content-title` for the title in the navigation drawer,
2. `content-nav` for the content in the navigation drawer,
3. `content-bar` for the title in the app bar and
4. `content-main` for the main app area
```json
{"base_template": "vuetify-base"}
```
2. copy https://raw.githubusercontent.com/voila-dashboards/voila-vuetify/master/share/jupyter/voila/templates/vuetify-default/nbconvert_templates/app.html (`app.html`) into a directory called `nbconvert_templates`
are defined.
%% Cell type:markdown id: tags:
# Navigation Bar
For example, we can set the app bar title with
Set color in custom template `app.html`
```html
<v-navigation-drawer v-model="showNavBar" absolute app>
<v-toolbar color="primary" dark flat>
[...]
</v-toolbar>
[...]
</v-navigation-drawer>
%% Cell type:code id: tags:
``` python
appbar_title = v.ToolbarTitle(
_metadata={'mount_id':'toolbar-title'},
children=['My first vuetify app']
)
# No need to render it in the notebook, since the voila vuetify template
# only looks at the mount_id metadata and not at the notebook output.
```
%% Cell type:markdown id: tags:
### How do widgets in ipyvuetify work?
## How do widgets in ipyvuetify work?
Think of them as HTML in Python code!
```html
<v-list-item link>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
```
Compare the Python code below:
%% Cell type:code id: tags:
``` python
list_items = [
v.ListItem(link=True, children=[
v.ListItemIcon(children=[
v.Icon(children=["account_box"]),
]),
v.ListItemContent(children=[
v.ListItemTitle(children=["Account"])
])
]),
v.ListItem(link=True, children=[
v.ListItemIcon(children=[
v.Icon(children=["timeline"]),
]),
v.ListItemContent(children=[
v.ListItemTitle(children=["Analytics"])
])
]),
v.ListItem(link=True, children=[
v.ListItemIcon(children=[
v.Icon(children=["build"]),
]),
v.ListItemContent(children=[
v.ListItemTitle(children=["Settings"])
])
])
]
```
%% Cell type:markdown id: tags:
Create a list and mount it as the navigation drawer content
%% Cell type:code id: tags:
``` python
v.List(
_metadata={'mount_id':'content-nav'},
column=True,
children=list_items
)
```
%% Output
%% Cell type:markdown id: tags:
# Toolbar Title
> *ipyvuetify* tries to stay as close to the *Vue.js* and *Vuetify template* syntax as possible. You should be able to find a corresponding *ipyvuetify* widget for all *vuetify* components.
> For an overview of all existing components, take a look at the [*vuetify* api explorer](https://vuetifyjs.com/en/components/api-explorer).
%% Cell type:markdown id: tags:
Change colors
## Setting `content-main`
`content-main` is the main area of the app and where you will want to render your interactive widgets.
Since we have three entries in the navigation bar, we will create three separate app pages. These are meant to show some of the possibilities of ipyvuetify.
```html
<v-app-bar color="primary" app absolute dark>
%% Cell type:code id: tags:
``` python
lorem_ipsum = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. \
At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." * 2
```
%% Cell type:markdown id: tags:
Add mount point to dynamically change the toolbar title
```html
<v-toolbar-title>
<jupyter-widget-mount-point mount-id="toolbar-title" />
</v-toolbar-title>
```
### Page 1
%% Cell type:code id: tags:
``` python
toolbar_title = v.ToolbarTitle(
_metadata={'mount_id':'toolbar-title'},
children=['My Custom Title']
)
```
card1 = v.Card(children=[
v.CardTitle(children=[
v.ListItem(class_="grow", children=[
v.ListItemAvatar(children=[
v.Icon(large=True, left=True,
children=["account_circle"])
]),
v.ListItemContent(children=["Evan You"])
])
]),
v.ExpansionPanels(children=[
v.ExpansionPanel(children=[
v.ExpansionPanelHeader(children=[header]),
v.ExpansionPanelContent(children=[lorem_ipsum])
])
for header in ["Add", "Some", "Content"]])
])
%% Output
card2 = v.Card(height="100%", children=[
v.CardTitle(children=["Responsiveness"]),
v.CardText(children=["Vuetify automatically creates a responsive layout if you use v.Layout() and v.Flex()",
v.List(children=[
v.ListItem(href="https://vuetifyjs.com/en/styles/display#display",
children=[
v.ListItemContent(children=["Click here to open the documentation for the material design viewport breakpoints."])
])
])])
])
%% Cell type:markdown id: tags:
# Content Main
card3 = v.Card(height="100%", children=[
v.CardTitle(children=["CSS Spacing helpers"]),
v.CardText(children=[
v.List(children=[
v.ListItem(href="https://vuetifyjs.com/en/styles/spacing#how-it-works",
children=[
v.ListItemContent(children=["""Similarly, click here for the documentation on vuetify's css spacing helpers.
You can update your layout without creating new classes.
Spacing helpers are useful for modifying the padding and margin of an element.""" ])
]),
v.ListItem(href="https://vuetifyjs.com/en/styles/spacing#playground",
children=[
v.ListItemContent(children=["Click here to directly head to the playground to get a feel for what the different helper classes can do."])
])
])
])
])
```
%% Cell type:markdown id: tags:
### Content 1
`v.Layout` is a basic flexbox. For the *vuetify* CSS flex helpers, see https://vuetifyjs.com/en/styles/flex#flex.
For `v.Flex`, *xs12*, *md6* and *xl4* stand for the viewport breakpoints, *pa_4* for **p**adding in **a**ll directions.
For more information, follow the links in the cards.
%% Cell type:code id: tags:
``` python
card1 = v.Card(children=[
v.CardTitle(children=['Card 1']),
v.CardText(children=[
"Some text",
v.Spacer(),
v.Btn(children=["Or a widget"])
]),
v.CardActions(children=[
v.Layout(row=True, children=[
v.Btn(icon=True, children=[
v.Icon(children=['favorite'])
]),
v.Btn(icon=True, children=[
v.Icon(children=['bookmark'])
]),
v.Btn(icon=True, children=[
v.Icon(children=['share'])
])
])
])
page1 = v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, md6=True, xl4=True, pa_4=True, children=[ card ])
for card in [card1, card2, card3]
])
```
%% Cell type:markdown id: tags:
card2 = v.Card(children=[
v.CardTitle(children=['Card 2']),
v.CardText(children=["With flex, the layout will be responsive"])
])
In the standard notebook, the output will likely have wrong styling and layouts.
This is because the vuetify css style sheets are not loaded in the notebook.
Once started with the voila vuetify template, they should render correctly.
%% Cell type:code id: tags:
content1 = v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, md6=True, xl4=True, children=[
card1
]),
v.Flex(xs12=True, md6=True, xl4=True, children=[
card2
]),
])
``` python
page1
```
%% Output
%% Cell type:markdown id: tags:
### Content 2
### Page 2
Widgets from other notebook extensions can be rendered in the app as well.
%% Cell type:code id: tags:
``` python
from bqplot import pyplot as plt
import bqplot
from ipywidgets import link
import numpy as np
# Create a random plot
n = 200
x = np.linspace(0.0, 10.0, n)
y = np.cumsum(np.random.randn(n)*10).astype(int)
fig = plt.figure( title='Histogram')
np.random.seed(0)
hist = plt.hist(y, bins=25)
hist.scales['sample'].min = float(y.min())
hist.scales['sample'].max = float(y.max())
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
```
%% Cell type:markdown id: tags:
Ipyvuetify widgets don't use `value` but `v_model`:
%% Cell type:code id: tags:
``` python
content2 = v.Layout(column=True, children=[
v.Html(tag='h1', children=['An example plot']),
fig
slider = v.Slider(thumb_label='always', class_="px-4", v_model=30)
link((slider, 'v_model'), (hist, 'bins'));
```
%% Cell type:code id: tags:
``` python
page2 = v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, lg6=True, xl4=True, children=[ fig, slider ])
])
page2
```
%% Output
%% Cell type:markdown id: tags:
### Page 3
Some more advanced usage of ipyvuetify widgets.
%% Cell type:markdown id: tags:
#### The `v_model` trait
%% Cell type:code id: tags:
``` python
switch = v.Switch(label="Switch", v_model=False)
checkbox = v.Checkbox(label="Checkbox", v_model=False)
link((switch, 'v_model'), (checkbox, 'v_model'))
display(switch)
display(checkbox)
```
%% Output
%% Cell type:markdown id: tags:
### Content 3
The `value` trait of ipywidgets corresponds to `v_model` in ipyvuetify widgets.
So for example,
```python
v_model=False
```
sets a `v.Checkbox` to "not selected",
```python
v_model=True
```
to "selected".
Uncomment the code below to play around with the `v_model` trait:
%% Cell type:code id: tags:
``` python
from ipywidgets import jslink
switch.v_model = True
```
%% Cell type:code id: tags:
``` python
switch = v.Switch(label="Switch", v_model=False)
checkbox = v.Checkbox(label="Checkbox", v_model=False)
checkbox.v_model = False
```
content3 = v.Layout(column=True, px_5=True, children=[
switch,
checkbox
])
%% Cell type:markdown id: tags:
#### (Scoped) slots
For (scoped) slots, the argument `v_slots` can be used, see the [scoped slots](https://github.com/mariobuikhuizen/ipyvuetify#scoped-slots) section of the ipyvuetify documentation.
%% Cell type:markdown id: tags:
The CSS attributes class and style need to be suffixed with an underscore: `class_`, `style_`
%% Cell type:code id: tags:
``` python
accept_btn = v.Btn(color="primary", children=["I accept"])
dialog = v.Dialog(max_width="50%",
v_model=False,
v_slots=[{
'name': 'activator',
'variable': 'x',
'children': v.Btn(v_on='x.on',
color="red lighten-2",
children=['Click to open dialog'])
}],
children=[v.Card(children=[
v.CardTitle(class_='headline gray lighten-2',
children=["Lorem Ipsum"]),
v.CardText(style_='width: "auto";',
children=[lorem_ipsum]),
v.Divider(),
v.CardActions(children=[
v.Spacer(),
accept_btn
])
])])
```
%% Cell type:markdown id: tags:
#### *ipyvuetify* widget events
Widget events for *ipyvuetify* widgets are handled using the `on_event` method.
%% Cell type:code id: tags:
``` python
# Dialog can be closed by clicking the accept button
def close_dialog(*args):
dialog.v_model = False
accept_btn.on_event('click', close_dialog)
```
%% Cell type:code id: tags:
jslink((switch, 'v_model'), (checkbox, 'v_model'))
``` python
page3 = v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, lg6=True, xl4=True, px_4=True, children=[
switch, checkbox, v.Layout(children=[dialog]),
])
])
page3
```
%% Output
%% Cell type:markdown id: tags:
### Switch between contents when the navigation bar is clicked
## Switch between pages when the corresponding entry in the navigation drawer is clicked
%% Cell type:code id: tags:
``` python
# By default, page1 is shown
content_main = v.Layout(
_metadata={'mount_id': 'content-main'},
children=[content1]
children=[page1]
)
```
%% Cell type:code id: tags:
``` python
def switch_to_first(*args):
toolbar_title.children = ['Account']
content_main.children = [content1]
appbar_title.children = ['Account']
content_main.children = [page1]
def switch_to_second(*args):
toolbar_title.children = ['Analytics']
content_main.children = [content2]
appbar_title.children = ['Analytics']
content_main.children = [page2]
def switch_to_third(*args):
toolbar_title.children = ['Settings']
content_main.children = [content3]
appbar_title.children = ['Settings']
content_main.children = [page3]
list_items[0].on_event('click', switch_to_first)
list_items[1].on_event('click', switch_to_second)
list_items[2].on_event('click', switch_to_third)
```
%% Cell type:markdown id: tags:
# Start voila with your custom template
# Advanced usage - Create a custom template
%% Cell type:markdown id: tags:
```bash
voila --template path/to/your/template path/to/your/notebook
## Setting up your custom template
1. Create a directory called `template` to hold your custom template. In that directory, create
a) the sub-directory `nbconvert_templates` and
b) a file `conf.json` which specifies from which template you want to inherit.
> *Example*: To inherit from the base vuetify template, we create the file `conf.json` with
> ```json
{"base_template": "vuetify-base"}
```
2. In the sub-directory `nbconvert_templates`, create a file called `app.html`. This is where you can customize your template.
To get started, you can copy the [base vuetify](https://raw.githubusercontent.com/voila-dashboards/voila-vuetify/master/share/jupyter/voila/templates/vuetify-default/nbconvert_templates/app.html) `app.html` and play around with it.
%% Cell type:markdown id: tags:
## Customizing your template
### Layout and Styling
In the template, you can change the layout and visual properties of the fixed app components, like the navigation drawer or the app bar.
For example, we can customize the styling of the app bar. For possible properties, see the [app bar props](https://vuetifyjs.com/en/components/app-bars#api) from the vueitfy documentation.
Here, we color the app bar blue and apply the dark theme variant to the component to the the title more legible on the dark background.
```html
<v-app-bar color="primary" app absolute dark>
```
### Adding custom components
You can add a new app component that can be updated from the notebook by adding a new widget mount point.
```html
<jupyter-widget-mount-point mount-id="mycustomcomponent">
...
</jupyter-widget-mount-point>
```
Remember to add a mount id and set the mount_id metadata in the widget in the notebook so the output gets rendered.
```python
mycustomcomponent = v.Btn({'mount_id': 'mycustomcomponent'})
```
%% Cell type:markdown id: tags:
For example:
## Starting voila with your custom template
%% Cell type:markdown id: tags:
```bash
voila --template /Users/grosch/Devel/dashboards/mycustomtemplate/ vuetify-custom.ipynb
voila --template full/path/to/your/template path/to/your/notebook
```
For example, using Jupyter Server Proxy (see the [vuetify-bqplot.ipynb](./vuetify-bqplot.ipynb#Using-Jupyter-Server-Proxy) notebook):
%% Cell type:code id: tags:
``` python
import os
print("""voila --template ./mycustomtemplate \
--server_url=/ --base_url={prefix}proxy/{port}/ --VoilaConfiguration.file_whitelist="['.*']" \
./dashboards/vuetify-custom.ipynb
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=8866))
```
%% Output
voila --template ./mycustomtemplate --server_url=/ --base_url=/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866/ --VoilaConfiguration.file_whitelist="['.*']" ./dashboards/vuetify-custom.ipynb
%% Cell type:markdown id: tags:
Dashboard now runs on
https://jupyter-jsc.fz-juelich.de/user/[your_web_account]/[labserver_name]/proxy/[port]
Example:
%% Cell type:code id: tags:
``` python
print("""
https://jupyter-jsc.fz-juelich.de{prefix}proxy/{port}
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=8866))
```
%% Output
https://jupyter-jsc.fz-juelich.de/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
# Got Scotch?
%% Cell type:markdown id: tags:
In this notebook, we're going to create a dashboard that recommends scotches based on their taste profiles.
%% Cell type:markdown id: tags:
voila --template=gridstack examples/ --VoilaConfiguration.resources='{"gridstack": {"show_handles": True}}'
%% Cell type:code id: tags:
``` python
%matplotlib widget
```
%% Cell type:code id: tags:
``` python
import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import os
```
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
from traitlets import Unicode, List, Instance, link, HasTraits
from IPython.display import display, clear_output, HTML, Javascript
```
%% Cell type:code id: tags:
``` python
display(widgets.Button())
```
%% Output
%% Cell type:markdown id: tags:
## Load Data <span style="float: right; font-size: 0.5em"><a href="#Got-Scotch?">Top</a></span>
%% Cell type:code id: tags:
``` python
features = [[2, 2, 2, 0, 0, 2, 1, 2, 2, 2, 2, 2],
[3, 3, 1, 0, 0, 4, 3, 2, 2, 3, 3, 2],
[1, 3, 2, 0, 0, 2, 0, 0, 2, 2, 3, 1],
[4, 1, 4, 4, 0, 0, 2, 0, 1, 2, 1, 0],
[2, 2, 2, 0, 0, 1, 1, 1, 2, 3, 1, 3],
[2, 3, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
[0, 2, 0, 0, 0, 1, 1, 0, 2, 2, 3, 1],
[2, 3, 1, 0, 0, 2, 1, 2, 2, 2, 2, 2],
[2, 2, 1, 0, 0, 1, 0, 0, 2, 2, 2, 1],
[2, 3, 2, 1, 0, 0, 2, 0, 2, 1, 2, 3],
[4, 3, 2, 0, 0, 2, 1, 3, 3, 0, 1, 2],
[3, 2, 1, 0, 0, 3, 2, 1, 0, 2, 2, 2],
[4, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 2],
[2, 2, 1, 0, 0, 2, 2, 0, 0, 2, 3, 1],
[3, 2, 2, 0, 0, 3, 1, 1, 2, 3, 2, 2],
[2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 2],
[1, 2, 1, 0, 0, 0, 1, 1, 0, 2, 2, 1],
[2, 2, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2],
[2, 2, 3, 1, 0, 2, 2, 1, 1, 1, 1, 3],
[1, 1, 2, 2, 0, 2, 2, 1, 2, 2, 2, 3],
[1, 2, 1, 1, 0, 1, 1, 1, 1, 2, 2, 1],
[3, 1, 4, 2, 1, 0, 2, 0, 2, 1, 1, 0],
[1, 3, 1, 0, 0, 1, 1, 0, 2, 2, 2, 1],
[3, 2, 3, 3, 1, 0, 2, 0, 1, 1, 2, 0],
[2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 1, 2],
[2, 3, 2, 1, 0, 0, 1, 0, 2, 2, 2, 1],
[4, 2, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2],
[3, 2, 2, 1, 0, 1, 2, 2, 1, 2, 3, 2],
[2, 2, 2, 0, 0, 2, 1, 0, 1, 2, 2, 1],
[2, 2, 1, 0, 0, 2, 1, 1, 1, 3, 2, 2],
[2, 3, 1, 1, 0, 0, 0, 0, 1, 2, 2, 1],
[2, 3, 1, 0, 0, 2, 1, 1, 4, 2, 2, 2],
[2, 3, 1, 1, 1, 1, 1, 2, 0, 2, 0, 3],
[2, 3, 1, 0, 0, 2, 1, 1, 1, 1, 2, 1],
[2, 1, 3, 0, 0, 0, 3, 1, 0, 2, 2, 3],
[1, 2, 0, 0, 0, 1, 0, 1, 2, 1, 2, 1],
[2, 3, 1, 0, 0, 1, 2, 1, 2, 1, 2, 2],
[1, 2, 1, 0, 0, 1, 2, 1, 2, 2, 2, 1],
[3, 2, 1, 0, 0, 1, 2, 1, 1, 2, 2, 2],
[2, 2, 2, 2, 0, 1, 0, 1, 2, 2, 1, 3],
[1, 3, 1, 0, 0, 0, 1, 1, 1, 2, 0, 1],
[1, 3, 1, 0, 0, 1, 1, 0, 1, 2, 2, 1],
[4, 2, 2, 0, 0, 2, 1, 4, 2, 2, 2, 2],
[3, 2, 1, 0, 0, 2, 1, 2, 1, 2, 3, 2],
[2, 4, 1, 0, 0, 1, 2, 3, 2, 3, 2, 2],
[1, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 1],
[1, 2, 0, 0, 0, 1, 1, 1, 2, 2, 3, 1],
[1, 2, 1, 0, 0, 1, 2, 0, 0, 2, 2, 1],
[2, 3, 1, 0, 0, 2, 2, 2, 1, 2, 2, 2],
[1, 2, 1, 0, 0, 1, 2, 0, 1, 2, 2, 1],
[2, 2, 1, 1, 0, 1, 2, 0, 2, 1, 2, 1],
[2, 3, 1, 0, 0, 1, 1, 2, 1, 2, 2, 2],
[2, 3, 1, 0, 0, 2, 2, 2, 2, 2, 1, 2],
[2, 2, 3, 1, 0, 2, 1, 1, 1, 2, 1, 3],
[1, 3, 1, 1, 0, 2, 2, 0, 1, 2, 1, 1],
[2, 1, 2, 2, 0, 1, 1, 0, 2, 1, 1, 3],
[2, 3, 1, 0, 0, 2, 2, 1, 2, 1, 2, 2],
[4, 1, 4, 4, 1, 0, 1, 2, 1, 1, 1, 0],
[4, 2, 4, 4, 1, 0, 0, 1, 1, 1, 0, 0],
[2, 3, 1, 0, 0, 1, 1, 2, 0, 1, 3, 1],
[1, 1, 1, 1, 0, 1, 1, 0, 1, 2, 1, 1],
[3, 2, 1, 0, 0, 1, 1, 1, 3, 3, 2, 2],
[4, 3, 1, 0, 0, 2, 1, 4, 2, 2, 3, 2],
[2, 1, 1, 0, 0, 1, 1, 1, 2, 1, 2, 1],
[2, 4, 1, 0, 0, 1, 0, 0, 2, 1, 1, 1],
[3, 2, 2, 0, 0, 2, 3, 3, 2, 1, 2, 2],
[2, 2, 2, 2, 0, 0, 2, 0, 2, 2, 2, 3],
[1, 2, 2, 0, 1, 2, 2, 1, 2, 3, 1, 3],
[2, 1, 2, 2, 1, 0, 1, 1, 2, 2, 2, 3],
[2, 3, 2, 1, 1, 1, 2, 1, 0, 2, 3, 1],
[3, 2, 2, 0, 0, 2, 2, 2, 2, 2, 3, 2],
[2, 2, 1, 1, 0, 2, 1, 1, 2, 2, 2, 2],
[2, 4, 1, 0, 0, 2, 1, 0, 0, 2, 1, 1],
[2, 2, 1, 0, 0, 1, 0, 1, 2, 2, 2, 1],
[2, 2, 2, 2, 0, 2, 2, 1, 2, 1, 0, 3],
[2, 2, 1, 0, 0, 2, 2, 2, 3, 3, 3, 2],
[2, 3, 1, 0, 0, 0, 2, 0, 2, 1, 3, 1],
[4, 2, 3, 3, 0, 1, 3, 0, 1, 2, 2, 0],
[1, 2, 1, 0, 0, 2, 0, 1, 1, 2, 2, 1],
[1, 3, 2, 0, 0, 0, 2, 0, 2, 1, 2, 1],
[2, 2, 2, 1, 0, 0, 2, 0, 0, 0, 2, 3],
[1, 1, 1, 0, 0, 1, 0, 0, 1, 2, 2, 1],
[2, 3, 2, 0, 0, 2, 2, 1, 1, 2, 0, 3],
[0, 3, 1, 0, 0, 2, 2, 1, 1, 2, 1, 1],
[2, 2, 1, 0, 0, 1, 0, 1, 2, 1, 0, 3],
[2, 3, 0, 0, 1, 0, 2, 1, 1, 2, 2, 1]]
feature_names = ['Body', 'Sweetness', 'Smoky',
'Medicinal', 'Tobacco', 'Honey',
'Spicy', 'Winey', 'Nutty',
'Malty', 'Fruity', 'cluster']
brand_names = ['Aberfeldy',
'Aberlour',
'AnCnoc',
'Ardbeg',
'Ardmore',
'ArranIsleOf',
'Auchentoshan',
'Auchroisk',
'Aultmore',
'Balblair',
'Balmenach',
'Belvenie',
'BenNevis',
'Benriach',
'Benrinnes',
'Benromach',
'Bladnoch',
'BlairAthol',
'Bowmore',
'Bruichladdich',
'Bunnahabhain',
'Caol Ila',
'Cardhu',
'Clynelish',
'Craigallechie',
'Craigganmore',
'Dailuaine',
'Dalmore',
'Dalwhinnie',
'Deanston',
'Dufftown',
'Edradour',
'GlenDeveronMacduff',
'GlenElgin',
'GlenGarioch',
'GlenGrant',
'GlenKeith',
'GlenMoray',
'GlenOrd',
'GlenScotia',
'GlenSpey',
'Glenallachie',
'Glendronach',
'Glendullan',
'Glenfarclas',
'Glenfiddich',
'Glengoyne',
'Glenkinchie',
'Glenlivet',
'Glenlossie',
'Glenmorangie',
'Glenrothes',
'Glenturret',
'Highland Park',
'Inchgower',
'Isle of Jura',
'Knochando',
'Lagavulin',
'Laphroig',
'Linkwood',
'Loch Lomond',
'Longmorn',
'Macallan',
'Mannochmore',
'Miltonduff',
'Mortlach',
'Oban',
'OldFettercairn',
'OldPulteney',
'RoyalBrackla',
'RoyalLochnagar',
'Scapa',
'Speyburn',
'Speyside',
'Springbank',
'Strathisla',
'Strathmill',
'Talisker',
'Tamdhu',
'Tamnavulin',
'Teaninich',
'Tobermory',
'Tomatin',
'Tomintoul',
'Tormore',
'Tullibardine']
```
%% Cell type:code id: tags:
``` python
features_df = pd.DataFrame(features, columns=feature_names, index=brand_names)
features_df = features_df.drop('cluster', axis=1)
```
%% Cell type:code id: tags:
``` python
norm = (features_df ** 2).sum(axis=1).apply('sqrt')
normed_df = features_df.divide(norm, axis=0)
sim_df = normed_df.dot(normed_df.T)
```
%% Cell type:code id: tags:
``` python
def radar(df, ax=None):
# calculate evenly-spaced axis angles
num_vars = len(df.columns)
theta = 2*np.pi * np.linspace(0, 1-1./num_vars, num_vars)
# rotate theta such that the first axis is at the top
theta += np.pi/2
if not ax:
fig = plt.figure(figsize=(4, 4))
ax = fig.add_subplot(1,1,1, projection='polar')
else:
ax.clear()
for d, color in zip(df.itertuples(), sns.color_palette()):
ax.plot(theta, d[1:], color=color, alpha=0.7)
ax.fill(theta, d[1:], facecolor=color, alpha=0.5)
ax.set_xticklabels(df.columns)
legend = ax.legend(df.index, loc=(0.9, .95))
return ax
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
class RadarWidget(HasTraits):
factors_keys = List(['Aberfeldy'])
def __init__(self, df, **kwargs):
self.df = df
super(RadarWidget, self).__init__(**kwargs)
self.ax = None
self.factors_keys_changed()
def factors_keys_changed(self):
new_value = self.factors_keys
if self.ax:
self.ax.clear()
self.ax = radar(self.df.loc[new_value], self.ax)
```
%% Cell type:markdown id: tags:
We now define a *get_similar( )* function to return the data of the top n similar scotches to a given scotch.
%% Cell type:code id: tags:
``` python
def get_similar(name, n, top=True):
a = sim_df[name].sort_values(ascending=False)
a.name = 'Similarity'
df = pd.DataFrame(a) #.join(features_df).iloc[start:end]
return df.head(n) if top else df.tail(n)
```
%% Cell type:markdown id: tags:
We also need a function *on_pick_scotch* that will display a table of the top 5 similar scotches that Radar View watches, based on a given selected Scotch.
%% Cell type:code id: tags:
``` python
def on_pick_scotch(Scotch):
name = Scotch
# Get top 6 similar whiskeys, and remove this one
top_df = get_similar(name, 6).iloc[1:]
# Get bottom 5 similar whiskeys
df = top_df
# Make table index a set of links that the radar widget will watch
df.index = ['''<a class="scotch" href="#" data-factors_keys='["{}","{}"]'>{}</a>'''.format(name, i, i) for i in df.index]
tmpl = f'''<p>If you like {name} you might want to try these five brands. Click one to see how its taste profile compares.</p>'''
prompt_w.value = tmpl
table.value = df.to_html(escape=False)
radar_w.factors_keys = [name]
plot = radar_w.factors_keys_changed()
```
%% Cell type:code id: tags:
``` python
prompt_w = widgets.HTML(value='Aberfeldy')
display(prompt_w)
```
%% Output
%% Cell type:code id: tags:
``` python
table = widgets.HTML(
value="Hello <b>World</b>"
)
display(table)
```
%% Output
%% Cell type:code id: tags:
``` python
radar_w = RadarWidget(df=features_df)
```
%% Output
%% Cell type:code id: tags:
``` python
picker_w = widgets.interact(on_pick_scotch, Scotch=list(sim_df.index))
```
%% Output
%% Cell type:code id: tags:
``` python
radar_w.factors_keys
```
%% Output
['Aberfeldy']
%% Cell type:markdown id: tags:
Powered by data from https://www.mathstat.strath.ac.uk/outreach/nessie/nessie_whisky.html and inspired by analysis from http://blog.revolutionanalytics.com/2013/12/k-means-clustering-86-single-malt-scotch-whiskies.html. This dashboard originated as a Jupyter Notebook.
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```