I am interested in making movies using matplotlib. Examples I've seen so far, such as this one for moviewriter, seem to have you editing the data in-place for each frame. This is very efficient, avoiding redrawing the parts of the image that stay the same each time. However, it can be clunky for rapid data exploration. I would like a recipe that lets me simply take a fully drawn figure as each frame (clearing the same figure object each time is fine).
The reason for this: I often create moderately complicated figures using custom functions, with a form like plotme(ax, data, **options). Often I develop these functions without animations in mind, and later want to animate the figures by calling the plotting function in a loop. I don't want to have to change the logic of the functions to "setData" of existing artists in the figure for each frame.
Although the example code you've shown updates existing plot objects, there is no reason that you need to do so. The critical part of the attached code is the writer.grab_frame() which simply gets a screen capture of the current figure.
Here is an example without using existing plot objects
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.animation as manimation
FFMpegWriter = manimation.writers['ffmpeg']
metadata = dict(title='Movie Test', artist='Matplotlib',
comment='Movie support!')
writer = FFMpegWriter(fps=15, metadata=metadata)
fig = plt.figure()
with writer.saving(fig, "writer_test.mp4", 100):
for k in range(10):
# Create a new plot object
plt.plot(range(k), range(k), 'o')
writer.grab_frame()
Related
I have a script which I'm adapting to include a GUI. In it, I create a plot with subplots (the arrangement of which depends on the number of plots - e.g. 4 plots go into a square rather than 4-across). That plot (with a subplot for each of the "targets" analyzed) gets saved to a .png.
In building the GUI, I'm writing up the 'results' frame and would like to show these individual subplots on their own tabs. I've written the code to lay out the frame how I want it, but in order to separate the subplots into their own plots, I need to draw the completed Axes object (e.g. the entire subplot for that target) onto a new figure in the frame.
Since the number of subplots isn't known before runtime, I already have my Axes objects/subplots in an array (/list?) axs, whose members are the individual Axes objects (each containing data points created with ax.scatter() and several lines and annotations created with ax.plot() and ax.annotate).
When I initially create the axes, I do so with
fig, axs = plt.subplots(num='Title', nrows=numrow, ncols=numcol,
figsize=[numcol*5, numrow*5],
subplot_kw={'adjustable':'box', 'aspect':1})
Is there a way to now take these axes and draw them onto a new figure (the one that will be contained in the 'results' frame of the GUI)? In my searches, I only came up with ways to plot multiple axes onto a single figure (i.e. how to use subplots()) but nothing came up on how I'd throw a pre-existing Axes object into a new figure that it wasn't originally associated with. I'd rather not re-draw the axes from scratch -- there's quite a bit of decoration and multiple datasets / lines plotted onto them already.
Any ideas? Happy to post code as requested, but since this more of a "How do I do this" than a "why doesn't my code work", I didn't post much of it.
Thank you!
I believe that's not possible and you will have to recreate the Axes objects inside the other figure. Which is just a matter of code reorganization. Note that your approach would not noticeably improve rendering performance. Matplotlib would have to re-render the Axes objects anyway, and that's the computationally expensive part. Creating the objects is relatively cheap.
What you're trying to do is pretty much this:
from matplotlib import pyplot
pyplot.ion()
figure1 = pyplot.figure()
axes = figure1.add_subplot()
axes.plot([0, 1], [0, 1])
figure2 = pyplot.figure()
figure2.add_axes(axes)
Which raises:
ValueError: The Axes must have been created in the present figure
And the documentation of add_axes() notes:
In rare circumstances, add_axes may be called with a single argument, an Axes instance already created in the present figure but not in the figure's list of Axes.
So that's a pretty clear indication that this is not a supported use case.
In matplotlib.pyplot, what is the difference between plt.clf() and plt.close()? Will they function the same way?
I am running a loop where at the end of each iteration I am producing a figure and saving the plot. On first couple tries the plot was retaining the old figures in every subsequent plot. I'm looking for, individual plots for each iteration without the old figures, does it matter which one I use? The calculation I'm running takes a very long time and it would be very time consuming to test it out.
plt.close() will close the figure window entirely, where plt.clf() will just clear the figure - you can still paint another plot onto it.
It sounds like, for your needs, you should be preferring plt.clf(), or better yet keep a handle on the line objects themselves (they are returned in lists by plot calls) and use .set_data on those in subsequent iterations.
I think it is worth mentioning that plt.close() releases the memory, thus is preferred when generating and saving many figures in one run.
Using plt.clf() in such case will produce a warning after 20 plots (even if they are not going to be shown by plt.show()):
More than 20 figures have been opened. Figures created through the
pyplot interface (matplotlib.pyplot.figure) are retained until
explicitly closed and may consume too much memory.
plt.clf() clears the entire current figure with all its axes, but leaves the window opened, such that it may be reused for other plots.
plt.close() closes a window, which will be the current window, if not specified otherwise.
There is a slight difference between the two functions.
plt.close() - It altogether plots the graph in seperate windows,releasing
memory,retaining each window for view.
plt.clf() - We can say,it displays the graph in the same window one after other
For illustration, I have plotted two graphs with paramters year and views on X axis and Y axis each. Initially I have used closed function.it displayed the graphs in two seperate windows…
Afterwords, when I run the program with clf() it clears the graph and displays next one in same window i.e figure 1.
Here is the code snippet -
import matplotlib.pyplot as plt
year = [2001,2002,2003,2004]
Views= [12000,14000,16000,18000]
Views2 = [15000,1800,24000,84000]
plt.plot(year,Views)
plt.show()
plt.clf()
plt.plot(year,Views2)
plt.show()
plt.clf()
I'm not really new to matplotlib and I'm deeply ashamed to admit I have always used it as a tool for getting a solution as quick and easy as possible. So I know how to get basic plots, subplots and stuff and have quite a few code which gets reused from time to time...but I have no "deep(er) knowledge" of matplotlib.
Recently I thought I should change this and work myself through some tutorials. However, I am still confused about matplotlibs plt, fig(ure) and ax(arr). What is really the difference?
In most cases, for some "quick'n'dirty' plotting I see people using just pyplot as plt and directly plot with plt.plot. Since I am having multiple stuff to plot quite often, I frequently use f, axarr = plt.subplots()...but most times you see only code putting data into the axarr and ignoring the figure f.
So, my question is: what is a clean way to work with matplotlib? When to use plt only, what is or what should a figure be used for? Should subplots just containing data? Or is it valid and good practice to everything like styling, clearing a plot, ..., inside of subplots?
I hope this is not to wide-ranging. Basically I am asking for some advice for the true purposes of plt <-> fig <-> ax(arr) (and when/how to use them properly).
Tutorials would also be welcome. The matplotlib documentation is rather confusing to me. When one searches something really specific, like rescaling a legend, different plot markers and colors and so on the official documentation is really precise but rather general information is not that good in my opinion. Too much different examples, no real explanations of the purposes...looks more or less like a big listing of all possible API methods and arguments.
pyplot is the 'scripting' level API in matplotlib (its highest level API to do a lot with matplotlib). It allows you to use matplotlib using a procedural interface in a similar way as you can do it with Matlab. pyplot has a notion of 'current figure' and 'current axes' that all the functions delegate to (#tacaswell dixit). So, when you use the functions available on the module pyplot you are plotting to the 'current figure' and 'current axes'.
If you want 'fine-grain' control of where/what your are plotting then you should use an object oriented API using instances of Figure and Axes.
Functions available in pyplot have an equivalent method in the Axes.
From the repo anatomy of matplotlib:
The Figure is the top-level container in this hierarchy. It is the overall window/page that everything is drawn on. You can have multiple independent figures and Figures can contain multiple Axes.
But...
Most plotting occurs on an Axes. The axes is effectively the area that we plot data on and any ticks/labels/etc associated with it. Usually we'll set up an Axes with a call to subplot (which places Axes on a regular grid), so in most cases, Axes and Subplot are synonymous.
Each Axes has an XAxis and a YAxis. These contain the ticks, tick locations, labels, etc.
If you want to know the anatomy of a plot you can visit this link.
I think that this tutorial explains well the basic notions of the object hierarchy of matplotlib like Figure and Axes, as well as the notion of current figure and current Axes.
If you want a quick answer: There is the Figure object which is the container that wraps multiple Axes(which is different from axis) which also contains smaller objects like legends, line, tick marks ... as shown in this image taken from matplotlib documentation
So when we do
>>> import matplotlib.pyplot as plt
>>> fig, ax = plt.subplots()
>>> type(fig)
<class 'matplotlib.figure.Figure'>
>>> type(ax)
<class 'matplotlib.axes._subplots.AxesSubplot'>
We have created a Figure object and an Axes object that is contained in that figure.
pyplot is matlab like API for those who are familiar with matlab and want to make quick and dirty plots
figure is object-oriented API for those who doesn't care about matlab style plotting
So you can use either one but perhaps not both together.
I want to make an animated barchart in Python and save this animation in mp4 format. My problem is that the frames in the saved video overlay, although I use "blit=True" to tell the animation that only the things that change from frame to frame are drawn. Surprisingly, this problem does not occur in the built-in preview of Python.
Here is a minimal that reflects my situation:
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n): #That's the list of bars I want to display
C=[]
for k in range(1,6):
C.append(1/float(n*k))
return C
fig=plt.figure()
n=100 #Number of frames
def animate(i):
x=range(1,6)
y=barlist(i+1)
return plt.bar(x,y)
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=True,frames=n,
interval=50)
anim.save('barchart_animated_'+str(n)+'.mp4')
plt.show()
I must admit that I'm not pretty sure what I should do to remove this flaw. The only example I know of where the bars do not overlay in the frames is here (more exactly, I'm referring to the code of the first answer of the following link):
Dynamically updating a bar plot in matplotlib
It seems that I somehow have to tell the animation how it should set the height of each bar at each frame with the set_height-method. But as I said, I don't really know what's wrong in the above example. Thanks for any help!
Martin
The problem you have here is that you create a new barplot in every iteration of the animation. They will one by one be added to the plot, but since their height is shrinking over time, it may look as though only the first bar is present.
There are two ways to overcome this. First option is to clear the axes before plotting a new bar plot. This however will rescale the axis limits, which should then be constantly set to the same value.
The other option is to manipulate the one and only bar plot in the axes and adapt it's height for every frame. This is shown in the code below.
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n):
return [1/float(n*k) for k in range(1,6)]
fig=plt.figure()
n=100 #Number of frames
x=range(1,6)
barcollection = plt.bar(x,barlist(1))
def animate(i):
y=barlist(i+1)
for i, b in enumerate(barcollection):
b.set_height(y[i])
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=False,frames=n,
interval=100)
anim.save('mymovie.mp4',writer=animation.FFMpegWriter(fps=10))
plt.show()
Answers to the questions from the comments:
Blotting is a technique where all the parts of the figure which do not change are stored as a background. Then for each animated frame, only the changing parts are redrawn. This avoids the background to be redrawn from scratch and thus allows for much faster animations. Blitting will only affect the on-screen animation, because saving the animation to a file is not performed in real-time (and doesn't need to anyways).
Using blit=False here allows to make the code more simple because we do not need to care about the differences between the animation on screen and the one saved - they are just the same.
The enumerate function yields both the index as well as the object from the enumerated sequence. I did use it here, because it is a convenient way to obtain both in the same loop. It is not at all important here, you could alternatively do something like
for i in range(len(barcollection)):
barcollection[i].set_height(y[i])
programming noob here. I'm trying to use a matplotlib widget in a PyQt4 GUI. The widget is similar to matplotlib's example for qt.
At some point the user needs to click on the plot, which I thought something like ginput() would handle. However, this doesn't work because the figure doesn't have a manager (see below). Note that this is very similar to another question but it never got answered.
AttributeError: 'NoneType' object has no attribute 'manager'
Figure.show works only for figures managed by pyplot, normally created by pyplot.figure().
I'm assuming by "normally" there's a way around this.
Another simple script to demonstrate:
from __future__ import print_function
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 5, 0.1)
y = np.sin(x)
# figure creation by plt (also given a manager, although not explicitly)
plt.figure()
plt.plot(x,y)
coords = plt.ginput() # click on the axes somewhere; this works
print(coords)
# figure creation w/o plt
manualfig = Figure()
manualaxes = manualfig.add_subplot(111)
manualaxes.plot(x,y)
manualfig.show() # will fail because of no manager, yet shown as a method
manualcoords = manualfig.ginput() # comment out above and this fails too
print(manualcoords)
As popular as pyplot is (I can't hardly find an answer without it), it doesn't seem to play nice when working with a GUI. I thought pyplot was simply a wrapper for the OO framework but I guess I'm just a noob.
My question then is this:
Is there some way to attach pyplot to an instance of matplotlib.figure.Figure?
Is there an easy way to attach a manager to a Figure? I found new_figure_manager() in matplotlib.backends.backend_qt4agg, but couldn't get it to work, even if it is the right solution.
Many thanks,
James
pyplot is just a wrapper for the OO interface, but it does a lot of work for you read the example you link to again carefully, the
FigureCanvas.__init__(self, fig)
line is very important as that is what tells the figure what canvas to use. The Figure object is just a collection of Axes objects (and a few Text objects), the canvas object is what knows how to turn Artist objects (ie matplotlib's internal representation of lines, text, points, etc) in to pretty colors. Also see something I wrote for another embedding example which does not sub-class FigureCanvas.
There is a PR to make this process easier, but it is stalled while we get 1.4 out the door.
also see: Which is the recommended way to plot: matplotlib or pylab?, How can I attach a pyplot function to a figure instance?