Search code examples
pythonmatplotlibseaborn

seaborn heatmap cell style for nan-Values


I have created a heatmap with seaborn in a jupyter notebook:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(rc={"figure.figsize": (10, 10), })
sns.set(font_scale=0.9)
ax = plt.subplots()
ax = sns.heatmap(
    forHeatmap, 
    square=True, # make cells square
    cbar_kws={'fraction' : 0.01, 'label': 'Länge (km)'}, # shrink colour bar
    cmap='OrRd', # use orange/red colour map
    linewidth=0.5 # space between cells
)

And the output (without label on the axis) looks like this: enter image description here

Unfortunately I cannot show the underlying data, but it is a pandas dataframe with the index set on the variable s How can I cange the style (not just the color!) for the Nan-values? I would like to have them as grey squares, just as the squares representing values.

EDIT: When I do the same with small testdata, it looks like this:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

data = pd.DataFrame(
    {"x": (1, 2, 3, 4), "y": (1, 2, np.nan, 4), "z": (14, 15, 23, 2)}
).set_index("x")

The dataframe looks now like it, with the index set on the first column (x):

enter image description here

Now, I plot with this:

sns.set(
    rc={
        "figure.figsize": (5, 5),
    }
)
sns.set(font_scale=0.9)
ax = plt.subplots()
ax = sns.heatmap(
    data,
    square=True,  # make cells square
    cmap="OrRd",  # use orange/red colour map
    linewidth=0.5,  # space between cells
)

And the result is:

enter image description here

As shown, the cell for the nan-value is represented as 4 small squares instead of one. How can I change it? As I couldn't find a thread regarding this topic I think it may be jupyter notebook related?


Solution

  • By default, the nan or masked values are drawn transparent. You can change the background color via ax.set_facecolor('grey'). The white lines normally come from the edge color of the cells. But in this case, there also seem to be white gridlines (maybe set via general matplotlib configuration parameters).

    To change the background color, use ax.set_facecolor('grey'). Turn off the grid via ax.grid(False). And if nothing else helps, you can assign a non-transparent color to the cells: ax.collections[0].cmap.set_bad('grey').

    import matplotlib.pyplot as plt
    import seaborn as sns
    import pandas as pd
    import numpy as np
    
    data = pd.DataFrame(
        {"x": (1, 2, 3, 4), "y": (1, 2, np.nan, 4), "z": (14, 15, 23, 2)}
    ).set_index("x")
    sns.set(
        rc={
            "figure.figsize": (5, 5),
        },
        font_scale=0.9
    )
    fig = plt.figure()
    ax = sns.heatmap(
        data,
        square=True,  # make cells square
        cmap="OrRd",  # use orange/red colour map
        linewidth=0.5,  # space between cells
    )
    ax.collections[0].cmap.set_bad('0.7')
    plt.tight_layout()
    plt.show()
    

    changing color of NaN cells in seaborn heatmap

    PS: Note that plt.subplots() creates both a figure and an ax. If you want to use that ax, you need to assign the result via fig, ax = plt.subplots() and then use it as parameter in the seaborn function: sns.heatmap(..., ax=ax).