Black Lives Matter. Please consider donating to Black Girls Code today.

Heatmaps with multicolor cells

Hi,

I am trying to plot the occurrence of some predefined events (say A, B, C) throughout the year using a “calendar heatmap”, similar to that proposed in Colored calendar heatmap in dash.
Problem is, on a given day multiple events may occur. I would like to plot the different combinations (A+B, B+C, A+B+C, …) by splitting the associated heatmap cells in halves (or thirds) and color them with the labels’ colors.

This figure depicts an example:
multi_label_heatmap

What would be the best way to achieve this? I’ve considered different options but they all come with shortcomings:

  1. Use a datatable and then perform conditional style formatting of the cells with linear gradients. However, this then prevents me from using hover callbacks (I guess I could live without them but it would be nice to use them).
  2. Plot the heatmap for a single color, and then “fix” it for the remaining colors by adding some rectangle shapes on top. However, this feels hacky and it may be hard to make sure the colored rectangles align perfectly.

Any suggestions?
Thanks!

Hi @ruisilva, welcome to the forum! You could also have a given number of go.Heatmap cells for each day, and color them accordingly. In your example with A, B, C you would need 6 cells per day (so that you can do either 6 cells for one color, 3 + 3 or 2 + 2 +2). For more possible events n you would need to compute the least common multiplier (lcm, see numpy.lcm) of 2, 3, … n.

Hi @Emmanuelle,
Thanks for the fast reply! I followed your advice and it is mostly working.
My only problem now regards the separation of the 6x6 blocks.

It seems that xgap and ygap parameters of Heatmap separate the small individual blocks.
I’ve also tried setting a grid in the x and yaxis but it does not seem to show up:

Here is the code I’ve used to generate the example above:

def small_example():
    import plotly.graph_objects as go
    import itertools
    import numpy as np

    lcm = 6
    # 2 x 3 list of events
    nrow, ncol = 2, 3
    labels = [[[1], [4, 2, 3], [2]],
              [[3], [2, 3], [1]]]
    data = np.array(labels)

    def expand_cols(x):
        return list(
            itertools.chain.from_iterable(
                itertools.repeat(n, lcm // len(x)) for n in x))
    expander = np.vectorize(expand_cols, otypes=[np.object])
    data = expander(data) # expand cols
    
    data = np.vstack(data.tolist()) # make np array
    data = data.reshape(nrow, -1)
    
    data = np.repeat(data, lcm, axis=0) # expand rows

    data = [
        go.Heatmap(
            z=data,
            hoverinfo="text",
            # xgap=1,
            # ygap=1,
            showscale=True),
    ]
    layout = go.Layout(
        yaxis=dict(
            showline=False,
            showgrid=True,
            gridcolor='white',
            gridwidth=2,
            zeroline=False,
            tickmode='array',
            ticktext=['R2', 'R1'],
            tickvals=np.arange(lcm / 2 - .5, nrow * lcm, lcm),
        ),
        xaxis=dict(
            showline=False,
            showgrid=True,
            gridcolor='black',
            gridwidth=2,
            zeroline=False,
            tickmode='array',
            ticktext=['C1', 'C2', 'C3'],
            tickvals=np.arange(lcm / 2 - .5, ncol * lcm, lcm),
        ),
    )
    fig = go.Figure(data=data, layout=layout)
    fig.show()

small_example()

I was able to achieve the desired effect in matplotlib using ax.grid. Is there something similar in dash?

Thanks!