I would like to create a plot that consists of three subplots, where the upper left plot has the same width as the lower left plot but 1/3 of the height. Besides, I'd also like to plot the legend in the upper right area from the lower left plot. Is this even possible?
fig, ax = plt.subplots(2, figsize = (16,9))
ax1 = plt.subplot2grid((2,3), (1,0), colspan=2)
ax2 = plt.subplot2grid((2,3), (1,2), colspan=1)
ax3 = plt.subplot2grid((2,3), (0,0), colspan=2)
fig.suptitle('Title')
fig.tight_layout()
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
x = np.linspace(0, 2*np.pi)
y1 = np.cos(x)
y2 = np.sin(x)
fig = plt.figure()
gs = GridSpec(2, 2, width_ratios=[2, 1], height_ratios=[1, 3])
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1])
ax3 = fig.add_subplot(gs[2])
ax4 = fig.add_subplot(gs[3])
ax3.plot(x, y1, label="cos")
ax3.plot(x, y2, label="sin")
handles, labels = ax3.get_legend_handles_labels()
# hide axis on the top left subplot
ax2.axis("off")
# adding two legends
legend1 = ax2.legend([handles[0]], [labels[0]], loc="upper left")
legend2 = ax2.legend([handles[1]], [labels[1]], loc="lower right")
ax2.add_artist(legend1)
plt.tight_layout()
For some reason I couldn't find information on this (I'm pretty sure it exists somewhere), but in the following generic example, I would like to reduce the hspace between ax1 and ax2 while keeping the same hspace between ax2-ax3 and ax3-ax4.
I'd also appreciate any links to an example like that!
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
def annotate_axes(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
ax.tick_params(labelbottom=False, labelleft=False)
fig = plt.figure()
gs1 = GridSpec(6, 1, hspace=0.2)
ax1 = fig.add_subplot(gs1[0])
ax2 = fig.add_subplot(gs1[1])
ax3 = fig.add_subplot(gs1[2:4])
ax4 = fig.add_subplot(gs1[4:6])
annotate_axes(fig)
plt.show()
One way that might suit your need is to create a subgrid (in this example, putting hspace to 0):
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
def annotate_axes(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
ax.tick_params(labelbottom=False, labelleft=False)
fig = plt.figure()
gs1 = GridSpec(6, 1, hspace=0.2)
# subgrid for the first two slots
# in this example with no space
subg = gs1[0:2].subgridspec(2, 1, hspace = 0)
# note the ax1 and ax2 being created from the subgrid
ax1 = fig.add_subplot(subg[0])
ax2 = fig.add_subplot(subg[1])
ax3 = fig.add_subplot(gs1[2:4])
ax4 = fig.add_subplot(gs1[4:6])
annotate_axes(fig)
plt.show()
I am plotting 4 subplots in one figure, and I want to adjust the space in-between evenly.
I tried grid.GridSpec.update.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure(figsize=(8,8))
gs2 = gridspec.GridSpec(2, 2)
gs2.update(wspace=0.01, hspace=0.01)
ax1 = plt.subplot(gs2[0,0],aspect='equal')
ax1.imshow(img)
ax1.axis('off')
ax2 = plt.subplot(gs2[0,1],aspect='equal')
ax2.imshow(img)
ax2.axis('off')
ax3 = plt.subplot(gs2[1,0],aspect='equal')
ax3.imshow(img)
ax3.axis('off')
ax4 = plt.subplot(gs2[1,1],aspect='equal')
ax4.imshow(img)
ax4.axis('off')
The vertical space in-between 2 plots is too big, and it does not change no matter how I adjust gs2.update(hspace= ), as shown below:
It's likely your aspect='equal' that's causing the problem.
Try this
import numpy as np
%matplotlib inline # if in a jupyter notebook like environment
img = np.ones((30, 30))
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(8,8),
gridspec_kw={'wspace': 0.01, 'hspace': 0.01})
axes = axes.ravel()
for ax in axes:
# aspect : ['auto' | 'equal' | scalar], optional, default: None
ax.imshow(img, aspect='auto')
ax.axis('off')
I want to know if it is possible to synchronize xticks with xticklabels, in this way:
x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
XLabels = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U']
Now on screen 0 5 10 15 20, but on second subplot, there is A B C D E.
I want to see: A F K P U.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter, MultipleLocator # format X scale
import numpy as np
x=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot((np.arange(min(x), max(x)+1, 1.0)), 'b-')
ax2 = fig.add_subplot(2,1,2) # , sharex=ax1)
ax2.plot((np.arange(min(x), max(x)+1, 1.0)), 'r-')
#plt.setp(ax1.get_xticklabels(), visible=False)
ax2.xaxis.set_major_formatter(FormatStrFormatter('%0d'))
ax2.set_xticklabels(['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'], fontsize=12)
plt.show()
How can synchronize, so that I have A F K P U corresponding with 0 5 10 15 20 ?
The easiest option is to set the ticks and ticklabels manually. E.g. if you want to tick every Nth integer, you can choose a subset of the lists ([::N]) to set the ticks and labels to.
import matplotlib.pyplot as plt
import numpy as np
x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
XLabels = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q',
'R','S','T','U']
N = 5
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot((np.arange(min(x), max(x)+1, 1.0)), 'b-')
ax1.set_xticks(x[::N])
ax2 = fig.add_subplot(2,1,2) # , sharex=ax1)
ax2.plot((np.arange(min(x), max(x)+1, 1.0)), 'r-')
ax2.set_xticks(x[::N])
ax2.set_xticklabels(XLabels[::N], fontsize=12)
plt.show()
In order to kind of automate this, you can use a FuncFormatter, which selects the correct letter from the XLabels list according to the tick's location.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FuncFormatter, MultipleLocator
import numpy as np
x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
XLabels = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q',
'R','S','T','U']
N = 5
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot((np.arange(min(x), max(x)+1, 1.0)), 'b-')
ax1.xaxis.set_major_locator(MultipleLocator(N))
ax2 = fig.add_subplot(2,1,2) # , sharex=ax1)
ax2.plot((np.arange(min(x), max(x)+1, 1.0)), 'r-')
def f(c,pos):
if int(c) in x:
d = dict(zip(x,XLabels))
return d[int(c)]
else:
return ""
ax2.xaxis.set_major_locator(MultipleLocator(N))
ax2.xaxis.set_major_formatter(FuncFormatter(f))
plt.show()
You can filter the list of labels to get the ones matching the x axis, and use set_xticks and set_xticklabels
A simplified example:
import matplotlib.pyplot as plt
x = [0, 5, 10, 15, 20]
labels = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U']
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x, x, 'b-')
ax2 = fig.add_subplot(2,1,2)
ax2.plot(x, x, 'r-')
ax2.set_xticks(x)
ax2.set_xticklabels([label for index, label in enumerate(labels) if index in x])
plt.show()
I would like to set the colorbar of my plot to a custom height, not necessarily to match the size of the plot. In fact I would like the height of the colorbar PLUS the title on top of it to match the height of the figure.
With
ax3 = divider.append_axes('right', size='10%', pad=0.3)
cb = plt.colorbar(Q, cax=ax3, ticks=[0.0, 3.0, 6.0, 9.0, 12.0, 15.0], format='%.1f')
I managed to have a colorbar with the same height as the plot, which has been asked for many other times, now I would like to shrink it.
Following suggestion provided in other questions I decided to explicitly give the colorbar its own axes with add_axes, after getting the position of the last plot axes with get_position. Here is what I'm trying to do. There are no data and no colorbar in this example, just to show that I'm not getting the result I expected:
from __future__ import unicode_literals
import numpy as np
from scipy.interpolate import griddata
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib.pylab import cm
import matplotlib.colors as colors
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
matplotlib.rcParams.update({'font.size': 8})
fig = plt.figure()
fig.set_size_inches(6.3,6.3)
ax1 = plt.subplot(111)
divider = make_axes_locatable(ax1)
ax2 = divider.append_axes('right', size='100%', pad=0.3)
axes = [ax1, ax2]
ltypes = ['dashed', 'solid']
xi = np.linspace(-18.125, 18.125, 11)
yi = np.linspace(0, 28, 9)
xv, yv = np.meshgrid(xi, yi)
xcOdd = 0.2
zcOdd = 0.725
xcEven = 0.6
zcEven = 0.725
maskRadius = 0.15
for i in range(2):
ax = axes[i]
ax.set_xlabel('distance [m]')
if i == 0:
ax.set_ylabel('depth [m]')
if i == 1:
ax.set_yticklabels([])
ax.invert_yaxis()
ax.tick_params(direction='in')
ax.set_aspect('equal')
odd = Circle((xcOdd, zcOdd), .15, linewidth=1.2, color='k', fill=False)
even = Circle((xcEven, zcEven), .15, linewidth=1.2, linestyle=ltypes[i], color='k', fill=False)
vmax = 15.
vmin = 0.
norm = matplotlib.colors.Normalize(vmin,vmax, clip=False)
color_map = matplotlib.colors.ListedColormap(plt.cm.Greys(np.linspace(0.25, 1, 5)), "name")
ax.add_patch(odd)
pad = 0.03
width = 0.03
pos = ax2.get_position()
ax3 = fig.add_axes([pos.xmax + pad, pos.ymin, width, 0.7*(pos.ymax-pos.ymin) ])
plt.savefig('prova-vect-paper-test-2.eps', format='eps')
Why is get_position returning the wrong boundingbox?
You need to draw the canvas before obtaining the actual position from .get_position(). This is because due to the equal aspect ratio, the axes changes size and position at draw time.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from mpl_toolkits.axes_grid1 import make_axes_locatable
matplotlib.rcParams.update({'font.size': 8})
fig = plt.figure()
fig.set_size_inches(6.3,6.3)
ax1 = plt.subplot(111)
divider = make_axes_locatable(ax1)
ax2 = divider.append_axes('right', size='100%', pad=0.3)
axes = [ax1, ax2]
xi = np.linspace(-18.125, 18.125, 11)
yi = np.linspace(0, 28, 9)
xv, yv = np.meshgrid(xi, yi)
for i in range(2):
ax = axes[i]
ax.set_xlabel('distance [m]')
if i == 0:
ax.set_ylabel('depth [m]')
if i == 1:
ax.set_yticklabels([])
ax.invert_yaxis()
ax.tick_params(direction='in')
ax.set_aspect('equal')
vmax = 15.
vmin = 0.
norm = colors.Normalize(vmin,vmax, clip=False)
color_map = colors.ListedColormap(plt.cm.Greys(np.linspace(0.25, 1, 5)), "name")
im = ax.imshow(yv, cmap=color_map, norm=norm)
pad = 0.03
width = 0.03
fig.canvas.draw()
pos = ax2.get_position()
ax3 = fig.add_axes([pos.xmax + pad, pos.ymin, width, 0.7*(pos.ymax-pos.ymin) ])
fig.colorbar(im, cax=ax3)
plt.show()