Nathan
Nathan

Reputation: 10326

How can I have multiple heatmaps share axes in holoviews?

I'm creating two heatmaps which have different ranges of data. I'd like to plot them together with the same extents, with empty cells added as needed to make them the same size. Here's code for two sample heatmaps:

import random
import holoviews as hv
hv.extension("matplotlib")

data1 = [(x, y, random.random()) for x in range(3) for y in range(6)]
data2 = [(x, y, random.random() * 3) for x in range(7) for y in range(2)]
hmap1 = hv.HeatMap(data1)
hmap2 = hv.HeatMap(data2)
combined = (hmap1 + hmap2).opts(hv.opts.HeatMap(show_values=False, colorbar=True))

which renders as

enter image description here

with one taller heatmap and one wider. I'd like them both to be 7x6 in this example. I tried doing combined.opts(shared_axes=True), but the results are the same. Doing hmap1.redim.values(x=[0, 1, 2, 3, 4, 5, 6]) also produces the same plot.

How can I resize multiple (not necessarily just two) heatmaps to plot them together with the same grid?

Upvotes: 0

Views: 42

Answers (1)

Nathan
Nathan

Reputation: 10326

It's a bit hacky, but the best way I've found to do this is to create new heatmaps from expanded data that just uses NaN for the empty values. Here's a quick function to do so:

import random

import holoviews as hv
import pandas as pd

hv.extension("matplotlib")

data1 = [(x, y, random.random()) for x in range(3) for y in range(6)]
data2 = [(x, y, random.random() * 3) for x in range(7) for y in range(2)]
hmap1 = hv.HeatMap(data1)
hmap2 = hv.HeatMap(data2)

def combine_hmaps(*hmaps: hv.HeatMap) -> list[hv.HeatMap]:
    "WARNING: the heatmaps are modified in-place (but also returned for convenience)."
    all_xys = set()
    for hmap in hmaps:
        all_xys.update(tuple(xy) for xy in hmap.data.iloc[:, :2].values)
    
    for hmap in hmaps:
        xys = {tuple(xy) for xy in hmap.data.iloc[:, :2].values}
        df = pd.DataFrame([(x, y, float("nan")) for x, y in all_xys - xys], columns=hmap.data.columns)
        hmap.data = pd.concat([hmap.data, df])
    return [hmap.sort() for hmap in hmaps]


combined_hmaps = combine_hmaps(hmap1, hmap2)
hv.Layout(combined_hmaps).opts(hv.opts.HeatMap(show_values=False, colorbar=True))

This gives

enter image description here

There's probably a slightly prettier way to do this using pandas better, but I'd be especially interested if anybody has a solution which doesn't require modifying the data / making new heatmaps but just using holoviews options.

EDIT: I modify the existing heatmaps instead of creating new ones so that their options (title, xlabel, etc.) aren't lost. You could also create a copy of the original first if you don't want it modified.

Upvotes: 0

Related Questions