Labels on Gridspec [duplicate] - matplotlib

I'm facing a problem in showing the legend in the correct format using matplotlib.
EDIT: I have 4 subplots in a figure in 2 by 2 format and I want legend only on the first subplot which has two lines plotted on it. The legend that I got using the code attached below contained endless entries and extended vertically throughout the figure. When I use the same code using linspace to generate fake data the legend works absolutely fine.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import os
#------------------set default directory, import data and create column output vectors---------------------------#
path="C:/Users/Pacman/Data files"
os.chdir(path)
data =np.genfromtxt('vrp.txt')
x=np.array([data[:,][:,0]])
y1=np.array([data[:,][:,6]])
y2=np.array([data[:,][:,7]])
y3=np.array([data[:,][:,9]])
y4=np.array([data[:,][:,11]])
y5=np.array([data[:,][:,10]])
nrows=2
ncols=2
tick_l=6 #length of ticks
fs_axis=16 #font size of axis labels
plt.rcParams['axes.linewidth'] = 2 #Sets global line width of all the axis
plt.rcParams['xtick.labelsize']=14 #Sets global font size for x-axis labels
plt.rcParams['ytick.labelsize']=14 #Sets global font size for y-axis labels
plt.subplot(nrows, ncols, 1)
ax=plt.subplot(nrows, ncols, 1)
l1=plt.plot(x, y2, 'yo',label='Flow rate-fan')
l2=plt.plot(x,y3,'ro',label='Flow rate-discharge')
plt.title('(a)')
plt.ylabel('Flow rate ($m^3 s^{-1}$)',fontsize=fs_axis)
plt.xlabel('Rupture Position (ft)',fontsize=fs_axis)
# This part is not working
plt.legend(loc='upper right', fontsize='x-large')
#Same code for rest of the subplots
I tried to implement a fix suggested in the following link, however, could not make it work:
how do I make a single legend for many subplots with matplotlib?
Any help in this regard will be highly appreciated.

If I understand correctly, you need to tell plt.legend what to put as legends... at this point it is being loaded empty. What you get must be from another source. I have quickly the following, and of course when I run fig.legend as you do I get nothing.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.4, 0.7])
ax2 = fig.add_axes([0.55, 0.1, 0.4, 0.7])
x = np.arange(0.0, 2.0, 0.02)
y1 = np.sin(2*np.pi*x)
y2 = np.exp(-x)
l1, l2 = ax1.plot(x, y1, 'rs-', x, y2, 'go')
y3 = np.sin(4*np.pi*x)
y4 = np.exp(-2*x)
l3, l4 = ax2.plot(x, y3, 'yd-', x, y4, 'k^')
fig.legend(loc='upper right', fontsize='x-large')
#fig.legend((l1, l2), ('Line 1', 'Line 2'), 'upper left')
#fig.legend((l3, l4), ('Line 3', 'Line 4'), 'upper right')
plt.show()
I'd suggest doing one by one, and then applying for all.

It is useful to work with the axes directly (ax in your case) when when working with subplots. So if you set up two plots in a figure and only wish to have a legend in your second plot:
t = np.linspace(0, 10, 100)
plt.figure()
ax1 = plt.subplot(2, 1, 1)
ax1.plot(t, t * t)
ax2 = plt.subplot(2, 1, 2)
ax2.plot(t, t * t * t)
ax2.legend('Cubic Function')
Note that when creating the legend, I am doing so on ax2 as opposed to plt. If you wish to create a second legend for the first subplot, you can do so in the same way but on ax1.

Related

Set one colorbar for two images/subplots, and another colorbar for third image in 3 panel figure

This MWE from the matplotlib doc is a useful reference.
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
plt.subplot(311)
plt.imshow(np.random.random((100, 100)))
plt.subplot(312)
plt.imshow(np.random.random((100, 100)))
plt.subplot(313)
plt.imshow(np.random.random((100, 100)))
plt.subplots_adjust(bottom=0.1, right=0.8, top=.9)
cax = plt.axes([0.85, 0.1, 0.075, 0.8])
plt.colorbar(cax=cax)
plt.show()
This produces:
My two main questions are:
How do I get the first two plots to share a colorbar and the third to have its own?
I don't really understand what 'cax' is doing or why the values are what they are.
As the question just says - two plots share a colorbar, you can either have the first two in the first row with a common colorbar, while the third will have another one, or you could do all 3 in separate columns with the first two sharing a colorbar.
Code for first option
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, ax = plt.subplots()
plt.subplot(221) ## 2x2 plot, 1st item or in position 1,1
plt.imshow(np.random.random((100, 100)))
ax2 = plt.subplot(222)
im2 = ax2.imshow(np.random.random((100, 100)))
plt.colorbar(im2, ax=ax2)
ax3 = plt.subplot(223)
im3 = ax3.imshow(np.random.random((100, 100)))
plt.colorbar(im3, ax=ax3)
plt.show()
Plot
Option 2 code
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, ax = plt.subplots(figsize=(5,6))
ax1 = plt.subplot(311) ## 3 rows and 1 column, position 1,1 =1
im = ax1.imshow(np.random.random((100, 100)))
ax2 = plt.subplot(312)
im = ax2.imshow(np.random.random((100, 100)))
ax3 = plt.subplot(313)
im3 = ax3.imshow(np.random.random((100, 100)))
plt.colorbar(im3, ax=ax3)
plt.colorbar(im, ax=[ax1, ax2], aspect = 40) ##Common colobar for ax1 and ax2; aspect used to set colorbar thickness/width
plt.show()
Plot
Although I have not used colorbar axis, it is the axis into which the colorbar is drawn, similar to what we have above in ax1, ax2, ax3 above. The numbers are used to specify where the colorbar should be located. Look at the last example here to see how the position is set. Hope this helps

How to have only 1 shared colorbar for multiple plots [duplicate]

I've spent entirely too long researching how to get two subplots to share the same y-axis with a single colorbar shared between the two in Matplotlib.
What was happening was that when I called the colorbar() function in either subplot1 or subplot2, it would autoscale the plot such that the colorbar plus the plot would fit inside the 'subplot' bounding box, causing the two side-by-side plots to be two very different sizes.
To get around this, I tried to create a third subplot which I then hacked to render no plot with just a colorbar present.
The only problem is, now the heights and widths of the two plots are uneven, and I can't figure out how to make it look okay.
Here is my code:
from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches
from matplotlib.ticker import NullFormatter
# SIS Functions
TE = 1 # Einstein radius
g1 = lambda x,y: (TE/2) * (y**2-x**2)/((x**2+y**2)**(3/2))
g2 = lambda x,y: -1*TE*x*y / ((x**2+y**2)**(3/2))
kappa = lambda x,y: TE / (2*np.sqrt(x**2+y**2))
coords = np.linspace(-2,2,400)
X,Y = np.meshgrid(coords,coords)
g1out = g1(X,Y)
g2out = g2(X,Y)
kappaout = kappa(X,Y)
for i in range(len(coords)):
for j in range(len(coords)):
if np.sqrt(coords[i]**2+coords[j]**2) <= TE:
g1out[i][j]=0
g2out[i][j]=0
fig = plt.figure()
fig.subplots_adjust(wspace=0,hspace=0)
# subplot number 1
ax1 = fig.add_subplot(1,2,1,aspect='equal',xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{1}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
plt.ylabel(r"y ($\theta_{E}$)",rotation='horizontal',fontsize="15")
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.imshow(g1out,extent=(-2,2,-2,2))
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
e1 = patches.Ellipse((0,0),2,2,color='white')
ax1.add_patch(e1)
# subplot number 2
ax2 = fig.add_subplot(1,2,2,sharey=ax1,xlim=[-2,2],ylim=[-2,2])
plt.title(r"$\gamma_{2}$",fontsize="18")
plt.xlabel(r"x ($\theta_{E}$)",fontsize="15")
ax2.yaxis.set_major_formatter( NullFormatter() )
plt.axhline(y=0,linewidth=2,color='k',linestyle="--")
plt.axvline(x=0,linewidth=2,color='k',linestyle="--")
plt.imshow(g2out,extent=(-2,2,-2,2))
e2 = patches.Ellipse((0,0),2,2,color='white')
ax2.add_patch(e2)
# subplot for colorbar
ax3 = fig.add_subplot(1,1,1)
ax3.axis('off')
cbar = plt.colorbar(ax=ax2)
plt.show()
Just place the colorbar in its own axis and use subplots_adjust to make room for it.
As a quick example:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)
plt.show()
Note that the color range will be set by the last image plotted (that gave rise to im) even if the range of values is set by vmin and vmax. If another plot has, for example, a higher max value, points with higher values than the max of im will show in uniform color.
You can simplify Joe Kington's code using the axparameter of figure.colorbar() with a list of axes.
From the documentation:
ax
None | parent axes object(s) from which space for a new colorbar axes will be stolen. If a list of axes is given they will all be resized to make room for the colorbar axes.
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()
This solution does not require manual tweaking of axes locations or colorbar size, works with multi-row and single-row layouts, and works with tight_layout(). It is adapted from a gallery example, using ImageGrid from matplotlib's AxesGrid Toolbox.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
# Set up figure and image grid
fig = plt.figure(figsize=(9.75, 3))
grid = ImageGrid(fig, 111, # as in plt.subplot(111)
nrows_ncols=(1,3),
axes_pad=0.15,
share_all=True,
cbar_location="right",
cbar_mode="single",
cbar_size="7%",
cbar_pad=0.15,
)
# Add data to image grid
for ax in grid:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
# Colorbar
ax.cax.colorbar(im)
ax.cax.toggle_label(True)
#plt.tight_layout() # Works, but may still require rect paramater to keep colorbar labels visible
plt.show()
Using make_axes is even easier and gives a better result. It also provides possibilities to customise the positioning of the colorbar.
Also note the option of subplots to share x and y axes.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
cax,kw = mpl.colorbar.make_axes([ax for ax in axes.flat])
plt.colorbar(im, cax=cax, **kw)
plt.show()
As a beginner who stumbled across this thread, I'd like to add a python-for-dummies adaptation of abevieiramota's very neat answer (because I'm at the level that I had to look up 'ravel' to work out what their code was doing):
import numpy as np
import matplotlib.pyplot as plt
fig, ((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(2,3)
axlist = [ax1,ax2,ax3,ax4,ax5,ax6]
first = ax1.imshow(np.random.random((10,10)), vmin=0, vmax=1)
third = ax3.imshow(np.random.random((12,12)), vmin=0, vmax=1)
fig.colorbar(first, ax=axlist)
plt.show()
Much less pythonic, much easier for noobs like me to see what's actually happening here.
Shared colormap and colorbar
This is for the more complex case where the values are not just between 0 and 1; the cmap needs to be shared instead of just using the last one.
import numpy as np
from matplotlib.colors import Normalize
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig, axes = plt.subplots(nrows=2, ncols=2)
cmap=cm.get_cmap('viridis')
normalizer=Normalize(0,4)
im=cm.ScalarMappable(norm=normalizer)
for i,ax in enumerate(axes.flat):
ax.imshow(i+np.random.random((10,10)),cmap=cmap,norm=normalizer)
ax.set_title(str(i))
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()
As pointed out in other answers, the idea is usually to define an axes for the colorbar to reside in. There are various ways of doing so; one that hasn't been mentionned yet would be to directly specify the colorbar axes at subplot creation with plt.subplots(). The advantage is that the axes position does not need to be manually set and in all cases with automatic aspect the colorbar will be exactly the same height as the subplots. Even in many cases where images are used the result will be satisfying as shown below.
When using plt.subplots(), the use of gridspec_kw argument allows to make the colorbar axes much smaller than the other axes.
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
Example:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im = ax.imshow(np.random.rand(11,8), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,8), vmin=0, vmax=1)
ax.set_ylabel("y label")
fig.colorbar(im, cax=cax)
plt.show()
This works well, if the plots' aspect is autoscaled or the images are shrunk due to their aspect in the width direction (as in the above). If, however, the images are wider then high, the result would look as follows, which might be undesired.
A solution to fix the colorbar height to the subplot height would be to use mpl_toolkits.axes_grid1.inset_locator.InsetPosition to set the colorbar axes relative to the image subplot axes.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(7,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im = ax.imshow(np.random.rand(11,16), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,16), vmin=0, vmax=1)
ax.set_ylabel("y label")
ip = InsetPosition(ax2, [1.05,0,0.05,1])
cax.set_axes_locator(ip)
fig.colorbar(im, cax=cax, ax=[ax,ax2])
plt.show()
New in matplotlib 3.4.0
Shared colorbars can now be implemented using subfigures:
New Figure.subfigures and Figure.add_subfigure allow ... localized figure artists (e.g., colorbars and suptitles) that only pertain to each subfigure.
The matplotlib gallery includes demos on how to plot subfigures.
Here is a minimal example with 2 subfigures, each with a shared colorbar:
fig = plt.figure(constrained_layout=True)
(subfig_l, subfig_r) = fig.subfigures(nrows=1, ncols=2)
axes_l = subfig_l.subplots(nrows=1, ncols=2, sharey=True)
for ax in axes_l:
im = ax.imshow(np.random.random((10, 10)), vmin=0, vmax=1)
# shared colorbar for left subfigure
subfig_l.colorbar(im, ax=axes_l, location='bottom')
axes_r = subfig_r.subplots(nrows=3, ncols=1, sharex=True)
for ax in axes_r:
mesh = ax.pcolormesh(np.random.randn(30, 30), vmin=-2.5, vmax=2.5)
# shared colorbar for right subfigure
subfig_r.colorbar(mesh, ax=axes_r)
The solution of using a list of axes by abevieiramota works very well until you use only one row of images, as pointed out in the comments. Using a reasonable aspect ratio for figsize helps, but is still far from perfect. For example:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(9.75, 3))
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()
The colorbar function provides the shrink parameter which is a scaling factor for the size of the colorbar axes. It does require some manual trial and error. For example:
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.75)
To add to #abevieiramota's excellent answer, you can get the euqivalent of tight_layout with constrained_layout. You will still get large horizontal gaps if you use imshow instead of pcolormesh because of the 1:1 aspect ratio imposed by imshow.
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=2, constrained_layout=True)
for ax in axes.flat:
im = ax.pcolormesh(np.random.random((10,10)), vmin=0, vmax=1)
fig.colorbar(im, ax=axes.flat)
plt.show()
I noticed that almost every solution posted involved ax.imshow(im, ...) and did not normalize the colors displayed to the colorbar for the multiple subfigures. The im mappable is taken from the last instance, but what if the values of the multiple im-s are different? (I'm assuming these mappables are treated in the same way that the contour-sets and surface-sets are treated.) I have an example using a 3d surface plot below that creates two colorbars for a 2x2 subplot (one colorbar per one row). Although the question asks explicitly for a different arrangement, I think the example helps clarify some things. I haven't found a way to do this using plt.subplots(...) yet because of the 3D axes unfortunately.
If only I could position the colorbars in a better way... (There is probably a much better way to do this, but at least it should be not too difficult to follow.)
import matplotlib
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
cmap = 'plasma'
ncontours = 5
def get_data(row, col):
""" get X, Y, Z, and plot number of subplot
Z > 0 for top row, Z < 0 for bottom row """
if row == 0:
x = np.linspace(1, 10, 10, dtype=int)
X, Y = np.meshgrid(x, x)
Z = np.sqrt(X**2 + Y**2)
if col == 0:
pnum = 1
else:
pnum = 2
elif row == 1:
x = np.linspace(1, 10, 10, dtype=int)
X, Y = np.meshgrid(x, x)
Z = -np.sqrt(X**2 + Y**2)
if col == 0:
pnum = 3
else:
pnum = 4
print("\nPNUM: {}, Zmin = {}, Zmax = {}\n".format(pnum, np.min(Z), np.max(Z)))
return X, Y, Z, pnum
fig = plt.figure()
nrows, ncols = 2, 2
zz = []
axes = []
for row in range(nrows):
for col in range(ncols):
X, Y, Z, pnum = get_data(row, col)
ax = fig.add_subplot(nrows, ncols, pnum, projection='3d')
ax.set_title('row = {}, col = {}'.format(row, col))
fhandle = ax.plot_surface(X, Y, Z, cmap=cmap)
zz.append(Z)
axes.append(ax)
## get full range of Z data as flat list for top and bottom rows
zz_top = zz[0].reshape(-1).tolist() + zz[1].reshape(-1).tolist()
zz_btm = zz[2].reshape(-1).tolist() + zz[3].reshape(-1).tolist()
## get top and bottom axes
ax_top = [axes[0], axes[1]]
ax_btm = [axes[2], axes[3]]
## normalize colors to minimum and maximum values of dataset
norm_top = matplotlib.colors.Normalize(vmin=min(zz_top), vmax=max(zz_top))
norm_btm = matplotlib.colors.Normalize(vmin=min(zz_btm), vmax=max(zz_btm))
cmap = cm.get_cmap(cmap, ncontours) # number of colors on colorbar
mtop = cm.ScalarMappable(cmap=cmap, norm=norm_top)
mbtm = cm.ScalarMappable(cmap=cmap, norm=norm_btm)
for m in (mtop, mbtm):
m.set_array([])
# ## create cax to draw colorbar in
# cax_top = fig.add_axes([0.9, 0.55, 0.05, 0.4])
# cax_btm = fig.add_axes([0.9, 0.05, 0.05, 0.4])
cbar_top = fig.colorbar(mtop, ax=ax_top, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_top)
cbar_top.set_ticks(np.linspace(min(zz_top), max(zz_top), ncontours))
cbar_btm = fig.colorbar(mbtm, ax=ax_btm, orientation='vertical', shrink=0.75, pad=0.2) #, cax=cax_btm)
cbar_btm.set_ticks(np.linspace(min(zz_btm), max(zz_btm), ncontours))
plt.show()
plt.close(fig)
## orientation of colorbar = 'horizontal' if done by column
This topic is well covered but I still would like to propose another approach in a slightly different philosophy.
It is a bit more complex to set-up but it allow (in my opinion) a bit more flexibility. For example, one can play with the respective ratios of each subplots / colorbar:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
# Define number of rows and columns you want in your figure
nrow = 2
ncol = 3
# Make a new figure
fig = plt.figure(constrained_layout=True)
# Design your figure properties
widths = [3,4,5,1]
gs = GridSpec(nrow, ncol + 1, figure=fig, width_ratios=widths)
# Fill your figure with desired plots
axes = []
for i in range(nrow):
for j in range(ncol):
axes.append(fig.add_subplot(gs[i, j]))
im = axes[-1].pcolormesh(np.random.random((10,10)))
# Shared colorbar
axes.append(fig.add_subplot(gs[:, ncol]))
fig.colorbar(im, cax=axes[-1])
plt.show()
The answers above are great, but most of them use the fig.colobar() method applied to a fig object. This example shows how to use the plt.colobar() function, applied directly to pyplot:
def shared_colorbar_example():
fig, axs = plt.subplots(nrows=3, ncols=3)
for ax in axs.flat:
plt.sca(ax)
color = np.random.random((10))
plt.scatter(range(10), range(10), c=color, cmap='viridis', vmin=0, vmax=1)
plt.colorbar(ax=axs.ravel().tolist(), shrink=0.6)
plt.show()
shared_colorbar_example()
Since most answers above demonstrated usage on 2D matrices, I went with a simple scatter plot. The shrink keyword is optional and resizes the colorbar.
If vmin and vmax are not specified this approach will automatically analyze all of the subplots for the minimum and maximum value to be used on the colorbar. The above approaches when using fig.colorbar(im) scan only the image passed as argument for min and max values of the colorbar.
Result:

How to use mode='expand' and center a figure-legend label given only one label entry?

I would like to generate a centered figure legend for subplot(s), for which there is a single label. For my actual use case, the number of subplot(s) is greater than or equal to one; it's possible to have a 2x2 grid of subplots and I would like to use the figure-legend instead of using ax.legend(...) since the same single label entry will apply to each/every subplot.
As a brief and simplified example, consider the code just below:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y, color='orange', label='$f(x) = sin(x)$')
fig.subplots_adjust(bottom=0.15)
fig.legend(mode='expand', loc='lower center')
plt.show()
plt.close(fig)
This code will generate the figure seen below:
I would like to use the mode='expand' kwarg to make the legend span the entire width of the subplot(s); however, doing so prevents the label from being centered. As an example, removing this kwarg from the code outputs the following figure.
Is there a way to use both mode='expand' and also have the label be centered (since there is only one label)?
EDIT:
I've tried using the bbox_to_anchor kwargs (as suggested in the docs) as an alternative to mode='expand', but this doesn't work either. One can switch out the fig.legend(...) line for the line below to test for yourself.
fig.legend(loc='lower center', bbox_to_anchor=(0, 0, 1, 0.5))
The handles and labels are flush against the left side of the legend. There is no mechanism to allow for aligning them.
A workaround could be to use 3 columns of legend handles and fill the first and third with a transparent handle.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = np.sin(x)
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.15)
line, = ax.plot(x, y, color='orange', label='$f(x) = sin(x)$')
proxy = plt.Rectangle((0,0),1,1, alpha=0)
fig.legend(handles=[proxy, line, proxy], mode='expand', loc='lower center', ncol=3)
plt.show()

how to add variable error bars to scatter plot points with shared axes in python matplotlib

I have generated a plot that shows a topographic profile with points along the profile that represent dated points. However, these dated points also have symmetric uncertainty values/error bars (that typically vary for each point).
In this example, I treat non-dated locations as 'np.nan'. I would like to add uncertainty values to the y2 axis (Mean Age) with defined uncertainty values as y2err.
Everytime I use the ax2.errorbar( ... ) line, my graph is squeezed and distorted.
import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
#Longitude = x; Elevation = y
x = (-110.75696,-110.75668,-110.75640,-110.75612,-110.75584,-110.75556,-110.75528)
y = (877,879,878,873,871,872,872)
ax1.plot(x, y)
ax1.set_xlabel('Longitude')
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_ylabel('Elevation', color='b')
ax1.tick_params('y', colors='b')
ax2 = ax1.twinx()
# Mean Age, np.nan = 0.0
y2 = (np.nan,20,np.nan,np.nan,np.nan,np.nan,np.nan)
y2err = (np.nan,5,np.nan,np.nan,np.nan,np.nan,np.nan)
ax2.scatter(x, y2, color='r')
#add error bars to scatter plot points
# (??????) ax2.errorbar(x, y, y2, y2err, capsize = 0, color='black')
ax2.set_ylim(10,30)
ax2.set_ylabel('Mean Age', color='r')
ax2.tick_params('y', colors='r')
fig.tight_layout()
plt.show()
If I do not apply the ax2.errorbar... line my plot looks like the first image, which is what I want but with the points showing uncertainty values (+/- equal on both side of point in the y-axis direction).
Plot of Elevation vs Age without error bars
When I use the ax2.errorbar line it looks like the second image:
Plot when using ax2.errorbar line
Thanks!

How can I have each plot in matplotlib's `subplots` use a different axes?

So when I try to graph multiple subplots using pyplot.subplots I get something like:
How can I have:
Multiple independent axes for every subplot
Axes for every subplot
Overlay plots in every subplot axes using subplots. I tried to do ((ax1,ax2),(ax3,ax4)) = subplots and then do ax1.plot twice, but as a result, neither of the two showed.
Code for the picture:
import string
import matplotlib
matplotlib.use('WX')
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import numpy as np
from itertools import izip,chain
f,((ax1,ax2),(ax3,ax4)) = plt.subplots(2,2,sharex='col',sharey='row')
ax1.plot(range(10),2*np.arange(10))
ax2.plot(range(10),range(10))
ax3.plot(range(5),np.arange(5)*1000)
#pyplot.yscale('log')
#ax2.set_autoscaley_on(False)
#ax2.set_ylim([0,10])
plt.show()
Questions 1 & 2:
To accomplish this, explicitly set the subplots options sharex and sharey=False.
replace this line in the code for the desired results.
f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex=False, sharey=False)
Alternatively, those two options can be omitted altogether, as False is the default. (as noted by rubenvb below)
Question 3:
Here are two examples of adding secondary plots to two of the subplots:
(add this snippet before plt.show())
# add an additional line to the lower left subplot
ax3.plot(range(5), -1*np.arange(5)*1000)
# add a bar chart to the upper right subplot
width = 0.75 # the width of the bars
x = np.arange(2, 10, 2)
y = [3, 7, 2, 9]
rects1 = ax2.bar(x, y, width, color='r')
Don't tell it to share axes:
f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
ax1.plot(range(10),2*np.arange(10))
ax2.plot(range(10),range(10))
ax3.plot(range(5),np.arange(5)*1000)
doc