How could I arrange multiple pyplot figures in a kind of layout? - python

I created a software to do signal analyses. There are multiple functions, and each finally display a complex figure containing labels, plot, axhspan, axvspan etc... Usually, these functions are called individually. Everyone of my functions returns a figure object, that I can save in pdf for example.
def Myfunction1(self):
fig = pyplot.figure()
...do somestuff, create my figure
pyplot.show()
fig.savefig('C:\MyFigurefolder\figure1.pdf', dpi=300)
return fig
def Myfunction2(self):
fig = pyplot.figure()
...do some other stuff, create my 2nd figure
pyplot.show()
fig.savefig('C:\MyFigurefolder\figure2.pdf', dpi=300)
return fig
Now, I would like to create a kind of "summary figure", by doing a metaanalyses, and pooling multiple figures together, and saving them in a final pdf.
I don't really know how I should do that. Is there a way to use whole figure objects, (or maybe the multiple individual pdf) to do my figure?
Something like:
def FinalFigure(self):
final = A_Kind_Of_Layout_Or_A_Figure_or_something
a=self.Myfunction1()
b=self.Myfunction2()
Action_to_arrange_a_and_b_like_gridspec
final.savefig('C:\MyFigurefolder\FinalFigure.pdf', dpi=300)

You may combine several plots with matplotlib.pyplot.subplot. For more control on the layout, check out the GridSpec.
Edit: As requested, a short example from the linked tutorial:
A gridspec instance provides array-like (2d or 1d) indexing that returns the SubplotSpec instance. For, SubplotSpec that spans multiple cells, use slice.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3)
ax1 = plt.subplot(gs[0, :])
ax2 = plt.subplot(gs[1,:-1])
ax3 = plt.subplot(gs[1:, -1])
ax4 = plt.subplot(gs[-1,0])
ax5 = plt.subplot(gs[-1,-2])

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.

Returning matplotlib plot/figure from a function and saving it later

As the title states I want to return a plt or figure (still not sure what the difference between the two things are) using matplotlib. The main idea behind it is so I can save the plt/figure later.
import seaborn as sns
from matplotlib import pyplot as plt
def graph(df, id):
# size of the graph
xlims = (-180, 180)
ylims = (-180, 180)
# dictate the colors of the scatter plot based on the grouping of hot or cold
color_dict = {'COLD': 'blue',
'HOT': 'red'}
title_name = f"{id}"
ax = sns.scatterplot(data=df, hue='GRP', x='X_GRID', y='Y_GRID',
legend=False, palette=color_dict)
ax.set_title(title_name)
ax.set(xlim=xlims)
ax.set(ylim=ylims)
if show_grid:
# pass in the prev graph so I can overlay grid
ax = self.__get_grid( ax)
circle1 = plt.Circle(xy=(0, 0), radius=150, color='black', fill=False, zorder=3)
ax.add_patch(circle1)
ax.set_aspect('equal')
plt.axis('off')
plt.savefig(title_name + '_in_ftn.png')
fig = plt.figure()
plt.clf()
return (fig, title_name + '.png')
plots = []
# dfs is just a tuple of df, id for example purposes
for df, id in dfs:
plots.append(graph(df, id))
for plot, file_name in plots:
plot.savefig(file_name)
plot.clf()
When using plot.savefig(filename) it saves, but the saved file is blank which is wrong. Am I not properly returning the object I want to save? If not what should I return to be able to save it?
I kind of having it work, but not really. I am currently saving two figures for testing purposes. For some reason when I use the fig=plt.figure() and saving it outside the function the title of the figure and the filename are different (even though they should be the same since the only difference is .png)
However, when saving it inside the function the title name of the figure and the filename name are the same.
You code has multiple issues that I'll try to discuss here:
Your confusion around plt
First of all, there is no such thing as "a plt". plt is the custom name you are giving to the matplotlib.pyplot module when you are importing it with the line import matplotlib.pyplot as plt. You are basically just renaming the module with an easy to type abbreviation. If you had just written import matplotlib, you would have to write matplotlib.pyplot.axis('off') instead of plt.axis('off').
Mix of procedural and object oriented approach
You are using a mix of the procedural and object oriented approach for matplotlib.
Either you call your methods on the axis object (ax) or you can call functions that implicitly handle the axis and figure. For example you could either create and axis and then call ax.plot(...) or instead use plt.plot(...), which implicitly creates the figure and axis. In your case, you mainly use the object oriented approach on the axis object that is returned by the seaborn function. However, you should use ax.axis('off') instead of plt.axis('off').
You create a new blank figure
When you are calling the seaborn function sns.scatterplot, you are implicitly creating a matplotlib figure and axis object. You catch that axis object in the variable ax. You then use plt.savefig to save your image in the function, which works by implicitly getting the figure corresponding to the currently used axis object. However, you are then creating a new figure by calling fig = plt.figure(), which is of course blank, and then returning it. What you should do, is getting the figure currently used by the axis object you are working with. You can get it by calling fig = plt.gcf() (which stands for "get current figure") and would be the procedural approach, or better use fig = ax.get_figure()
What you should do instead is something like this:
import seaborn as sns
from matplotlib import pyplot as plt
def graph(df, id):
# size of the graph
xlims = (-180, 180)
ylims = (-180, 180)
# dictate the colors of the scatter plot based on the grouping of hot or cold
color_dict = {'COLD': 'blue',
'HOT': 'red'}
title_name = f"{id}"
ax = sns.scatterplot(data=df, hue='GRP', x='X_GRID', y='Y_GRID',
legend=False, palette=color_dict)
ax.set_title(title_name)
ax.set(xlim=xlims)
ax.set(ylim=ylims)
if show_grid:
# pass in the prev graph so I can overlay grid
ax = self.__get_grid( ax)
circle1 = plt.Circle(xy=(0, 0), radius=150, color='black', fill=False, zorder=3)
ax.add_patch(circle1)
ax.set_aspect('equal')
ax.axis('off')
fig = ax.get_figure()
fig.savefig(title_name + '_in_ftn.png')
return (fig, title_name + '.png')

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.

about .show() of matplotlib

I am working with matplotlib to generate some graphs but I do not know the difference between these two ways of showing an image. I already read some documentation about it but I do not understand yet.
First way:
import matplotlib.pyplot as plt
plt.figure()
plt.plot(x, y)
plt.show()
Second way:
import matplotlib.pyplot as plt
graph = plt.figure()
plt.plot(x, y)
graph.show()
I think this two ways do not do the same thing but it is not clear to me.
Could someone explain it step by step for the two ways?
Simplified, plt.show() will start an event loop and create a graphical representation for each figure that is active inside the pyplot state.
In contrast, fig.show(), where fig is a figure instance, would show only this figure. Since it would also not block, it is (only) useful in interactive sessions; else the figure would be closed directly after showing it due to the script exiting.
In the usual case you would hence prefer plt.show(). This does not prevent you from using the object-oriented interface. A recommended way of creating and showing a figure is hence,
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
For two windows you can just repeat the plotting,
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
ax1.plot(x1, y1)
fig2, ax2 = plt.subplots()
ax2.plot(x2, y2)
plt.show()
Matplotlib has two styles of API implemented. One is object based (graph.show()) and the other is procedural (plt.show()) and looks a lot like the Matlab plotting API.
The procedural API works on the current figure and/or set of axes. You can always getting the current figure with plt.gcf() and the current axes with plt.gca().
There are occasionally some slight differences in syntax here and there. For example, if you want to set the x axis limits:
plt.xlim([0, 10])
or
ax = plt.gca()
ax.set_xlim([0, 10])
plt.figure returns an object that is assigned with graph = plt.figure() to graph . this is used when specific characteristics of this object ( the plot ) are intended to be changed, now the object can be refered to by its instance graph ( object-based plotting )
you use this i.e. if you want to access the axes of the graph or labels, subplots, ...
see https://python4mpia.github.io/plotting/advanced.html for object-based plotting
to manipulate the plot object you have to get a reference to it ( handle ) and this is done by graph = plt.figure() ( cf Object-Oriented Programming )

Python figure and axes object

I have a big unresolved question about the matplotlib Python module.
If I create a figure called [Figure1], with 2 axes [Ax1, Ax2], and another figure [Figure2], is there a function or method that will allow me to export the Ax1 object from Figure1 and redraw it to the Figure2 object?
In general axes are bound to a figure. The reason is, that matplotlib usually performs some operations in the background to make them look nice in the figure.
There are some hacky ways around this, also this one, but the general consensus seems to be that one should avoid trying to copy axes.
On the other hand this need not be a problem or a restriction at all.
You can always define a function which does the plotting and use this on several figures like so:
import matplotlib.pyplot as plt
def plot1(ax, **kwargs):
x = range(5)
y = [5,4,5,1,2]
ax.plot(x,y, c=kwargs.get("c", "r"))
ax.set_xlim((0,5))
ax.set_title(kwargs.get("title", "Some title"))
# do some more specific stuff with your axes
#create a figure
fig, (ax1, ax2) = plt.subplots(1,2)
# add the same plot to it twice
plot1(ax1)
plot1(ax2, c="b", title="Some other title")
plt.savefig(__file__+".png")
plt.close("all")
# add the same plot to a different figure
fig, ax1 = plt.subplots(1,1)
plot1(ax1)
plt.show()

Categories