Search code examples
pythonbokehheatmap

Bokeh: show text in heatmap cells


I make heatmap:

enter image description here

With this code:

import pandas as pd
from bokeh.models import BasicTicker, ColorBar, LinearColorMapper, PrintfTickFormatter, LabelSet
from bokeh.plotting import figure, show

df = pd.read_csv('data.csv', sep=';')

colors = ["#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce", "#ddb7b1", "#cc7878", "#933b41", "#550b1d"]
mapper = LinearColorMapper(palette=colors, low=df.cpu_sum.min(), high=df.cpu_sum.max())

p = figure(title="Heatmap", toolbar_location=None,
           x_range=days, y_range=list(reversed(hours)),
           x_axis_location="above", width=600, height=600)

p.rect(x="startdate_dayweek", y="startdate_hour", width=1, height=1,
       source=df,
       fill_color={'field': 'cpu_sum', 'transform': mapper},
       line_color=None)

show(p)

So, I want to show 'cpu_sum' values in heatmap cells, like on this model: enter image description here

I try these code lines but it doesn't work:

labels = LabelSet(x='startdate_dayweek', y='startdate_hour', text='cpu_sum', level='glyph',
                  x_offset=1, y_offset=1, source=df,
                  render_mode='canvas')
p.add_layout(labels)

The error message:

failed to validate LabelSet(id='21897', ...).source: expected an instance of type DataSource, got     startdate_hour startdate_dayweek  cpu_sum
0                0                 1       62
1                0                 2    27999
2                0                 3    27937
[...]
166              9                 6    22027
167              9                 7    33259

[168 rows x 3 columns] of type DataFrame

Solution

  • You have to make two changes:

    1. make sure that the values you want to add are of type string, otherwise you will break the figure.
    2. create a ColumnDataSource like source= ColumnDataSource(df) and pass this to the source keyword in LabelSet.

    Below you can see a minimal example with your names for the columns but with fake data.

    import pandas as pd
    from bokeh.models import LinearColorMapper, LabelSet, ColumnDataSource
    from bokeh.plotting import figure, show, output_notebook
    output_notebook()
    
    # create data
    days = []
    for i in range(7):
        days.extend([i]*24)
    hours = list(range(24))*7
    df = pd.DataFrame({
        "startdate_dayweek":days,
        "startdate_hour":hours,
        "cpu_sum":list(range(24*7)),
    })
    df['cpu_str'] = df['cpu_sum'].astype(str)
    
    # create figure
    source= ColumnDataSource(df)
    colors = ["#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce", "#ddb7b1", "#cc7878", "#933b41", "#550b1d"]
    mapper = LinearColorMapper(palette=colors, low=df.cpu_sum.min(), high=df.cpu_sum.max())
    
    p = figure(
        title="Heatmap",
        toolbar_location=None,
        x_axis_location="above",
        x_range=(-0.5,6.5),
        y_range=(-0.5,23.5),
        width=600,
        height=600
    )
    
    p.rect(
        x="startdate_dayweek",
        y="startdate_hour",
        width=1,
        height=1,
        source=source,
        fill_color={'field': 'cpu_sum', 'transform': mapper},
        line_color=None
    )
    labels = LabelSet(
        x='startdate_dayweek',
        y='startdate_hour',
        text='cpu_str',
        level='glyph',
        text_align='center',
        y_offset=-7,
        source=source,
        render_mode='canvas'
    )
    p.add_layout(labels)
    
    show(p)
    

    heatmap