3
votes

I want to plot an image and colorbar with its associated histogram below. The two axes of the image and the histogram must have the same width. Furthermore, the colorbar should be the same height as the image. The part that is (and should not) be complicated is to superpose a plot of a cumulative histogram with the percentage of each bin in respect to the size of the data.

For the moment, I obtained something like this:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable


data = np.random.normal(0,2,size=(100,100))

fig     = plt.figure()
ax      = fig.add_subplot(2,1,1)
im      = ax.imshow(data,cmap="bone")
divider = make_axes_locatable(ax)
ax1     = divider.append_axes("right", size="5%", pad=0.05) 
plt.colorbar(im,cax=ax1)   
ax2     = divider.append_axes("bottom",size="100%",pad = 0.3)
n,bins,patches = ax2.hist(data.flatten(),bins=20)
ax3     = ax2.twinx()
ax3.plot(bins[:-1],np.cumsum(n*100/np.size(data)),lw=2)

plt.show()

Everything is going smoothly until I try to use twinx on ax2 (in order to plot my cumulative distribution on ax3 with a different y-scale). The resulting axis, instead of being with ax2, is wrapping all the axes of the figure.

I don't understand what is wrong and how I can fix this.

1

1 Answers

6
votes

This is a hard one. The problem is that the axes_grid1 toolkit is designed to position the axes at the time of drawing. Apparently it draws the twin axis first and only after that relocates the axes according to the divider.
What makes things worse is that you want to have an axes with equal aspect ratio bound to an axes with unequal aspect, which makes it impossible to use AxisGrid.

While any two-fold combination of equal+unequal or equal+twin or unequal+twin would work in one way or the other, all three are just too much.

So the solution is probably to start from scratch, just putting the axes to the canvas and only at the very end reposition/resize them. This can be done using an event listener connected to a function which gets the position of the axes with equal aspect and resizes the other two axes accordingly.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

data = np.random.normal(0,2,size=(100,100))

fig     = plt.figure()
ax  = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

im      = ax.imshow(data,cmap="bone")
n,bins,patches = ax2.hist(data.flatten(),bins=20)

divider = make_axes_locatable(ax)
cax     = divider.append_axes("right", size="5%", pad=0.05) 
plt.colorbar(im, cax=cax)

ax3     = ax2.twinx()
ax3.plot(bins[:-1],np.cumsum(n*100/np.size(data)),lw=2, c=plt.cm.bone(0.4))

def resize(event):
    axpos = ax.get_position()
    axpos2 = ax2.get_position()
    newpos = [axpos.x0, axpos2.y0,  axpos.width, axpos2.height] 
    ax2.set_position(newpos)
    ax3.set_position(newpos)

cid = fig.canvas.mpl_connect('draw_event', resize)
cid2 = fig.canvas.mpl_connect('resize_event', resize)

#if you want to save the figure, trigger the event manually
save=False
if save:
    fig.canvas.draw()
    resize()
    plt.savefig(__file__+".png")
plt.show()

enter image description here