2

I have a heatmap that looks like this (from: Plotting a 2D heatmap with Matplotlib). enter image description here

I am trying to create a heatmap that don't have the ticklabels for each value, but group by ranges. For example, the first three tick marks have not been signed but have been signed with one joint signed 'Apples'.

It seems easy to disable the ticklabels at all with the use of:

plt.tick_params(
    axis='x',
    which='both',
    bottom=False,
    top=False,
    labelbottom=False)

or to choose only selected ticks:

for (i,l) in enumerate(ax.xaxis.get_ticklabels()):
    if i == 0 or i == 4 or i == 5:
        l.set_visible(True)
    else:
        l.set_visible(False) 

but could it be done as below?

enter image description here

An example code for a heatmap:

corr = np.corrcoef(np.random.randn(10, 200))
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
    ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True,  cmap="YlGnBu")
    plt.show()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
chrisd231
  • 37
  • 4

1 Answers1

1

You can use the xaxis transform to place texts and draw short lines, using data coordinates for the x-position, and axes coordinates for the y-position (0 at the bottom, 1 at the top). By default, texts aren't clipped by the axes, but other elements are, so they need clip_on=False to be visible outside the main plot area.

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

corr = np.corrcoef(np.random.randn(10, 20))
corr[np.triu_indices_from(corr)] = np.nan
sns.set_style("white")
ax = sns.heatmap(corr, vmax=.3, square=True, cmap="YlGnBu",
                 annot=True, fmt='.2f', cbar_kws={'pad': 0})
labels = ['Apples', 'Oranges', 'Pears', 'Bananas']
label_lens = [4, 2, 1, 3]

ax.set_xticks([]) # remove the x ticks
ax.set_yticks([]) # remove the y ticks
pos = 0
for label, label_len in zip(labels, label_lens):
    if pos != 0:
        ax.vlines(pos, pos, len(corr), color='r', lw=2)
        ax.vlines(pos, 0, -0.02, color='r', lw=2,
                  transform=ax.get_xaxis_transform(), clip_on=False)
        ax.hlines(pos, 0, pos, color='r', lw=2)
        ax.hlines(pos, 0, -0.02, color='r', lw=2,
                  transform=ax.get_yaxis_transform(), clip_on=False)
    ax.text(pos + label_len / 2, -0.02, label, ha='center', va='top',
            transform=ax.get_xaxis_transform())
    ax.text(-0.02, pos + label_len / 2, label, ha='right', va='center', rotation=90,
            transform=ax.get_yaxis_transform())
    pos += label_len
plt.tight_layout()
plt.show()

sns.heatmap with grouped ticks

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • 1
    @Mr.T Indeed. It is just a copy of OP's example. Maybe to avoid that too many values close to zero get the same color. It strongly depends on the dataset and the message one want to convey. – JohanC Jan 05 '22 at 16:38
  • I was just puzzled why the colorbar ended at 0.3 although values higher than that exist. – Mr. T Jan 05 '22 at 16:40