Search code examples
matplotlibseaborn

Pandas: Combine category-based heat map with values-based heatmap?


I'm trying to have a heatmap where each of the 5 categories (x axis) has its own overall color and the value for each month determines how light or dark that square is. I've been using sns.heatmap for the plot below but I'm open to other Python-based options. Does anyone have any suggestions on how to do that?

category by month

I've used the following code and with cmap="Greys" I can see how the value of each square varies, but each vertical column should ideally be a different overall preset color. Note I'm not looking for any additional squares.

sns.heatmap(viewed, linewidth=.5, cmap="Greys", fmt="d")


Solution

  • You can loop through the columns and use each one as a mask. To have consistent dark/light squares, one norm can be calculated for the full set.

    An independent colorbar could be created by combining the norm with a grey colormap.

    from matplotlib import pyplot as plt
    from matplotlib.cm import ScalarMappable
    import seaborn as sns
    import pandas as pd
    import numpy as np
    
    viewed = pd.DataFrame(np.random.rand(10, 5) * 18 + 12, columns=[*'abcde'], index=range(1, 11))
    viewed_np = viewed.to_numpy()
    norm = plt.Normalize(viewed_np.min(), viewed_np.max())
    
    cmaps = ['Reds', 'Blues', 'Oranges', 'Purples', 'Greens']
    
    fig, ax = plt.subplots()
    for col in range(len(viewed.columns)):
        mask = np.ones_like(viewed_np)
        mask[:, col] = 0
        sns.heatmap(viewed, mask=mask, linewidth=.5, cmap=cmaps[col], norm=norm, cbar=False, ax=ax)
    ax.tick_params(axis='y', rotation=0)
    fig.colorbar(ScalarMappable(cmap='Greys', norm=norm), ax=ax)
    fig.tight_layout()
    plt.show()
    

    seaborn heatmap with different cmap per column

    To indicate specific colors instead of the predefined colormaps, a LinearSegmentedColormap can be created between white and a given color:

    colors = ['darkred', 'darkblue', 'saddlebrown', 'darkgreen', 'darkorchid']
    for col, color in zip(range(len(viewed.columns)), colors):
        mask = np.ones_like(viewed_np)
        mask[:, col] = 0
        sns.heatmap(viewed, mask=mask, linewidth=.5, cmap=LinearSegmentedColormap.from_list('', ['white', color]),
                    norm=norm, cbar=False, ax=ax)