J'essaie de générer une carte thermique dans Holoviews avec des valeurs de couleur personnalisées pour chaque point, afin de spécifier différentes valeurs de couleur alpha.

Actuellement, je génère deux graphiques Holoviews avec des valeurs alpha différentes et je les superpose comme suit:

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

Plot

Cependant, cela me donne un objet Holoviews Overlay, où j'aimerais avoir un seul objet HeatMap à la place.

Mon idée était de mapper chaque coordonnée x / y à une valeur de couleur spécifique sous forme hexadécimale, qui contient l'alpha nécessaire déjà calculé. Mon DataFrame ressemblerait donc à ceci (exemple):

    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

Existe-t-il un moyen de dire à Holoviews d'utiliser ces couleurs? Lorsque je passe la liste des couleurs au paramètre «cmap», il l'interprète comme des intervalles de couleur, le fait de transmettre le nom de la colonne soulève une erreur car il ne peut pas trouver le cmap spécifié.

Lorsque je passe la liste au paramètre "couleur", le tracé ne s'affiche plus du tout dans le bloc-notes Jupyter.

Éditer

J'ai trouvé un moyen d'obtenir ce que je veux en utilisant directement la bibliothèque Bokeh. Bokeh est également le backend que j'utilise avec Holviews. Voici le code et le tracé résultant.

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)

Plot2

Bokeh permet de passer le nom de la colonne de couleur au paramètre 'color' de la fonction rect. Quoi qu'il en soit, j'aimerais toujours l'avoir en tant que conteneur Holoviews, afin de pouvoir le combiner et créer des applications interactives par-dessus.

Solution

Avec l'aide de la réponse de @ thomas-pedot j'ai pu trouver une solution qui ressemble à ce que je recherchais:

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

Solution

2
iRave 19 oct. 2020 à 16:01

2 réponses

Meilleure réponse

Pas facile ... j'essaye pas mal de conversion différente en rgba puis retour en HEX cmap sans succès mais!

Faites-le avec 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')))
0
Thomas PEDOT 21 oct. 2020 à 18:51
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)

Résultats:

results

J'ai ajouté la ligne cmap1 = mpl.colors.ListedColormap(['#00FF00', '#FFB89F', '#D3AFF4']) qui vous permet de spécifier les couleurs que vous souhaitez. Si vous le mettez à jour avec une liste de couleurs (une liste dans la plage de toute votre carte de chaleur), ce sera exactement comme vous le souhaitez. Je suppose que vous pouvez travailler avec df["color"] dans votre cas (peut-être convertir directement en liste en premier, mais ça ira)

Remarquez que j'ai également ajouté le paramètre cmap=cmap1 à la carte thermique 'opt.

Je suppose que vous travaillez avec matplotlib en arrière-plan, car vous n'avez pas spécifié le contraire.

1
Roim 21 oct. 2020 à 12:48