What's the best practice to create multiple separate plots using matplotlib, so they can be called later or output into a pdf report? I'm a bit unclear as to how to do this in a way that retains each plot in memory (like we could with dataframes) for later reference.
Suppose we have this code:
%pylab inline
x1 = np.random.randn(50)*100
y1 = np.random.randn(50)*100
x2 = np.random.randn(50)*100
y2 = np.random.randn(50)*100
and the intent is to create 2 separate plots of (x1,y1) and (x2,y2) and 'save' them in some way to be referenced later. the intent is to be able to output these into a PDF (perhaps via reportlab). the relationship between "figures", "subplots" and "axes" is confusing to me and not sure what is optimal for this purpose. i started with an approach like:
plt.figure(1, figsize=(8, 6))
plt.subplot(211)
plt.scatter(x1, y1, c = 'r', alpha = 0.3)
plt.subplot(212)
plt.scatter(x2, y2, c = 'k', alpha = 0.7)
plt.show()
which does technically work, but i'm not sure how i can refer to these later. also, i am using a small example here for illustration, but in practice i may have many more of these.
With the implicit style that the question uses (where the figure object is not saved in a variable, and where plotting commands apply to the current figure), you can easily make a previous figure the current figure:
plt.figure(1)
will thus reactivate figure 1. plt.savefig() can then be used, additional plots can be made in it, etc.
Furthermore, you can also give a name to your figure when you create it, and then refer to it:
plt.figure("growth", figsize=…)
…
plt.figure("counts", figsize=…)
…
plt.figure("growth") # This figure becomes the active one again
(the figure reference parameter is called num, but it doesn't have to be a number and can be a string, which makes for a clearer code).
Things might make more sense if you start to use the object-oriented interface to matplotlib. In that case, you could do something like:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 6))
ax1 = fig.add_subplot(211)
ax1.scatter(x1, y1, c = 'r', alpha = 0.3)
ax2 = fig.add_subplot(212)
ax2.scatter(x2, y2, c = 'k', alpha = 0.7)
plt.show()
In this way, its easy to see that ax1 and ax2 belong to the figure instance, fig. You can then later refer back to ax1 and ax2, to plot more data on them, or adjust the axis limits, or add labels, etc., etc.
You can also add another figure, with its own set of subplots:
fig2 = plt.figure(figsize=(8, 6))
ax3 = fig2.add_subplot(211)
and then you can save the given figures at any point, and know that you are always referring to the correct figure:
fig.savefig('figure1.png')
fig2.savefig('figure2.png')
Related
I am using Plots.jl to make several plots in the same figure. When using the pyplot backend, each plot has it's own colorbar, which I don't want as they have the same data. I am trying to replicate the answer from this question, however I don't know in detail of the machinery under the Plots.jl API, so I haven't been able to replicate it. My plot is done as:
using Plots;pyplot()
p1 = plot(a,st=:contour,fill=true)
p2 = plot(b,st=:contour,fill=true)
p = plot(p1,p2)
And, the answer (which is in python) from the link is this:
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()
As far as I understand, the code inside the for is actually making the plots in the axes created by plt.subplots (in my case this is done by Plots.jl
The next line makes the plots closer, and then the line fig.add_axes creates a new axis for the colorbar.
Finally, the line of fig.colorbar creates a colorbar in the new axis and uses the data from the last plot in the for loop.
My current code is:
cbar_ax = p.o[:add_axes]([0.85, 0.15, 0.05, 0.7]);
p.o[:colorbar](p.o[:axes][1][:contourf],cax=cbar_ax)
display(p)
And it doesn't work (I wouldn't expect it to work because I don't know what I'm doing.
The error I get is:
AttributeError("'function' object has no attribute 'autoscale_None'")
Which makes me think p.o:axes[:contourf] is not the way to summon what I am trying to.
Can anyone help out? Thanks
In general, if you want to use code on the PyPlot object it's better to just use PyPlot and forget about Plots. The mix rarely works in practice.
If you do want to use Plots you should be able to do
using Plots;pyplot()
lims = extrema([a;b])
p1 = plot(a,st=:contour,fill=true, colorbar = false)
p2 = plot(b,st=:contour,fill=true, colorbar = true, clims = lims)
p = plot(p1,p2)
One of the subplots will be much wider than the other - you probably need to adjust with #layout to get them the same width.
I'm kind of new in coding and thus in python so this may sound quite dumb, but what are the main differences between .subplot() and .subplots() methods from matplotlib in python?
I didn't find this explanation anywhere else and after reading the documentation from https://matplotlib.org/ I inferred that with both methods you can create as many figures and plots as you want...so for me both of them seem to be quite the same thing and they just differ the way you can handle plots, axes, etc...or am I wrong?
Btw, I am using python3 in jupyter notebook if it makes any difference.
1. matplotlib.pyplot.subplots()
From the documentation page on matplotlib.pyplot.subplots():
This utility wrapper makes it convenient to create common layouts of subplots, including the enclosing figure object, in a single call.
That means you can use this single function to create a figure with several subplots with only one line of code. For example, the code below will return both fig which is the figure object, and axes which is a 2x3 array of axes objects which allows you to easily access each subplot:
fig, axes = plt.subplots(nrows=2, ncols=3)
2. matplotlib.pyplot.subplot()
In contrast, matplotlib.pyplot.subplot() creates only a single subplot axes at a specified grid position. This means it will require several lines of code to achieve the same result as matplot.pyplot.subplots() did in a single line of code above:
# first you have to make the figure
fig = plt.figure(1)
# now you have to create each subplot individually
ax1 = plt.subplot(231)
ax2 = plt.subplot(232)
ax3 = plt.subplot(233)
ax4 = plt.subplot(234)
ax5 = plt.subplot(235)
ax6 = plt.subplot(236)
or you can also use built-in method of fig:
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax3 = fig.add_subplot(233)
ax4 = fig.add_subplot(234)
ax5 = fig.add_subplot(235)
ax6 = fig.add_subplot(236)
Conclusion
The code above can be condensed with a loop, but it is still considerably more tedious to use. I'd therefore recommend you use matplotlib.pyplot.subplots() since it is more concise and easy to use.
I'm kind of confused what is going at the backend when I draw plots in matplotlib, tbh, I'm not clear with the hierarchy of plot, axes and figure. I read the documentation and it was helpful but I'm still confused...
The below code draws the same plot in three different ways -
#creating the arrays for testing
x = np.arange(1, 100)
y = np.sqrt(x)
#1st way
plt.plot(x, y)
#2nd way
ax = plt.subplot()
ax.plot(x, y)
#3rd way
figure = plt.figure()
new_plot = figure.add_subplot(111)
new_plot.plot(x, y)
Now my question is -
What is the difference between all the three, I mean what is going under the hood when any of the 3 methods are called?
Which method should be used when and what are the pros and cons of using any on those?
The names of objects
Matplotlib is strongly object oriented and its principal objects are the figure and the axes (I find the name axes a bit misleading, but probably it's just me).
You can think of the figure as a canvas, of which you typically specify the dimensions and possibly e.g., the background color etc etc. You use the canvas, the figure, essentially in two ways, placing other objects on it (mostly axes, but also text labels etc) and saving its contents with savefig.
You can think of an axes as a sort of Swiss Army knife, a handy object that offers a tool (e.g. .plot, .scatter, .hist etc) for everything, mostly. You can place one, two, ... many axes inside a figure using one of many different methods.
The plt interface
The plt procedural interface was originally developed to mimic the MATLAB™ interface but is not really different from the object oriented interface, even if you don't make a direct reference to the main objects (i.e., a figure and an axes) these objects are automatically instantiated and each plt method is, essentially, translated to a call of one of the methods of the underlying fundamental objects: e.g., a plt.plot() is a hidden_axes.plot and a plt.savefig is a hidden_figure.savefig.
In every moment you can have an handle on these hidden objects using plt.gcf and plt.gca, and this is sometimes necessary when one of the object methods has not been ported to a method in the plt namespace.
I'd like to add that the plt namespace contains also a number of convenience methods¹ to instantiate, in different ways, figure and axes.
Your examples
1st way
plt.plot(x, y)
Here you use only the plt interface, you can only use a single axes in each figure, but this is what you want when you are doing an exploration of your data,
a quick recipe that gets the work done...
2nd way
ax = plt.subplot()
ax.plot(x, y)
Here you use a convenience method in the plt namespace to give a name (and a handle) to your axes object, but btw there is also an hidden figure. You can later use the axes object to plot, to make an histogram etc, all things that you can do with the plt interface, but you can also access all its attributes and modify them with greater freedom.
3rd way
figure = plt.figure()
new_plot = figure.add_subplot(111)
new_plot.plot(x, y)
Here you start instantiating a figure using a convenience method in the plt namespace and later you use only the object oriented interface.
It is possible to bypass the plt convenience method (matplotlib.figure.Figure) but you then have to tweak the figure for a better interactive experience (after all, it's a convenience method).
Personal recommendations
I suggest bare plt.plot, plt.scatter in the context of an interactive session, possibly using IPython with its %matplotlib magic command, and also in the context of an exploratory Jupyter notebook.
On the other hand the object oriented approach, plus a few plt
convenience methods¹, is the way to go
if you have a permanent issue to solve once for all with a
customized arrangement of finely tuned subplots,
if you want to embed Matplotlib in the UI of a program you write.
There is a large gray area between these extremes and if you ask me what to do I'd just say "It depends"...
(1) The convenience methods in the plt name space are REALLY CONVENIENT! In particular, when you instantiate Figures and Axes using them, all the minute details needed to deal with interactive windows are automatically taken into account.
Method 1
plt.plot(x, y)
This lets you plot just one figure with (x,y) coordinates. If you just want to get one graphic, you can use this way.
Method 2
ax = plt.subplot()
ax.plot(x, y)
This lets you plot one or several figure(s) in the same window. As you write it, you will plot just one figure, but you can make something like this:
fig1, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
You will plot 4 figures which are named ax1, ax2, ax3 and ax4 each one but on the same window. This window will be just divided in 4 parts with my example.
Method 3
fig = plt.figure()
new_plot = fig.add_subplot(111)
new_plot.plot(x, y)
I didn't use it, but you can find documentation.
Example:
import numpy as np
import matplotlib.pyplot as plt
# Method 1 #
x = np.random.rand(10)
y = np.random.rand(10)
figure1 = plt.plot(x,y)
# Method 2 #
x1 = np.random.rand(10)
x2 = np.random.rand(10)
x3 = np.random.rand(10)
x4 = np.random.rand(10)
y1 = np.random.rand(10)
y2 = np.random.rand(10)
y3 = np.random.rand(10)
y4 = np.random.rand(10)
figure2, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
ax1.plot(x1,y1)
ax2.plot(x2,y2)
ax3.plot(x3,y3)
ax4.plot(x4,y4)
plt.show()
Other example:
Due to the 2nd answer of this question I supposed the following code
import matplotlib.pyplot as plt
for i1 in range(2):
plt.figure(1)
f, ax = plt.subplots()
plt.plot((0,3), (2, 2), 'b')
for i2 in range(2):
plt.figure(2)
f, ax = plt.subplots()
plt.plot([1,2,3], [1,2,3], 'r')
plt.savefig('foo_{}_bar_{}.jpg'.format(i2, i1))
plt.close()
plt.figure(1)
plt.plot( [1,2,3],[1,2,3], 'r')
plt.savefig('bar_{}.jpg'.format(i1))
plt.close()
to create plots bar_0.jpg and bar_1.jpg showing a blue and a red line each.
However, figures look like
instead of
How can I achieve the desired behaviour?
Note that plots foo_*.jpg have to be closed and saved during handling of the bar plots.
You're already saving the Axes objects, so instead of calling the PyPlot plot function (which draws on the last created or activated Axes), use the objects' plot function:
ax.plot(...)
If you then give both a different name, say ax1 and ax2, you can draw on the one you like without interfering with the other. All plt. commands also exist as an Axes member function, but sometimes the name changes (plt.xticks becomes ax.set_xticks for example). See the documentation of Axes for details.
To save to figures, use the Figure objects in the same way:
f.savefig(...)
This API type is only just coming to Matlab, FYI, and will probably replace the old-fashioned "draw on the last active plot" behaviour in the future. The object-oriented approach here is more flexible with minimal overhead, so I strongly recommend you use it everywhere.
If unsure, better to make it explicit:
import matplotlib.pyplot as plt
for i1 in range(2):
fig1,ax1 = plt.subplots()
fig2,ax2 = plt.subplots()
ax1.plot([0,4],[2,2],'b')
for i2 in range(2):
ax2.plot([1,2,3],[1,2,3],'r')
fig2.savefig('abc{}.png'.format(2*i1+i2))
plt.figure(1)
ax1.plot([1,2,3],[1,2,3],'r')
fig1.savefig('cba{}.png'.format(i1))
I have multiple lines to be drawn on the same axes, and each of them are dynamically updated (I use set_data), The issue being that i am not aware of the x and y limits of each of the lines. And axes.autoscale_view(True,True,True) / axes.set_autoscale_on(True) are not doing what they are supposed to. How do i auto scale my axes?
import matplotlib.pyplot as plt
fig = plt.figure()
axes = fig.add_subplot(111)
axes.set_autoscale_on(True)
axes.autoscale_view(True,True,True)
l1, = axes.plot([0,0.1,0.2],[1,1.1,1.2])
l2, = axes.plot([0,0.1,0.2],[-0.1,0,0.1])
#plt.show() #shows the auto scaled.
l2.set_data([0,0.1,0.2],[-1,-0.9,-0.8])
#axes.set_ylim([-2,2]) #this works, but i cannot afford to do this.
plt.draw()
plt.show() #does not show auto scaled
I have referred to these already, this , this.
In all cases I have come across, the x,y limits are known. I have multiple lines on the axes and their ranges change, keeping track of the ymax for the entire data is not practical
A little bit of exploring got me to this,
xmin,xmax,ymin,ymax = matplotlib.figure.FigureImage.get_extent(FigureImage)
But here again, i do not know how to access FigureImage from the Figure instance.
Using matplotlib 0.99.3
From the matplotlib docs for autoscale_view:
The data limits are not updated automatically when artist data are changed after the artist has been added to an Axes instance. In that case, use matplotlib.axes.Axes.relim() prior to calling autoscale_view.
So, you'll need to add two lines before your plt.draw() call after the set_data call:
axes.relim()
axes.autoscale_view(True,True,True)