I want to create a figure with two y-axes and add multiple curves to each of those axes at different points in my code (from different functions).
In a first function, I create a figure:
import matplotlib.pyplot as plt
from numpy import *
# Opens new figure with two axes
def function1():
f = plt.figure(1)
ax1 = plt.subplot(211)
ax2 = ax1.twinx()
x = linspace(0,2*pi,100)
ax1.plot(x,sin(x),'b')
ax2.plot(x,1000*cos(x),'g')
# other stuff will be shown in subplot 212...
Now, in a second function I want to add a curve to each of the already created axes:
def function2():
# Get handles of figure, which is already open
f = plt.figure(1)
ax3 = plt.subplot(211).axes # get handle to 1st axis
ax4 = ax3.twinx() # get handle to 2nd axis (wrong?)
# Add new curves
x = linspace(0,2*pi,100)
ax3.plot(x,sin(2*x),'m')
ax4.plot(x,1000*cos(2*x),'r')
Now my problem is that the green curve added in the first code block is not anymore visible after the second block is executed (all others are).
I think the reason for this is the line
ax4 = ax3.twinx()
in my second code block. It probably creates a new twinx() instead of returning a handle to the existing one.
How would I correctly get the handles to already existing twinx-axes in a plot?
you can use get_shared_x_axes (get_shared_y_axes) to get a handle to the axes created by twinx (twiny):
# creat some axes
f,a = plt.subplots()
# create axes that share their x-axes
atwin = a.twinx()
# in case you don't have access to atwin anymore you can get a handle to it with
atwin_alt = a.get_shared_x_axes().get_siblings(a)[0]
atwin == atwin_alt # should be True
I would guess that the cleanest way would be to create the axes outside the functions. Then you can supply whatever axes you like to the function.
import matplotlib.pyplot as plt
import numpy as np
def function1(ax1, ax2):
x = np.linspace(0,2*np.pi,100)
ax1.plot(x,np.sin(x),'b')
ax2.plot(x,1000*np.cos(x),'g')
def function2(ax1, ax2):
x = np.linspace(0,2*np.pi,100)
ax1.plot(x,np.sin(2*x),'m')
ax2.plot(x,1000*np.cos(2*x),'r')
fig, (ax, bx) = plt.subplots(nrows=2)
axtwin = ax.twinx()
function1(ax, axtwin)
function2(ax, axtwin)
plt.show()
Related
I have created a grid of subplots to my liking.
I initiated the plotting by defining fig,ax = plt.subplots(2,6,figsize=(24,8))
So far so good. I filled those subplots with their respective content. Now I want to plot a single or two particular subplot in isolation. I tried:
ax[idx][idx].plot()
This does not work and returns an empty list
I have tried:
fig_single,ax_single = plt.subplots(2,1)
ax_single[0]=ax[idx][0]
ax_single[1]=ax[idx][1]
This returns:
TypeError: 'AxesSubplot' object does not support item assignment
How do I proceed without plotting those subplots again by calling the respective plot functions?
You're close.
fig,ax = plt.subplots(nrows=2,ncols=6,sharex=False,sharey=False,figsize=(24,8))
#set either sharex=True or sharey=True if you wish axis limits to be shared
#=> very handy for interactive exploration of timeseries data, ...
r=0 #first row
c=0 #first column
ax[r,c].plot() #plot your data, instead of ax[r][c].plot()
ax[r,c].set_title() #name title for a subplot
ax[r,c].set_ylabel('Ylabel ') #ylabel for a subplot
ax[r,c].set_xlabel('X axis label') #xlabel for a subplot
A more complete/flexible method is to assign r,c:
for i in range(nrows*ncols):
r,c = np.divmod(i,ncols)
ax[r,c].plot() #....
You can afterwards still make modifications, e.g. set_ylim, set_title, ...
So if you want to name the label of the 11th subplot:
ax[2,4].set_ylabel('11th subplot ylabel')
You will often want to make use of fig.tight_layout() at the end, so that the figure uses the available area correctly.
Complete example:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,180,180)
nrows = 2
ncols = 6
fig,ax = plt.subplots(nrows=nrows,ncols=ncols,sharex=False,sharey=False,figsize=(24,8))
for i in range(nrows*ncols):
r,c = np.divmod(i,ncols)
y = np.sin(x*180/np.pi*(i+1))
ax[r,c].plot(x,y)
ax[r,c].set_title('%s'%i)
fig.suptitle('Overall figure title')
fig.tight_layout()
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.
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
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.
I am currently trying to implement a 'zoom' functionality into my code. By this I mean I would like to have two subplots side by side, one of which contains the initial data and the other which contains a 'zoomed in' plot which is decided by user input.
Currently, I can create two subplots side by side, but after calling for the user input, instead of updating the second subplot, my script is creating an entirely new figure below and not updating the second subplot. It is important that the graph containing data is plotted first so the user can choose the value for the input accordingly.
def plot_func(data):
plot_this = data
plt.close('all')
fig = plt.figure()
#Subplot 1
ax1 = fig.add_subplot(1,2,1)
ax1.plot(plot_this)
plt.show()
zoom = input("Where would you like to zoom to: ")
zoom_in = plot_this[0:int(zoom)]
#Subplot 2
ax2 = fig.add_subplot(1,2,2)
ax2.plot(zoom_in)
plt.show()
The code above is a simplified version of what I am hoping to do. Display a subplot, and let the user enter an input based on that subplot. Then either edit a subplot that is already created or create a new one that is next to the first. Again it is crucial that the 'zoomed in' subplot is alongside the first opposed to below.
I think it is not very convenient for the user to type in numbers for zooming. The more standard way would be mouse interaction as already provided by the various matplotlib tools.
There is no standard tool for zooming in a different plot, but we can easily provide this functionality using matplotlib.widgets.RectangleSelector as shown in the code below.
We need to plot the same data in two subplots and connect the RectangleSelector to one of the subplots (ax). Every time a selection is made, the data coordinates of the selection in the first subplot are simply used as axis-limits on the second subplot, effectiveliy proving zoom-in (or magnification) functionality.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RectangleSelector
def onselect(eclick, erelease):
#http://matplotlib.org/api/widgets_api.html
xlim = np.sort(np.array([erelease.xdata,eclick.xdata ]))
ylim = np.sort(np.array([erelease.ydata,eclick.ydata ]))
ax2.set_xlim(xlim)
ax2.set_ylim(ylim)
def toggle_selector(event):
# press escape to return to non-zoomed plot
if event.key in ['escape'] and toggle_selector.RS.active:
ax2.set_xlim(ax.get_xlim())
ax2.set_ylim(ax.get_ylim())
x = np.arange(100)/(100.)*7.*np.pi
y = np.sin(x)**2
fig = plt.figure()
ax = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
#plot identical data in both axes
ax.plot(x,y, lw=2)
ax.plot([5,14,21],[.3,.6,.1], marker="s", color="red", ls="none")
ax2.plot(x,y, lw=2)
ax2.plot([5,14,21],[.3,.6,.1], marker="s", color="red", ls="none")
ax.set_title("Select region with your mouse.\nPress escape to deactivate zoom")
ax2.set_title("Zoomed Plot")
toggle_selector.RS = RectangleSelector(ax, onselect, drawtype='box', interactive=True)
fig.canvas.mpl_connect('key_press_event', toggle_selector)
plt.show()
%matplotlib inline
import mpld3
mpld3.enable_notebook()