I have two omics datasets which I woud like to compare. For this I plot one as a clustermap, and extract the order from it and plot the exact same genes as a heatmap, so that I can make a direct comparison between the two datasets. However, I would like to show the colorbars for both, and I cannot seem to get it to work.
I used this to combine the two plots, and this to adjust the colorbar of the clustermap, but this does not work as a function for the heatmap. I was playing around with cbar_kws but I don't think this allows me to do what I want, though I don't fully understand the potential there I think.
Using python in VScode;
import matplotlib.gridspec
#clustermap from averaged protein data!
g = sns.clustermap(hmprot, figsize=(8,12), col_cluster=False, yticklabels=True, cmap = 'viridis')
labels = [t.get_text() for t in g.ax_heatmap.yaxis.get_majorticklabels()]
g.gs.update(left=0.05, right=0.49)
#create new gridspec for the right part
gs2 = matplotlib.gridspec.GridSpec(1,1)
hmrna = hmrna.reindex(index=labels)
# create axes within this new gridspec
ax2 = g.fig.add_subplot(gs2[0])
# get position of heatmap
heatmap_bbox = g.ax_heatmap.get_position()
ax2.set_position([0.5, heatmap_bbox.y0, .35, heatmap_bbox.height])
# plot heatmap in the new axes
sns.heatmap(hmrna, ax=ax2, cmap = 'viridis', cbar=True, yticklabels=True,
cbar_kws= dict(use_gridspec=False,location = 'right', shrink= 0.5))
#change font size for the genes on the y-axis
g.tick_params(axis='y', labelsize=6, labelright = False, right=False)
g.tick_params(axis='x', labelbottom = True, bottom=False)
ax2.tick_params(axis='y', labelsize=8, labelright=True, left=False, labelleft=False, labelrotation = 0)
ax2.tick_params(axis='x', labelbottom = True, bottom=False)
#then adjusting the labels for each axis individually
# g.set_xlabel(' ') -> Doesn't work
ax2.set_xlabel(' ')
ax2.set_title('RNAseq', weight="bold")
# ax2.set_title('Proteomics(C)', weight="bold") -> Doesn't work
x0, _y0, _w, _h = g.cbar_pos
g.ax_cbar.set_position([0.01, 0.04, 0.02, 0.1])
g.ax_cbar.set_title('Z-score')
g.ax_cbar.tick_params(axis='x', length=10)
title = "Clustermap of Protein (left) & RNA (right) " + str(GO_term)
plt.suptitle(title, weight="bold", fontsize=20, y=0.85)
fig.tight_layout()
plt.show()
I have also tried the suggestion mentioned here, with the following code;
cbar_2_ax = fig.add_axes([0.95, 0.04, 0.02, 0.1])
cbar_2 = mp.colorbar(ax2, cax=cbar_2_ax)
Then I get an error :'Axes' object has no attribute 'get_array' I'm not sure how to proceed now, I'm probably not using the right functions to change this but I have not been able to find something that does work.
In addition, I managed to 'configure' the heatmap with its own title, and removed the 'group' label on the x-axis. I have not been able to figure out how to do the same for the clustermap, to give it it's own title, and remove this 'group' label. The same functions for the heatmap do not work here.
A dummy version of my code that replicates the issues for me. How do I fix the right colorbar??
Some dummy version of my code that replicates the issue:
df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
#clustermap from df1
g = sns.clustermap(df, figsize=(12,18), col_cluster=False, yticklabels=True, cmap = 'viridis')
g.gs.update(left=0.05, right=0.49)
#create new gridspec for the right part
gs2 = matplotlib.gridspec.GridSpec(1,1)
# create axes within this new gridspec
ax2 = g.fig.add_subplot(gs2[0])
# get position of heatmap
heatmap_bbox = g.ax_heatmap.get_position()
ax2.set_position([0.5, heatmap_bbox.y0, .35, heatmap_bbox.height])
# plot boxplot in the new axes
sns.heatmap(df2, ax=ax2, cmap = 'viridis', cbar=True, yticklabels=True,
cbar_kws= dict(use_gridspec=False,location = 'right', shrink= 0.5)
)
g.tick_params(axis='y', labelsize=6, labelright = False, right=False)
g.tick_params(axis='x', labelbottom = True, bottom=False)
ax2.tick_params(axis='y', labelsize=8, labelright=True, left=False, labelleft=False, labelrotation = 0)
ax2.tick_params(axis='x', labelbottom = True, bottom=False)
ax2.set_title('title', weight="bold") # Set a custom title
x0, _y0, _w, _h = g.cbar_pos
g.ax_cbar.set_position([0.01, 0.04, 0.02, 0.1])
g.ax_cbar.set_title('Z-score')
g.ax_cbar.tick_params(axis='x', length=10)
title = "Clustermap (left) & heatmap (right) "
plt.suptitle(title, weight="bold", fontsize=20, y=0.85)
fig.tight_layout()
plt.show()
Below is my output figure from this code. I've highlighted the issues I'm hoping to fix.
Below you find some example code to adapt the layout and add the colorbars.
Some remarks:
sns.clustermap
has a parameter cbar_pos
to directly set the position of the colobarsns.clustermap
also has a parameter dendrogram_ratio
which define the space for the row and column dendrograms (the column dendrograms aren't used in this example, so the spacing can be set smaller)g.tick_params
changes the ticks of all the subplots. This probably causes the missing ticks of the colorbars. To only change the ticks of the clustermap's heatmap, you can use g.ax_heatmap.tick_params()
g.ax_heatmap.set_title()
sets a title for the clustermapg.ax_heatmap.set_xlabel('')
removes the label (apparently labeled 'group' in the original image) of the clustermapSomething that is rather unclear, is that the clustermap changes the order of the rows (it places "similar" rows closer together). But the heatmap and its row labels at the right still seem to use their original order.
To reorder the rows of the heatmap to the same order as the clustermap, the y-tick-labels of the clustermap can be extracted. Then, df2.reindex(...)
reorders the rows of the heatmap. As the tick labels are strings, the example code below supposes the index of the dataframe are strings.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
index = [f'r{i:02}' for i in range(50)]
df = pd.DataFrame(np.random.randint(0, 100, size=(50, 4)), columns=list('ABCD'), index=index)
df2 = pd.DataFrame(np.random.randint(0, 100, size=(50, 4)), columns=list('ABCD'), index=index)
# clustermap from df1
g = sns.clustermap(df, figsize=(12, 18), col_cluster=False, yticklabels=True, cmap='viridis',
dendrogram_ratio=(0.12, 0.04), # space for the left and top dendograms
cbar_pos=[0.02, 0.04, 0.02, 0.1])
g.ax_cbar.set_title('Z-score')
g.ax_heatmap.set_xlabel('') # remove possible xlabel
g.ax_heatmap.set_title('clustermap title', weight="bold", fontsize=16) # Set a custom title
# extract the order of the y tick labels of the clustermap (before removing the ticks)
new_index = [t.get_text() for t in g.ax_heatmap.get_yticklabels()]
# remove right ticks and tick labels of the clustermap
g.ax_heatmap.tick_params(axis='y', right=False, labelright=False)
g.ax_heatmap.tick_params(axis='x', labelbottom=True, bottom=False)
# get position of heatmap
heatmap_bbox = g.ax_heatmap.get_position()
# make space for the right heatmap by reducing the size of the clustermap's heatmap
g.ax_heatmap.set_position([heatmap_bbox.x0, heatmap_bbox.y0, 0.49 - heatmap_bbox.x0, heatmap_bbox.height])
ax2 = plt.axes([0.50, heatmap_bbox.y0, 0.38, heatmap_bbox.height])
cbar_2_ax = plt.axes([0.94, 0.04, 0.02, 0.1])
# plot heatmap in the new axes, reordering the rows similar as in the clustermap
sns.heatmap(df2.reindex(new_index), cmap='viridis', cbar=True, yticklabels=True, ax=ax2, cbar_ax=cbar_2_ax)
ax2.tick_params(axis='y', labelsize=8, labelright=True, left=False, labelleft=False, labelrotation=0)
ax2.tick_params(axis='x', labelbottom=True, bottom=False)
ax2.set_title('heatmap title', weight="bold", fontsize=16) # Set a custom title
cbar_2_ax.set_title('Z-score')
# title = "Clustermap (left) & heatmap (right)"
# plt.suptitle(title, weight="bold", fontsize=20)
plt.show()