Add multiple axes from different sources into same figure - python

I am using Python/matplotlib to create a figure whereby it has three subplots, each returned from a different 'source' or class method.
For example, I have a script called 'plot_spectra.py' that contains the Spectra() class with method Plot().
So, calling Spectra('filename.ext').Plot() will return a tuple, as per the code below:
# create the plot
fig, ax = plt.subplots()
ax.contour(xx, yy, plane, levels=cl, cmap=cmap)
ax.set_xlim(ppm_1h_0, ppm_1h_1)
ax.set_ylim(ppm_13c_0, ppm_13c_1)
# return the contour plot
return fig, ax
It is my understanding that the 'figure' is the 'window' in matplotlib, and the 'ax' is an individual plot. I would then want to say, plot three of these 'ax' objects in the same figure, but I am struggling to do so because I keep getting an empty window and I think I have misunderstood what each object actually is.
Calling:
hnca, hnca_ax = Spectra('data/HNCA.ucsf', type='sparky').Plot(plane_ppm=resi.N(), vline=resi.H())
plt.subplot(2,2,1)
plt.subplot(hnca_ax)
eucplot, barplot = PlotEucXYIntensity(scores, x='H', y='N')
plt.subplot(2,2,3)
plt.subplot(eucplot)
plt.subplot(2,2,4)
plt.subplot(barplot)
plt.show()
Ultimately, what I am trying to obtain is a single window that looks like this:
Where each plot has been returned from a different function or class method.
What 'object' do I need to return from my functions? And how do I incorporate these three objects into a single figure?

I would suggest this kind of approach, where you specify the ax on which you want to plot in the function:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
def Spectra(data, ax):
ax.plot(data)
def PlotIntensity(data, ax):
ax.hist(data)
def SeabornScatter(data, ax):
sns.scatterplot(data, data, ax = ax)
spectrum = np.random.random((1000,))
plt.figure()
ax1 = plt.subplot(1,3,1)
Spectra(spectrum, ax1)
ax2 = plt.subplot(1,3,2)
SeabornScatter(spectrum, ax2)
ax3 = plt.subplot(1,3,3)
PlotIntensity(spectrum, ax3)
plt.tight_layout()
plt.show()
You can specify the grid for the subplots in very different ways, and you probably also want to have a look on the gridspec module.

One way to do this is:
f = plt.figure()
gs = f.add_gridspec(2,2)
ax = f.add_subplot(gs[0,:])
Think of the '2,2' as adding 2 row x 2 columns.
On the third line 'gs[0,:]' is telling to add a chart on row 0, all columns. This will create the chart on the top of your top. Note that indices begin with 0 and not with 1.
To add the 'eucplot' you will have to call a different ax on row 1 and column 0:
ax2 = f.add_subplot(gs[1,0])
Lastly, the 'barplot' will go in yet a different ax on row 1, column 1:
ax3 = f.add_subplot(gs[1,1])
See this site here for further reference: Customizing Figure Layouts Using GridSpec and Other Functions

Related

set xlim across multiple figures in python? [duplicate]

I'm trying to share two subplots axes, but I need to share the x axis after the figure was created. E.g. I create this figure:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)
fig = plt.figure()
ax1 = plt.subplot(211)
plt.plot(t,x)
ax2 = plt.subplot(212)
plt.plot(t,y)
# some code to share both x axes
plt.show()
Instead of the comment I want to insert some code to share both x axes.
How do I do this? There are some relevant sounding attributes
_shared_x_axes and _shared_x_axes when I check to figure axis (fig.get_axes()) but I don't know how to link them.
The usual way to share axes is to create the shared properties at creation. Either
fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212, sharex = ax1)
or
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
Sharing the axes after they have been created should therefore not be necessary.
However if for any reason, you need to share axes after they have been created (actually, using a different library which creates some subplots, like here might be a reason), there would still be a solution:
Using
ax1.get_shared_x_axes().join(ax1, ax2)
creates a link between the two axes, ax1 and ax2. In contrast to the sharing at creation time, you will have to set the xticklabels off manually for one of the axes (in case that is wanted).
A complete example:
import numpy as np
import matplotlib.pyplot as plt
t= np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)
fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)
ax1.plot(t,x)
ax2.plot(t,y)
ax1.get_shared_x_axes().join(ax1, ax2)
ax1.set_xticklabels([])
# ax2.autoscale() ## call autoscale if needed
plt.show()
The other answer has code for dealing with a list of axes:
axes[0].get_shared_x_axes().join(axes[0], *axes[1:])
As of Matplotlib v3.3 there now exist Axes.sharex, Axes.sharey methods:
ax1.sharex(ax2)
ax1.sharey(ax3)
Just to add to ImportanceOfBeingErnest's answer above:
If you have an entire list of axes objects, you can pass them all at once and have their axes shared by unpacking the list like so:
ax_list = [ax1, ax2, ... axn] #< your axes objects
ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list)
The above will link all of them together. Of course, you can get creative and sub-set your list to link only some of them.
Note:
In order to have all axes linked together, you do have to include the first element of the axes_list in the call, despite the fact that you are invoking .get_shared_x_axes() on the first element to start with!
So doing this, which would certainly appear logical:
ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list[1:])
... will result in linking all axes objects together except the first one, which will remain entirely independent from the others.

Adjusting the x-axis label for multiple subplots [duplicate]

I'm trying to share two subplots axes, but I need to share the x axis after the figure was created. E.g. I create this figure:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)
fig = plt.figure()
ax1 = plt.subplot(211)
plt.plot(t,x)
ax2 = plt.subplot(212)
plt.plot(t,y)
# some code to share both x axes
plt.show()
Instead of the comment I want to insert some code to share both x axes.
How do I do this? There are some relevant sounding attributes
_shared_x_axes and _shared_x_axes when I check to figure axis (fig.get_axes()) but I don't know how to link them.
The usual way to share axes is to create the shared properties at creation. Either
fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212, sharex = ax1)
or
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
Sharing the axes after they have been created should therefore not be necessary.
However if for any reason, you need to share axes after they have been created (actually, using a different library which creates some subplots, like here might be a reason), there would still be a solution:
Using
ax1.get_shared_x_axes().join(ax1, ax2)
creates a link between the two axes, ax1 and ax2. In contrast to the sharing at creation time, you will have to set the xticklabels off manually for one of the axes (in case that is wanted).
A complete example:
import numpy as np
import matplotlib.pyplot as plt
t= np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)
fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)
ax1.plot(t,x)
ax2.plot(t,y)
ax1.get_shared_x_axes().join(ax1, ax2)
ax1.set_xticklabels([])
# ax2.autoscale() ## call autoscale if needed
plt.show()
The other answer has code for dealing with a list of axes:
axes[0].get_shared_x_axes().join(axes[0], *axes[1:])
As of Matplotlib v3.3 there now exist Axes.sharex, Axes.sharey methods:
ax1.sharex(ax2)
ax1.sharey(ax3)
Just to add to ImportanceOfBeingErnest's answer above:
If you have an entire list of axes objects, you can pass them all at once and have their axes shared by unpacking the list like so:
ax_list = [ax1, ax2, ... axn] #< your axes objects
ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list)
The above will link all of them together. Of course, you can get creative and sub-set your list to link only some of them.
Note:
In order to have all axes linked together, you do have to include the first element of the axes_list in the call, despite the fact that you are invoking .get_shared_x_axes() on the first element to start with!
So doing this, which would certainly appear logical:
ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list[1:])
... will result in linking all axes objects together except the first one, which will remain entirely independent from the others.

plot graph in a specific figure in python

Actually, I am not clear that
fig_1 = plt.figure()
plt.subplot(2,2,1)
...
Is the ploting like plt.subplot(2,2,1) and other plt. plot on the fig_1 or system will automatically create a new empty figure?
Then how to plot something in a specific figure, for example:
fig_1 = plt.figure()
fig_2 = plt.figure()
plt.subplot(2,2,1)
I want to subplot on fig_2.
You can access a certain figure by e.g.
ax_1_1 = fig_1.add_subplot(2,2,1)
but this has a slightly different syntax (compare plt.subplot() against fig.add_subplot())
So I would recommend to create figures with subplots already prepared vis plt.subplots which returns handles for figure and axes on the fly:
fig_1, axs_1 = plt.subplots(2, 2)
fig_2, axs_2 = plt.subplots(3, 4)
axs_1[0, 0].plot(range(10))
axs_2[2, 3].plot(range(100))
fig_1.suptitle('Figure 1')
fig_2.suptitle('Figure 2')
etc. ...
You can use figure.add_subplot which will return an ax linked to your figure on which you can plot your data. Look at this page to get a global view of the different objects used by matplotlib.

How to adjust and shape the subplots?

I am trying to plot 2 plots in one figure. So just 2 subplots and adjust the figure sizes and find a decent one. At the moment I am using this code:
import matplotlib.pyplot as plt
import pandas as pd
#import numpy as np
##### import data #####
df=pd.read_csv('C:\\Users\Kevin\Documents\Afstudeer\Measurements/1st_plot.txt',sep=',',decimal='.',header=None)
df.columns=['Vx','Vy','undefined','Laser_signal']
fig, ax = plt.subplots(figsize=(8, 5))
ax1=fig.add_subplot(121)
ax1.plot(df['Vx'],df['Vy'],label='plot')
plt.xlabel(r'$V_x$')
plt.ylabel(r'$V_y$')
ax2=fig.add_subplot(122)
ax1.scatter(df['Vx'],df['Vy'],label='data_points')
plt.xlabel(r'$V_x$')
plt.ylabel(r'$V_y$')
plt.subplots_adjust(left=.2, bottom=.45, right=.8, top=.95,
wspace=.3, hspace=.4)
so its this last code that is confusing me. When i do the plot, i get something like this:
Here is my sample data:
-1.725953467,0.109343505,-10.433363664,0.159675246
-1.725953467,0.110607445,-10.433363664,0.159675246
-1.729140157,0.110607445,-10.433363664,0.159675246
-1.722766777,0.10839555,-10.433363664,0.159675246
-1.727865481,0.11534722,-10.433363664,0.159359499
-1.726272136,0.112503355,-10.433363664,0.159675246
-1.731689509,0.120086995,-10.433363664,0.159359499
-1.727228143,0.117559115,-10.433363664,0.159359499
-1.729140157,0.11977101,-10.433363664,0.159675246
-1.730096164,0.121350935,-10.433363664,0.159675246
-1.729458826,0.122614875,-10.433363664,0.159043752
-1.735832206,0.12482677,-10.433363664,0.159359499
-1.728821488,0.121350935,-10.433363664,0.159675246
-1.733920192,0.124510785,-10.433363664,0.159359499
-1.731052171,0.12166692,-10.433363664,0.159675246
-1.739018896,0.12735465,-10.433363664,0.159043752
-1.738062889,0.12861859,-10.433363664,0.159043752
-1.738700227,0.133358365,-10.433363664,0.159043752
-1.73455753,0.12988253,-10.433363664,0.159043752
-1.743161593,0.144101855,-10.433363664,0.159043752
As you will see from the code, I am only taking the 1st 2 columns. I am expecting 2 subplots in a one figure. So why do i get these up and bottom lines between the plots??
The two lines
fig, ax = plt.subplots(figsize=(8, 5))
ax1=fig.add_subplot(121)
are somehow mutually exclusive. Either you create the subplots via
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 5))
or you create them via
fig = plt.figure()
ax1=fig.add_subplot(121)
ax2=fig.add_subplot(122)
After that better use the axes handles ax1 and ax2 to set any properties, e.g.
ax1.set_xlabel(r'$V_x$')
instead of plt.xlabel.

Matplotlib wrong colorbar assigned to subplot

I have a wrapper, plot2d, for the matplotlib function imshow which also calls colorbar (see code below). When I use it on subplots in non-sequential order (e.g. subplot 2 followed by subplot 1) the wrong color bar gets plotted on at least one of the subplot axes. An example of this, is the function bad_cb_on_top, in the below code. However, when I use works_just_fine I get the intended result. The only difference between these two function is the order in which they plot to subplot axes.
My two questions are :
Why does the order in which I plot to subplot axes matter?
How can I get bad_cb_on_top to produce the same results that works_just_fine currently give, without modifying bad_cp_on_top?
Version info:
python 2.7.3
matplotlib 1.1.1
Example code:
from pylab import *
x = linspace(-1, 1, 100)
x, y = meshgrid(x, x)
data = 1./(x**2 + y**2)
def plot2d(data, ax=None, vmax=None):
'''A simple wrapper for implot.'''
# if ax was given, set ax as the current axis for plotting
if ax :
sca(ax)
# Plot the data
im = imshow(data, vmax=vmax, interpolation='lanczos')
# This line assures that ax is the axis which was just plotted on
# even if ax was not specified
ax = im.axes
# Add the colorbar
cb = colorbar(ax=ax, orientation='vertical')
return None
figsize=[4, 7]
def bad_cb_on_top():
# This function copies the color bar from the bottom panel
# to the top panel for some unknown reason.
fig, axs = subplots(2, 1, figsize=figsize)
plot2d(data, vmax=31, ax=axs[1])
plot2d(data, vmax=314, ax=axs[0])
fig.show()
def works_just_fine():
# This function works as intended despite little change
fig, axs = subplots(2, 1, figsize=figsize)
plot2d(data, vmax=314, ax=axs[0])
plot2d(data, vmax=31, ax=axs[1])
fig.show()
bad_cb_on_top()
works_just_fine()
Output from bad_cp_on_top():
Output from works_just_fine()
I could be very wrong, but you may be able to enforce the right one by passing im into colorbar() as the mappable. In plot2d:
cb = colorbar(im, ax=ax, orientation='vertical')
I think that way it specifies not only the axes but the mappable input. FYI, it makes no difference to me (using 1.3.1) so nothing breaks, but it also means I can't test it.

Categories