3

I can create and n by n heatmap using the following code, for example let n be 10:

random_matrix = np.random.rand(10,10)
number = 10
incrmnt = 1.0
x = list(range(1,number +1))
plt.pcolormesh(x, x, random_matrix)
plt.colorbar() 
plt.xlim(1, number)
plt.xlabel('Number 1')
plt.ylim(1, number)
plt.ylabel('Number 2')
plt.tick_params(
    axis = 'both',
    which = 'both',
    bottom = 'off',
    top = 'off', 
    labelbottom = 'off', 
    right = 'off',
    left = 'off',
    labelleft = 'off')

I would like to add a 2 row heatmap one near each of the x and y axis, from say row1 = np.random.rand(1,10)and col1 = np.random.rand(1,10). Here is an example image of what I would like to produce:

enter image description here

Thanks in advance.

1
  • 1
    There is also a very similar question asking how to "cut" the heatmap at different locations, in case someone is interested. Commented Mar 28, 2017 at 21:48

1 Answer 1

1

You would create a subplot grid where the width- and height ratios between the subplots correspond to the number of pixels in the respective dimension. You can then add respective plots to those subplots. In the code below I used an imshow plot, because I find it more intuitive to have one pixel per item in the array (instead of one less).

In order to have the colorbar represent the colors accross the different subplots, one can use a matplotlib.colors.Normalize instance, which is provided to each of the subplots, as well as the manually created ScalarMappable for the colorbar.

enter image description here

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

m = np.random.rand(10,10)
x = np.random.rand(1,m.shape[1])
y = np.random.rand(m.shape[0],1)

norm = matplotlib.colors.Normalize(vmin=0, vmax=1)
grid = dict(height_ratios=[1, m.shape[0]], width_ratios=[1,m.shape[0], 0.5 ])
fig, axes = plt.subplots(ncols=3, nrows=2, gridspec_kw = grid)

axes[1,1].imshow(m, aspect="auto", cmap="viridis", norm=norm)
axes[0,1].imshow(x, aspect="auto", cmap="viridis", norm=norm)
axes[1,0].imshow(y, aspect="auto", cmap="viridis", norm=norm)

axes[0,0].axis("off")
axes[0,2].axis("off")

axes[1,1].set_xlabel('Number 1')
axes[1,1].set_ylabel('Number 2')
for ax in [axes[1,1], axes[0,1], axes[1,0]]:
    ax.set_xticks([]); ax.set_yticks([])

sm = matplotlib.cm.ScalarMappable(cmap="viridis", norm=norm)
sm.set_array([])

fig.colorbar(sm, cax=axes[1,2]) 

plt.show()
Sign up to request clarification or add additional context in comments.

2 Comments

Works great, thank you. Although when I use the above code on my data aspect = "auto" turns each point into a rectangle rather than a square. When I try and specify aspect = 1, the plot are too far apart. Any suggestions?
Well, you'd need to adapt the figure size and spacings such that the aspect of the image is preserved. In the other question I did some rough adaption. A perfect solution would involve something similar as in this question. But it may also be sufficient to simply set the figure size close to the array's aspect, e.g. if you have a 5 x 20 array, set the figure height to one quarter of the width.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.