How to get joint kernel density plots to look better on black background (seaborn)

Viewed 315

I have three datasets (df1, df2, df3, randomly generated here as an example) that I want to plot the joint kernel densities together on a black background. I don't like how the overlapping parts of the joint kdes look because when the white parts (lowest densities) overlap against the black background it really sticks out. In contrast this looks fine against a white background (will include at the bottom for comparison), but I need it to be a black background.

An idea for how to make this better could be:

  1. Reverse the color bar so that the lowest densities are the dark colors and the higher densities are the bright colors.

Does anyone know how to do this or how to make it better?

I didn't know where to start looking but I found this closed issue on seaborn's GitHub that talks a little bit about shading.

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


# Uncomment for black background figure
plt.rcParams.update({
    "grid.color": "white",
    'hatch.color': 'k',
    "lines.color": "white",
    "patch.edgecolor": "white",
    'patch.facecolor': ([0, 1, 1]),
    'grid.alpha': 0.4,
    "text.color": "lightgray",
    "axes.facecolor": "black",
    "axes.edgecolor": "lightgray",
    "axes.labelcolor": "white",
    "xtick.color": "white",
    "ytick.color": "white",
    "grid.color": "lightgray",
    "figure.facecolor": "black",
    "figure.edgecolor": "black",
    "savefig.facecolor": "black",
    "savefig.edgecolor": "black"})

df1 = pd.DataFrame(np.random.randint(0,60,size=(1500, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.randint(20,80,size=(1500, 4)), columns=list('ABCD'))
df3 = pd.DataFrame(np.random.randint(40,100,size=(1500, 4)), columns=list('ABCD'))

f, axs = plt.subplots()

# Draw density plots
axs = sns.kdeplot(df1.A, df1.B, alpha=0.5,
                 cmap="Reds", shade=True, shade_lowest=False, cbar=True)
axs = sns.kdeplot(df2.A, df2.B, alpha=0.5,
                 cmap="Oranges", shade=True, shade_lowest=False, cbar=True)
axs = sns.kdeplot(df3.A, df3.B, alpha=0.5,
                 cmap="Blues", shade=True, shade_lowest=False, cbar=True)

enter image description here

enter image description here

1 Answers

Under the hood, seaborn is using matplotlib colormaps.

This answer provides some insight on reversing matplotlib colormaps, and is applicable here:

All standard colormaps also all have reversed versions. They have the same names with _r tacked on to the end. (Documentation here).

Some of your colormaps were not working on the latest version, so I used Reds, Oranges, and Blues instead. Notice that I swapped them to Reds_r, Oranges_r and Blues_r. I believe this is the result you are looking for.

# Draw density plots
axs = sns.kdeplot(
    df1.A, df1.B, alpha=0.5, cmap="Reds_r", shade=True, shade_lowest=False, cbar=True,
)
axs = sns.kdeplot(
    df2.A, df2.B, alpha=0.5, cmap="Greens_r", shade=True, shade_lowest=False, cbar=True
)
axs = sns.kdeplot(
    df3.A, df3.B, alpha=0.5, cmap="Blues_r", shade=True, shade_lowest=False, cbar=True
)

enter image description here

Update:

You can manually reverse a color map of choice by getting it, and calling reverse on it:

color_map = plt.cm.get_cmap('Blues')
reversed_color_map = color_map.reversed()

Then give your reversed color map to kdeplot:

axs = sns.kdeplot(
    df3.A, df3.B, alpha=0.5, cmap=reversed_color_map , shade=True, shade_lowest=False, cbar=True
)

This way you can use any color map, even one that doesn't have a predefined _r.

Related