Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine a pairplot and a triangular heatmap?

I am trying to make an upper triangle correlation matrix which ideally I would like to superpose to another picture of a lower triangle matrix. Therefore, I would like the mask color to be setup to none or transparent (otherwise if it's white I will not be able to superpose)...any idea about how to do this in seaborn?

Here is the figure I am trying to modify

EDIT

Here is what I would like to do: using a set of columns from dataframe, I would like to plot the pairplot (lower triangle) and the correlation map (upper triangle) of these columns

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


rs = np.random.RandomState(112358)
d1 = pd.DataFrame(data=rs.normal(size=(100, 10)), columns=[*'abcdefghij' ])

corr1 = d1.corr()
mask1 = np.tril(np.ones_like(corr1, dtype=bool))

fig, ax = plt.subplots(figsize=(11, 9))

sns.heatmap(corr1, mask=mask1, cmap='PRGn', vmax=.3, vmin=-.3,
            square=True, linewidths=.5, cbar_kws={"shrink": .85, "pad":-.01}, ax=ax)

def hide_current_axis(*args, **kwds):
    plt.gca().set_visible(False)

e = sns.pairplot(d1)

e.map_upper(hide_current_axis)

plt.show()

This code of course works, but it plots the two figures separately.

like image 279
AG86 Avatar asked Oct 12 '25 03:10

AG86


2 Answers

The normal way to create a triangular heatmap is to mask away the not-needed part. Nothing will be drawn there, the original background color will stay visible. If you draw a second heatmap, it also will only draw where it isn't masked away.

Here is some code to demonstrate the idea.

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

sns.set_theme(style="white")

rs = np.random.RandomState(112358)
d1 = pd.DataFrame(data=rs.normal(size=(100, 10)), columns=[*'abcdefghij' ])
d2 = pd.DataFrame(data=rs.normal(size=(100, 10)), columns=[*'abcdefghij' ])

corr1 = d1.corr()
corr2 = d2.corr()
mask1 = np.tril(np.ones_like(corr1, dtype=bool))
mask2 = np.triu(np.ones_like(corr2, dtype=bool))

fig, ax = plt.subplots(figsize=(11, 9))

sns.heatmap(corr1, mask=mask1, cmap='PRGn', vmax=.3, vmin=-.3,
            square=True, linewidths=.5, cbar_kws={"shrink": .85, "pad":-.01}, ax=ax)
sns.heatmap(corr1, mask=mask2, cmap='RdYlBu', vmax=.3, vmin=-.3,
            square=True, linewidths=.5, cbar_kws={"shrink": .85}, ax=ax)

# the following lines color and hatch the axes background, only the diagonals are visible
ax.patch.set_facecolor('grey')
ax.patch.set_edgecolor('yellow')
ax.patch.set_hatch('xx')

plt.show()

example plot

About the new question, to combine a pairplot with a triangular heatmap. As a pairplot is a figure-level function, it creates its own figure with subplots. It should be created first.

As a second step, a special ax for the heatmap can be created, using the positions of the pairplot's subplots. Setting its facecolor to 'none' makes it fully transparent (the default would be white, hiding everything behind).

Adding a colorbar can be more cumbersome, as the pairplot doesn't leave a good spot to position it.

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

def hide_current_axis(*args, **kwds):
    plt.gca().set_visible(False)

rs = np.random.RandomState(112358)
d1 = pd.DataFrame(data=rs.normal(size=(20, 5)), columns=[*'abcde'])

e = sns.pairplot(d1)
e.map_upper(hide_current_axis)

(xmin, _), (_, ymax) = e.axes[0, 0].get_position().get_points()
(_, ymin), (xmax, _) = e.axes[-1, -1].get_position().get_points()

ax = e.fig.add_axes([xmin, ymin, xmax - xmin, ymax - ymin], facecolor='none')

corr1 = d1.corr()
mask1 = np.tril(np.ones_like(corr1, dtype=bool))
sns.heatmap(corr1, mask=mask1, cmap='seismic', vmax=.5, vmin=-.5,
            linewidths=.5, cbar=False, annot=True, annot_kws={'size': 22}, ax=ax)
ax.set_xticks([])
ax.set_yticks([])
# ax.xaxis.tick_top()
# ax.yaxis.tick_right()

plt.show()

combining pairplot and triangular heatmap

As mentioned in the comments, an approach more faithful to seaborn's philosophy would be to color the axes of the upper right subplots according to the correlation together with a numeric display. I couldn't find example code, here is my attempt:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import pearsonr

def corrfunc(x, y, **kwds):
    cmap = kwds['cmap']
    norm = kwds['norm']
    ax = plt.gca()
    ax.tick_params(bottom=False, top=False, left=False, right=False)
    sns.despine(ax=ax, bottom=True, top=True, left=True, right=True)
    r, _ = pearsonr(x, y)
    facecolor = cmap(norm(r))
    ax.set_facecolor(facecolor)
    lightness = (max(facecolor[:3]) + min(facecolor[:3]) ) / 2
    ax.annotate(f"r={r:.2f}", xy=(.5, .5), xycoords=ax.transAxes,
                color='white' if lightness < 0.7 else 'black', size=26, ha='center', va='center')

rs = np.random.RandomState(112358)
d1 = pd.DataFrame(data=rs.normal(size=(20, 5)), columns=[*'abcde'])

g = sns.PairGrid(d1)
g.map_lower(plt.scatter, s=10)
g.map_diag(sns.histplot, kde=False)
g.map_upper(corrfunc, cmap=plt.get_cmap('seismic'), norm=plt.Normalize(vmin=-.5, vmax=.5))
g.fig.subplots_adjust(wspace=0.06, hspace=0.06) # equal spacing in both directions
plt.show()

coloring the upper right subplots

like image 50
JohanC Avatar answered Oct 14 '25 16:10

JohanC


Sometimes, it is useful to change the size of the correlation value based on the abs(r). Higher values ​​-> larger numbers.

def corrfunc(x, y, **kwds):
    cmap = kwds['cmap']
    norm = kwds['norm']
    ax = plt.gca()
    ax.tick_params(bottom=False, top=False, left=False, right=False)
    sns.despine(ax=ax, bottom=True, top=True, left=True, right=True)
    r, _ = pearsonr(x, y)
    facecolor = cmap(norm(r))
    ax.set_facecolor(facecolor)
    lightness = (max(facecolor[:3]) + min(facecolor[:3]) ) / 2
    tam = int(70*abs(r))
    if tam < 10:
        tam = 10
    ax.annotate(f"{r:.2f}", xy=(.5, .5), xycoords=ax.transAxes,
            color='white' if lightness < 0.7 else 'black', size=tam, ha='center', va='center')
like image 32
F.J. Martinez-de-Pison Avatar answered Oct 14 '25 17:10

F.J. Martinez-de-Pison