Reputation: 33
I'm trying to generate a Heatmap in Holoviews with custom color values for each point, in order to specify different alpha color values.
Currently I'm generating two Holoviews plots with different alpha values and overlay them like so:
data = pd.DataFrame([(i, 97+j, i*j) for i in range(5) for j in range(5)],
columns=['x', 'y', 'val'])
data_filtered = data[(data.x < 3) & (data.y < 100)]
hm_opts = dict(kdims=['x', 'y'], vdims=['val'])
hm = hv.HeatMap(data, **hm_opts).opts(alpha=0.5)
hm_filtered = hv.HeatMap(data_filtered, **hm_opts).opts()
hm * hm_filtered
This however, gives me a Holoviews Overlay object, where I'd like to have a single HeatMap object instead.
My idea was to map each x/y coordinate to a specific color value in hex form, which has the needed alpha already calculated into it. My DataFrame would therefore look something like this (example):
x y val color
0 0 97 0 #00FF00
1 0 98 0 #00FF00
2 0 99 0 #00FF00
...
22 4 99 8 #FFD29F
23 4 100 12 #FFB89F
24 4 101 16 #D3AFF4
Is there a way to tell Holoviews to use these colors? When I pass the list of colors to the "cmap" parameter it interpretes it as color intervals, passing the name of the column raises an error as it cannot find the specified cmap.
When I pass the list to the "color" parameter, the plot is not displayed at all anymore in the Jupyter Notebook.
I found a way to get what I want using the Bokeh library directly. Bokeh is also the backend I'm using with Holviews. This is the code and the resulting plot.
source = ColumnDataSource(
data=data
)
x_unique = data['x'].unique()
y_unique = data['y'].unique()
min_width = 110
min_height = 80
width = min_width + 25 * len(x_unique)
height = min_height + 25 * len(y_unique)
x_rect_width = 0.90
y_rect_width = 0.90
plot = figure(
plot_width=width,
plot_height=height,
title='',
x_range=FactorRange(*x_unique),
y_range=FactorRange(*y_unique),
x_axis_label='x',
y_axis_label='y',
)
plot.rect('x', 'y', height=y_rect_width, width=x_rect_width, source=source, color='color')
plot.xgrid.grid_line_color = None
plot.ygrid.grid_line_color = None
show(plot)
Bokeh allows to pass the color column name to the 'color' parameter of the rect function. Anyway, I'd still love to have it as a Holoviews container, so that I can combine it and build interactive apps on top of it.
With the help of the answer of @thomas-pedot I was able to find a solution which looks like what I was looking for:
data = pd.DataFrame([(i, 97+j, i*j) for i in range(5) for j in range(5)],
columns=['x', 'y', 'val'])
data = data.assign(alpha=((data.x < 3) & (data.y < 100)).replace({True: 1.0, False: 3/8}))
red = '#FF0000'
yellow = '#FFFF00'
green = '#00FF00'
blue_violet = '#8A2BE2'
max_cout = data.loc[:, column].max()
levels = [0, 1, max_cout / 2, max_cout - 1, max_cout]
colors = [green, yellow, red, blue_violet]
hm_opts = dict(kdims=['x', 'y'], vdims=['val', 'alpha'])
hm = hv.HeatMap(data, **hm_opts).opts(
alpha=hv.dim('alpha'),
color_levels=levels,
cmap=colors)
hm
Upvotes: 3
Views: 1684
Reputation: 993
Not easy... I try a lot of different converting to rgba then back to HEX cmap without success but !
Make it with style http://holoviews.org/user_guide/Style_Mapping.html
data = pd.DataFrame([(i, 97+j, i*j) for i in range(5) for j in range(5)],
columns=['x', 'y', 'val'])
data['a_new'] = 0.5
# Make a mask of you filter
mask = (data.x < 3) & (data.y < 100)
# Add it to a column
data1.loc[mask, 'a_new'] = 1.0
#Add the new column to a new dimension
hm_opts_new = dict(kdims=['x', 'y'], vdims=['val','a_new'])
#Enjoy ? I hope... set the alpha option depends on your new column
hv.HeatMap(data1, **hm_opts_new).opts(opts.HeatMap(tools=['hover'], alpha=dim('a_new')))
Upvotes: 0
Reputation: 3066
import pandas as pd
import holoviews as hv
import matplotlib as mpl
hv.extension('matplotlib')
data = pd.DataFrame([(i, 97+j, i*j) for i in range(5) for j in range(5)],
columns=['x', 'y', 'val'])
data_filtered = data[(data.x < 3) & (data.y < 100)]
cmap1 = mpl.colors.ListedColormap(['#00FF00', '#FFB89F', '#D3AFF4'])
hm_opts = dict(kdims=['x', 'y'], vdims=['val'])
hm = hv.HeatMap(data, **hm_opts).opts(alpha=0.5, cmap=cmap1)
results:
I added the line cmap1 = mpl.colors.ListedColormap(['#00FF00', '#FFB89F', '#D3AFF4'])
which allows you to specifiy which colors you want. If you update it to a list of colors (a list in range of your whole heatmap) then it will be exactly as you want. I guess you can work with df["color"]
in your case (maybe directly convert to list first, but it will be alright)
Notice I also added cmap=cmap1
param to heatmap' opt
.
I assume you work with matplotlib in background, since you didn't specify otherwise.
Upvotes: 1