Hi,
1 - you were pretty close, it can be confusing but you need to instantiate your Tap stream with a default value for x and y, It’s not like the other syntaxe where you put the columns names.
posxy = hv.streams.Tap(source=heatmap, x=10, y=10)
Then to create a gaussian you can use hv.Distribution
def tap_histogram(x, y):
points = np.random.normal(int(y), int(x),1000)
h= hv.Distribution(points).opts(width=500, height=500)
return h
2 - In order to customize the hover tool your can import HoverTool from bokeh.models and create a custom tool. Check this page
hoverCustom = HoverTool(tooltips={'mean':'@x','std':'@y','cv':'@z'})
3 - Sorry, I’ve never tried to save holoviews as HTML
Here is the complete code :
import pandas as pd
import numpy as np
import holoviews as hv
from holoviews import opts
hv.extension('bokeh')
from bokeh.models import HoverTool
from holoviews import streams
# here the mean and std vales are given (hardcoded)
mu = [-1, 5, 10]
std = [10, 50, 90]
# make an empty list to append coefficient of variation
cov = []
# calculate the coefficient of variation c_v = std/mu for all combos of std and mu
for i in range(0, len(mu)):
for j in range(0, len(std)):
cov.append(std[j] / mu[i])
# to place the values of std and mean on a 2D plot, values have to be
# repeated in a certain way, therefore
std_rep = np.tile(std, 3)
mu_rep = np.repeat(mu, 3)
sigma = std_rep.astype(str)
mean = mu_rep.astype(str)
###heatmap entries per cell have to be strings, like this
sigma2=['10', '50', '90', '10', '50', '90', '10', '50', '90']
mean2 = ['-1', '-1', '-1', '5', '5', '5', '10', '10', '10']
################################################
# make heatmap
heatmap = hv.HeatMap((sigma, mean, cov))
# declare tap stream with heatmap as source and initial values
posxy = hv.streams.Tap(source=heatmap, x=10, y=10)
# Define function to compute histogram based on tap location
def tap_histogram(x, y):
points = np.random.normal(int(y), int(x),1000)
h= hv.Distribution(points).opts(width=500, height=500)
return h
hoverCustom = HoverTool(tooltips={'mean':'@x','std':'@y','cv':'@z'})
heat=(heatmap).opts(opts.HeatMap(cmap='RdBu', tools=[hoverCustom, 'tap'], colorbar=True,
width=500, height=500, toolbar='above', clim=(-100,15),
title ='coefficient of variation',
fontsize={'xticks': '10pt', 'yticks': '10pt',
'xlabel': '10pt', 'ylabel': '10pt',
'title': '15pt'},
xlabel='STD', ylabel='MEAN'
))
tap_dmap = hv.DynamicMap(tap_histogram, streams=[posxy])
(heat+tap_dmap)
This code works but the range of the dynamic_map does not change so you may need to zoom_out.
I’ve another version using panel, in which I update programmatically the ranges. If you’re insterested you can take a look at the panel doc.
Here is the other version :
import pandas as pd
import numpy as np
import holoviews as hv
from holoviews import opts
hv.extension('bokeh')
from bokeh.models import HoverTool
from holoviews import streams
import panel as pn
import param
class HeatView(param.Parameterized):
# here the mean and std vales are given (hardcoded)
mu = [-1, 5, 10]
std = [10, 50, 90]
# make an empty list to append coefficient of variation
cov = []
# calculate the coefficient of variation c_v = std/mu for all combos of std and mu
for i in range(0, len(mu)):
for j in range(0, len(std)):
cov.append(std[j] / mu[i])
# to place the values of std and mean on a 2D plot, values have to be
# repeated in a certain way, therefore
std_rep = np.tile(std, 3)
mu_rep = np.repeat(mu, 3)
sigma = std_rep.astype(str)
mean = mu_rep.astype(str)
###heatmap entries per cell have to be strings, like this
sigma2=['10', '50', '90', '10', '50', '90', '10', '50', '90']
mean2 = ['-1', '-1', '-1', '5', '5', '5', '10', '10', '10']
################################################
# make heatmap
heatmap = hv.HeatMap((sigma, mean, cov))
# declare tap stream with heatmap as source and initial values
posxy = hv.streams.Tap(source=heatmap, x=10, y=10)
gauss=param.Parameter(hv.Curve([]))
# Define function to compute histogram based on tap location
def tap_histogram(self,x, y):
points = np.random.normal(int(y), int(x),1000)
h= hv.Distribution(points).opts(width=500, height=500)
h.redim.range(x=(int(y)-1.5*int(x),int(y)+1.5*int(x)))
self.gauss=h
hoverCustom = HoverTool(tooltips={'mean':'@x','std':'@y','cv':'@z'})
def view(self):
self.posxy.add_subscriber(self.tap_histogram)
heat=(self.heatmap).opts(opts.HeatMap(cmap='RdBu', tools=[self.hoverCustom, 'tap'], colorbar=True,
width=500, height=500, toolbar='above', clim=(-100,15),
title ='coefficient of variation',
fontsize={'xticks': '10pt', 'yticks': '10pt',
'xlabel': '10pt', 'ylabel': '10pt',
'title': '15pt'},
xlabel='STD', ylabel='MEAN'
))
return pn.Pane(heat)
@param.depends('gauss')
def dynamic_view(self):
return pn.Pane(self.gauss)
heat = HeatView()
pn.Row(heat.view,heat.dynamic_view)