3

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

Salvatore
  • 10,815
  • 4
  • 31
  • 69
JAG2024
  • 3,987
  • 7
  • 29
  • 58

1 Answers1

2

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.

Salvatore
  • 10,815
  • 4
  • 31
  • 69
  • 1
    Hi Matthew! Yes, this is what I'm looking for. Could you tell me what version of matplotlib you have installed? I get the error: `'Reds_r' is not a valid value for name; supported values are '538', 'accent', 'acton', 'algae', 'amp', 'balance', 'bamako'...` and will keep iterating to figure this out. Your figure looks good though and is what I'm going for. – JAG2024 Jul 30 '20 at 20:51
  • @JAG2024 Matplotlib is version `3.1.3`, Python is `3.7.6` – Salvatore Jul 30 '20 at 21:18
  • I noticed in your original code you used `reds` (all lowwercase). You could try `reds_r`. – Salvatore Jul 30 '20 at 21:20
  • I updated with a manual method to reverse the colormap. – Salvatore Jul 30 '20 at 21:22
  • 1
    Nice answer +10. It would probably also be beneficial to add sufficient spacing for the colorbar tick labels to not overlap. – Trenton McKinney Jul 30 '20 at 21:26
  • 1
    Once I switched to your matplotlib version with `pip install matplotlib==3.1.3` then it worked using the colormaps: `reds_r`, `blues_r`, etc.! Thanks so much. And yes, agreed on the sufficient spacing per @TrentonMcKinney which works fine w/: `plt.figure(figsize=(10, 6), dpi=80, facecolor='w', edgecolor='k')` – JAG2024 Jul 30 '20 at 21:39