Sharing x axis and y axis at the same time for images

51 Views Asked by At

I have an image that I am displaying with imshow. Then I add all the rows and show maximum values. I do the same for the columns. On the display plot, I would like to make the x and y axis of the image coincide with the x axis of the addition of columns and the y axis with the adition of rows. However, despite setting sharex and sharey respectively, it just doesnt seem to work. I would I just seem to be able to do one at a time:

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import argrelextrema
import matplotlib.animation as animation

fig=        plt.figure()
gs=         fig.add_gridspec(2,2, height_ratios=[1, 0.1], width_ratios=[1, 0.1], hspace=0, wspace=0)
ax1=        fig.add_subplot(gs[0,0])
ax2=        fig.add_subplot(gs[1,0], sharex=ax1)
ax3=        fig.add_subplot(gs[0,1], sharey=ax1)
frameNumber= 10
imgs=   []

for i in range(frameNumber):
    np.random.seed(i)
    randomImage= np.random.random((5,5))
    sumX=   np.sum(randomImage, axis=0)
    sumY=   np.sum(randomImage, axis=1)
    dataRange=  np.arange(len(sumX))
    randomDataSet=  np.random.random((10))
    randomMaximalX= argrelextrema(sumX, np.greater)
    randomMaximalY= argrelextrema(sumY, np.greater)

    img1=   ax1.imshow(randomImage, animated=True)
    img2=   ax2.plot(dataRange, sumX,animated=True)[0]
    img3=   ax3.plot(sumY,dataRange,animated=True)[0]
    img4=   ax2.vlines(x=randomMaximalX, ymin=0, ymax=5, animated=True, linestyles="dashed")
    img5=   ax3.hlines(y=randomMaximalY, xmin=0, xmax=5, animated=True, linestyles="dashed")
    imgs.append([img1, img2, img3, img4, img5])
ani=    animation.ArtistAnimation(fig, imgs, interval=1000, blit=False)
plt.show()

The current result is this:

enter image description here

And indeally I want something like this:

enter image description here

where H is the same value for both plots. Many thanks!

1

There are 1 best solutions below

0
Cameron Riddell On BEST ANSWER

There are two ways you can approach this problem:

  1. Use Axes.pcolormesh instead of Axes.imshow
  2. OR update the aspect ratios of the adjacent plots.

① Axes.pcolormesh

Axes.pcolormesh does not force the resultant image to be square (1:1 aspect ratio), so your cells will be rectangular but they'll appropriately fill the space provided.

from numpy.random import default_rng
import matplotlib.pyplot as plt

rng = default_rng(0)
image = rng.uniform(1, 10, size=(5, 5))

mosaic = [
    ['main',   'right'],
    ['bottom', '.'    ],
]

fig, axd = plt.subplot_mosaic(
    mosaic,
    gridspec_kw={
        'height_ratios': [1, .1], 'width_ratios': [1, .1],
        'wspace': .05, 'hspace': .05,
    },
    sharex=True,
    sharey=True,
)

axd['main'].pcolormesh(image)

plt.show()

enter image description here

② Aspect Updating

If you want to stick with Axes.imshow, then you'll need to adjust the aspect ratios of each plot manually. To get the correct ratios, you'll need to calculate from the supplied height_ratio and width_ratio fed into the GridSpec

from numpy.random import default_rng
import matplotlib.pyplot as plt

rng = default_rng(0)
image = rng.uniform(1, 10, size=(5, 5))

mosaic = [
    ['main',   'right'],
    ['bottom', '.'    ],
]

fig, axd = plt.subplot_mosaic(
    mosaic,
    sharex=True,
    sharey=True,
    gridspec_kw={
        'height_ratios': [1, .1], 'width_ratios': [1, .1],

        # change values to move adjacent plots closer to the main
        'wspace': .05, 'hspace': .05,
    },
)

axd['main'].imshow(image)
axd['main'].set_anchor('SE') # move main plot to bottom-right of bounding-box

# calculate the width and height scales
gs = axd['main'].get_gridspec() # you can also save these values from your `gridspec_kw`
width_scale = gs.get_width_ratios()[0]    / gs.get_width_ratios()[1]
height_scale = gs.get_height_ratios()[0]  / gs.get_height_ratios()[1]

# update the aspect ratios of the adjacent plots
#   set their anchors so they correctly align with the main plot
axd['right'].set_aspect(width_scale, anchor='SW')
axd['bottom'].set_aspect(1/height_scale, anchor='NE')

plt.show()

enter image description here