I am trying to save a simple matplotlib animation from Jake Vanderplas, but I keep getting OSError: [Errno 13] Permission denied.
I should note that I made two small modifications to Jake Vanderplas's example. I installed ffmpeg from MacPorts so I added the line plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin' and I ran into the problem discussed in (Using FFmpeg and IPython), so I added FFwriter = animation.FFMpegWriter().
Here is the code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin'
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
FFwriter = animation.FFMpegWriter()
anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])
Here is the traceback:
File "ani_debug.py", line 34, in <module>
anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site- packages/matplotlib/animation.py", line 712, in save
with writer.saving(self._fig, filename, dpi):
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 169, in saving
self.setup(*args)
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 159, in setup
self._run()
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 186, in _run
stdin=subprocess.PIPE)
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/subprocess.py", line 709, in __init__
errread, errwrite)
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/subprocess.py", line 1326, in _execute_child
raise child_exception
OSError: [Errno 13] Permission denied
I have also tried using Spyder's built-in python and received a similar traceback. Any suggestions?
EDIT: I realized that I did not give the proper path to ffmpeg. Apparently, plt.rcParams['animation.ffmpeg_path'] does not work similar to PYTHONPATH. You must tell the animation module exactly where ffmpeg is with plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'.
Now, I get a movie file that will play, but the content is completely garbled. I can't tell what I am looking at.
Here is the traceback:
Exception in Tkinter callback
Traceback (most recent call last):
File "Tkinter.pyc", line 1470, in __call__
File "Tkinter.pyc", line 531, in callit
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer
TimerBase._on_timer(self)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer
ret = func(*args, **kwargs)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 876, in _step
still_going = Animation._step(self, *args)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 735, in _step
self._draw_next_frame(framedata, self._blit)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 753, in _draw_next_frame
self._pre_draw(framedata, blit)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 766, in _pre_draw
self._blit_clear(self._drawn_artists, self._blit_cache)
File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 806, in _blit_clear
a.figure.canvas.restore_region(bg_cache[a])
KeyError: <matplotlib.axes.AxesSubplot object at 0x104cfb150>
EDIT:
For some reason, everything is working fine now. I have tried things on my home computer and my work computer, and neither one can recreate the garbled video file I got after I fixed the ffmpeg path issue.
EDIT:
Aaaahaaa! I tracked this sucker down. Sometimes I would import a module that had plt.rcParams['savefig.bbox'] = 'tight' in it. (I would never use that module, but rcParams persist, until you restart your python interpreter.) That setting causes the video to come out all garbled. I will post my solution below.
So it turns out there were two issues.
Issue #1: The path to ffmpeg was wrong. I thought I needed to provide the path to the directory that ffmpeg resides in, but I needed to provide the path all the way to the ffmpeg binary.
Issue #2: Prior to testing out my code to generate videos, I sometimes would import a module with the setting plt.rcParams['savefig.bbox'] = 'tight'. (I did not think much of it, because I did not use the module, but rcParams persist until you restart the python interpreter.) This plt.rcParams['savefig.bbox'] = 'tight' causes the video file to save without any errors, but the frames are all garbled when you try to play the video. Although it took me all evening to track this down, it turns out this is a known issue.
Here is the updated solution that creates a video file for me with a nice, translating, sine wave.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
FFwriter = animation.FFMpegWriter(fps=30, extra_args=['-vcodec', 'libx264'])
anim.save('basic_animation.mp4', writer=FFwriter)
Thank you Stretch for your valuable answer. I found that mentioning extra args inside anim.save() results in error. Hence the code is updated as shown below,
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = r'I:\FFmpeg\bin\ffmpeg' #make sure you download FFmpeg files for windows 10 from https://ffmpeg.zeranoe.com/builds/
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
FFwriter=animation.FFMpegWriter(fps=30, extra_args=['-vcodec', 'libx264'])
anim.save(r'I:\Understanding_objective functions\test\basic_animation.mp4', writer=FFwriter)
plt.show()
Hope this might helps someone trying to save animation plots to .mp4 format.
Further to Stretch's answer, I found that some of the parameters being passed to anim.save() do not appear to achieve the desired effect. Specifically fps was 5 (default) and not the 30 being set. By passing fps=30 to animation.FFMpegWriter it does work.
So:
FFwriter = animation.FFMpegWriter(fps=30)
anim.save('basic_animation.mp4', writer=FFwriter, extra_args=['-vcodec', 'libx264'])
Note that the video is now 7 seconds long (200 frames # 30 fps) rather than 40 seconds (200 frames # 5 fps). Note also that a default of 5 fps corresponds to the default 200 ms/frame interval in FuncAnimation, and strictly speaking the 20 ms animation interval used here corresponds to 50 fps.
For those struggling to achieve better video quality, it is also possible to pass a bitrate (integer in kbps) to animation.FFMpegWriter, eg:
FFwriter = animation.FFMpegWriter(fps=30, bitrate=2000)
I tried various extra_args in an effort to achieve better quality, without much success.
I had garbling issues when I first (naively) tried to modify the working
example of Stretch's answer to show the graph in realtime (as well as keep the movie).
Not quite right mods of Stretch's answer (which worked for me)
plt.ion() interaction on
plt.draw() and plt.show() inside animate function, before return statent
frames=20, interval=200 to slow graph creation a bit, but still make a 4 second movie
Now plot shows up in a window as it is being created,
but the output movie is garbled.
Correct step 2:
2a: plt.draw() inside animate function
2b: plt.show() just after the animate function
Now movie plays back ungarbled.
Related
In matplotlib, the update_from method of a Line2D object can be used to copy properties from another line (see e.g. this answer). This is not working if the two lines live on different axes. The following code:
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
line2.update_from(line1)
raises
AttributeError: 'NoneType' object has no attribute 'extents'
while the traceback leaves me puzzled.
My questions are:
Why is this error raised?
How can I copy (all) Line2D properties of line1 to line2 instead?
EDIT
After a bit more testing I can say that the AttributeError above is for example raised in a Jupyter notebook session with the %matplotlib inline backend. With the %matplotlib notebook backend or in a regular Python script (e.g. with the "qt5agg" backend), the code passes without an error but line2 is "invisible" afterwards.
For completeness, the above image was created using (Anaconda) Python 3.7.9 and matplotlib 3.3.1 with:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use("qt5agg")
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
line2.update_from(line1)
plt.savefig("test.png")
The problem remains that I cannot copy the Line2D properties from line1 to line2.
EDIT 2
Throwing a plt.tight_layout() into the mix brings back the AttributeError.
EDIT 3
As requested in the comments, here is the traceback for the error I get with plt.tight_layout() (EDIT 2):
Traceback (most recent call last):
File "test.py", line 11, in <module>
plt.tight_layout()
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/cbook/deprecation.py", line 451, in wrapper
return func(*args, **kwargs)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/pyplot.py", line 1490, in tight_layout
gcf().tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
return func(*inner_args, **inner_kwargs)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/figure.py", line 2615, in tight_layout
pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/tight_layout.py", line 308, in get_tight_layout_figure
pad=pad, h_pad=h_pad, w_pad=w_pad)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/tight_layout.py", line 84, in auto_adjust_subplotpars
bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 4199, in get_tightbbox
if np.all(clip_extent.extents == axbbox.extents):
AttributeError: 'NoneType' object has no attribute 'extents'
It seems update_from updates too much, including the transformation and the clipbox. Maybe the error comes from the object being totally invisible after clipping to the wrong clipbox?
A workaround can be to save both before updating and setting them back:
from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
old_transform = line2.get_transform()
old_clipbox = line2.clipbox
line2.update_from(line1)
line2.set_transform(old_transform)
line2.clipbox = old_clipbox
plt.tight_layout()
plt.draw()
I'm trying to compile the code from the following tutorial: https://fluidenginedevelopment.org/documentation/python.html
Here is the tutorial code:
from pyjet import *
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
ANIM_NUM_FRAMES = 360
ANIM_FPS = 60
def main():
# Create smoke solver
resX = 100
solver = GridSmokeSolver2(resolution=(resX, 2 * resX), domainSizeX=1.0)
# Customize pressure solver for real-time sim (less accurate, but much faster)
pressureSolver = GridFractionalSinglePhasePressureSolver2()
pressureSolver.linearSystemSolver = FdmGaussSeidelSolver2(20, 20, 0.001)
solver.pressureSolver = pressureSolver
# Setup emitter
sphere = Sphere2(center=(0.5, 0.5), radius=0.15)
emitter = VolumeGridEmitter2(sourceRegion=sphere)
solver.emitter = emitter
emitter.addStepFunctionTarget(solver.smokeDensity, 0.0, 1.0)
emitter.addStepFunctionTarget(solver.temperature, 0.0, 1.0)
# Visualization
fig = plt.figure()
den = np.array(solver.smokeDensity.dataAccessor(), copy=False)
im = plt.imshow(den, vmin=0, vmax=1, cmap=plt.cm.gray,
interpolation='bicubic', animated=True, origin='lower')
# Animation
frame = Frame(0, 1.0 / ANIM_FPS)
def updatefig(*args):
solver.update(frame)
frame.advance()
den = np.array(solver.smokeDensity.dataAccessor(), copy=False)
im.set_data(den)
return im,
if len(sys.argv) > 1:
format = sys.argv[1]
if format == 'gif':
anim = animation.FuncAnimation(fig, updatefig,
frames=ANIM_NUM_FRAMES,
interval=ANIM_FPS, blit=True)
anim.save('smoke_example01.gif', fps=ANIM_FPS, dpi=72,
writer='imagemagick')
elif format == 'mp4':
anim = animation.FuncAnimation(fig, updatefig,
frames=ANIM_NUM_FRAMES,
interval=ANIM_FPS, blit=True)
anim.save('smoke_example01.mp4', fps=ANIM_FPS, writer='ffmpeg')
else:
anim = animation.FuncAnimation(fig, updatefig, frames=ANIM_NUM_FRAMES,
interval=1, blit=True)
plt.show()
if __name__ == '__main__':
Logging.mute()
main()
before I'll change it to fit the logo I'm making. I've installed all of the required libraries (in pycharm), and I get the following error while trying to compile it:
/home/kali/PycharmProjects/AndromedaLogo/venv/bin/python /home/kali/PycharmProjects/AndromedaLogo/main.py
Traceback (most recent call last):
File "/home/kali/PycharmProjects/AndromedaLogo/main.py", line 1, in
from pyjet import *
TypeError: Item in pyjet.all must be str, not dtype
What seems to be the problem?
I think there's an issue with the library you are currently using. The issue is occuring at import time, which is before any of your code is running. The problem seems to be related to some initialization issue.
Specifically the reference the pyjet.all items must be strings instead of dtype makes me assume that for some reason a numpy array is being pass where it should've been a normal list of strings.
TypeError: Item in pyjet.all must be str, not dtype
I would suggest removing the software you installed and making sure you install the correct version (the one for which the tutorial is written).
I'm trying to plot the time evolution of a function f(x,t). The data is stored in a file which has the following format:
1st row:f(0,0) f(0,1) f(0,2) ....f(0,N)
2nd row:f(1,0) f(1,1) f(1,2) ....f(1,N)
Mth row:f(M,0) f(M,1) f(M,2) ....f(M,N)
where N is the no: of points of the simulation box and M is the number of timesteps.
I used basic_animation by Jake Vanderplas (https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/) to start with, the original example works fine as long as i put blit=False.
Then i tried to replace x by :
x= np.arange(0,192)
and y by the contents of the file mentioned above.
If i do just plt.plot(x,y), it does plot f(x,t) at a given time t, but I want the animation of f(x,t) in time.
set_data should accept 2 1Darrays and I've checked that len(x)=len(y).
But I get the following error message:
'RuntimeError: xdata and ydata must be the same length'
This is the code (in the future i would like to plot multiple functions):
"""
Modified Matplotlib Animation Example
original example:
email: vanderplas#astro.washington.edu
website: http://jakevdp.github.com
license: BSD
Feel free to use and modify this, but keep the above information.
"""
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import islice
filename = 'DensityByPropagation__a_0_VHxcS_kick'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 192), ylim=(-2, 2))
lineS, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
w = np.linspace(0, 2, 1000)
z = np.sin(2 * np.pi * (w - 0.01 * i))
x= np.arange(0,192)
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
#plt.plot(x,y)
#plt.show()
#print len(x)
#print len(y)
#lineS.set_data(w,z)
lineS.set_data(x,y)
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=False)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieJoh.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
I'm not sure what exactly is causing your error, but let me point something out, then I'll make a toy example that should help clarify what's happening.
These lines seem unnecessarily complicated.
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
If your data is in fact in the simple format you stated, i.e., values separated by spaces, np.loadtxt() would load all the values into an easy to manage array.
Example
Lets assume this is your data file (10 time steps, 2 points on plot at each step):
0 0 0 0 0 0 0 0 0 0
9 8 7 6 5 4 3 2 1 0
Now some code:
filename = 'data.txt'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 1), ylim=(0, 9))
lineS, = ax.plot([], [], lw=2)
x = range(2) # the domain
Now we load the data in with np.loadtxt(), this creates a 2-d matrix with t in the columns and x in the rows. We then transpose it to make indexing each time step possible in animate().
# load the data from file
data = np.loadtxt(filename)
# transpose so we could easily index in the animate() function
data = np.transpose(data)
Now for animation functions. This part is really quite simple. animate(i) takes one argument - the frame number. Using the frame number, we extract the values of f(x,t=frameNumber) and set that as the data on the plot.
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
lineS.set_data(x, data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=10, interval=100, blit=True)
plt.show()
This is the working code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Input
filename = 'PeriodicDensity' # Filename
x = np.linspace(-7.5,7.5,192) # Domain
xLimits=[-7.5,7.5] # x limits
yLimits=[0,1] # y limits
framesToUse = range(1,9000,150)# The time-steps to plot
# load the data from file
data = np.loadtxt(filename)
# Set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=xLimits,ylim=yLimits)
lineS, = ax.plot([], [], lw=2)
# Initialisation function
def init():
lineS.set_data([],[])
return lineS,
# Animation function (called sequentially)
def animate(i):
lineS.set_data(x,data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,interval=1000, frames=framesToUse, blit=True)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieDensity.mp4', fps=1, extra_args=['-vcodec', 'libx264'])
plt.show()
Here my code stripped down to its bare minimal to correctly reproduce my problem.
import matplotlib.animation as animation
import os
import matplotlib.pyplot as plt
import numpy as np
import os.path
f, ((ax1, ax2)) = plt.subplots(1, 2, figsize=(20,10))
def animate(i):
chosenEnergy = (0.0 + (i-1)*(0.02))
chosenEnergyLine = ax2.axvline(float(chosenEnergy),0,1, linestyle='dashed')
return chosenEnergyLine,
def init():
chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
return chosenEnergyLine,
ani = animation.FuncAnimation(f, animate, np.arange(1,nE), init_func=init,
interval=25, blit=False, repeat = False)
plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'
FFwriter = animation.FFMpegWriter()
ani.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])
print "finished"
The problem is that I want to new vertical line (at a different energy) to replace the old line. Instead, the final frame shows all lines.
I found a similar question (matplotlib circle, animation, how to remove old circle in animation), but does not seem to apply for my case.
Even if a similar function as the one used in the answer to this similar question (set_radius) exists for xvline, I'd prefer not to use it. In my other subplot (ax1), I have a scatter plot that will have to be updated each time. Is there a general way to clear the plot before the next time step?
I also didn't notice any change when using blit=False or blit=True.
Any suggestion on how to proceed?
Each time you call animate you're drawing a new line. You should either delete the old lines, or move your line with set_xdata instead of drawing a new one.
def animate(i,chosenEnergyLine):
chosenEnergy = (0.0 + (i-1)*(0.02))
chosenEnergyLine.set_xdata([chosenEnergy, chosenEnergy])
return chosenEnergyLine,
chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
ani = animation.FuncAnimation(f, animate, np.arange(1,10),
fargs=(chosenEnergyLine,), interval=25, blit=False, repeat = False)
UPDATE: That's because you're trying to delete the same global chosenEnergyLine multiple times (FuncAnimation catches the return value of animate but it does not update the global chosenEnergyLine with it). The solution is to use kind of a static variable in animate to keep track of the latest chosenEnergyLine
def animate(i):
chosenEnergy = (0.0 + (i-1)*(0.02))
animate.chosenEnergyLine.remove()
animate.chosenEnergyLine = ax2.axvline(float(chosenEnergy),0,1, linestyle='dashed')
return animate.chosenEnergyLine,
animate.chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
ani = animation.FuncAnimation(f, animate, np.arange(1,10),
interval=25, blit=False, repeat = False)
I have a fairly simple plotting routine that looks like this:
from __future__ import division
import datetime
import matplotlib
matplotlib.use('Agg')
from matplotlib.pyplot import figure, plot, show, legend, close, savefig, rcParams
import numpy
from globalconstants import *
def plotColumns(columnNumbers, t, out, showFig=False, filenamePrefix=None, saveFig=True, saveThumb=True):
lineProps = ['b', 'r', 'g', 'c', 'm', 'y', 'k', 'b--', 'r--', 'g--', 'c--', 'm--', 'y--', 'k--', 'g--', 'b.-', 'r.-', 'g.-', 'c.-', 'm.-', 'y.-', 'k.-']
rcParams['figure.figsize'] = (13,11)
for i in columnNumbers:
plot(t, out[:,i], lineProps[i])
legendStrings = list(numpy.zeros(NUMCOMPONENTS))
legendStrings[GLUCOSE] = 'GLUCOSE'
legendStrings[CELLULOSE] = 'CELLULOSE'
legendStrings[STARCH] = 'STARCH'
legendStrings[ACETATE] = 'ACETATE'
legendStrings[BUTYRATE] = 'BUTYRATE'
legendStrings[SUCCINATE] = 'SUCCINATE'
legendStrings[HYDROGEN] = 'HYDROGEN'
legendStrings[PROPIONATE] = 'PROPIONATE'
legendStrings[METHANE] = "METHANE"
legendStrings[RUMINOCOCCUS] = 'RUMINOCOCCUS'
legendStrings[METHANOBACTERIUM] = "METHANOBACTERIUM"
legendStrings[BACTEROIDES] = 'BACTEROIDES'
legendStrings[SELENOMONAS] = 'SELENOMONAS'
legendStrings[CLOSTRIDIUM] = 'CLOSTRIDIUM'
legendStrings = [legendStrings[i] for i in columnNumbers]
legend(legendStrings, loc='best')
dt = datetime.datetime.now()
dtAsString = dt.strftime('%d-%m-%Y_%H-%M-%S')
if filenamePrefix is None:
filenamePrefix = ''
if filenamePrefix != '' and filenamePrefix[-1] != '_':
filenamePrefix += '_'
if saveFig:
savefig(filenamePrefix+dtAsString+'.eps')
if saveThumb:
savefig(filenamePrefix+dtAsString+'.png', dpi=300)
if showFig: f.show()
close('all')
When I plot this in single iterations, it works fine. However, the moment I put it in a loop, matplotlib throws a hissy fit...
Traceback (most recent call last):
File "c4hm_param_variation_h2_conc.py", line 148, in <module>
plotColumns(columnNumbers, timeVector, out, showFig=False, filenamePrefix='c
4hm_param_variation_h2_conc_'+str(hydrogen_conc), saveFig=False, saveThumb=True)
File "D:\phdproject\alexander paper\python\v3\plotcolumns.py", line 48, in plo
tColumns
savefig(filenamePrefix+dtAsString+'.png', dpi=300)
File "C:\Python25\lib\site-packages\matplotlib\pyplot.py", line 356, in savefi
g
return fig.savefig(*args, **kwargs)
File "C:\Python25\lib\site-packages\matplotlib\figure.py", line 1032, in savef
ig
self.canvas.print_figure(*args, **kwargs)
File "C:\Python25\lib\site-packages\matplotlib\backend_bases.py", line 1476, i
n print_figure
**kwargs)
File "C:\Python25\lib\site-packages\matplotlib\backends\backend_agg.py", line
358, in print_png
FigureCanvasAgg.draw(self)
File "C:\Python25\lib\site-packages\matplotlib\backends\backend_agg.py", line
314, in draw
self.figure.draw(self.renderer)
File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
draw(artist, renderer, *kl)
File "C:\Python25\lib\site-packages\matplotlib\figure.py", line 773, in draw
for a in self.axes: a.draw(renderer)
File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
draw(artist, renderer, *kl)
File "C:\Python25\lib\site-packages\matplotlib\axes.py", line 1735, in draw
a.draw(renderer)
File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
draw(artist, renderer, *kl)
File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 374, in draw
bbox = self._legend_box.get_window_extent(renderer)
File "C:\Python25\lib\site-packages\matplotlib\offsetbox.py", line 209, in get
_window_extent
px, py = self.get_offset(w, h, xd, yd)
File "C:\Python25\lib\site-packages\matplotlib\offsetbox.py", line 162, in get
_offset
return self._offset(width, height, xdescent, ydescent)
File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 360, in findof
fset
return _findoffset(width, height, xdescent, ydescent, renderer)
File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 325, in _findo
ffset_best
ox, oy = self._find_best_position(width, height, renderer)
File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 817, in _find_
best_position
verts, bboxes, lines = self._auto_legend_data()
File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 669, in _auto_
legend_data
tpath = trans.transform_path(path)
File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1911, in t
ransform_path
self._a.transform_path(path))
File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1122, in t
ransform_path
return Path(self.transform(path.vertices), path.codes,
File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1402, in t
ransform
return affine_transform(points, mtx)
MemoryError: Could not allocate memory for path
This happens on iteration 2 (counting from 1), if that makes a difference. The code is running on Windows XP 32-bit with python 2.5 and matplotlib 0.99.1, numpy 1.3.0 and scipy 0.7.1.
EDIT: The code has now been updated to reflect the fact that the crash actually occurs at the call to legend(). Commenting that call out solves the problem, though obviously, I would still like to be able to put a legend on my graphs...
Is each loop supposed to generate a new figure? I don't see you closing it or creating a new figure instance from loop to loop.
This call will clear the current figure after you save it at the end of the loop:
pyplot.clf()
I'd refactor, though, and make your code more OO and create a new figure instance on each loop:
from matplotlib import pyplot
while True:
fig = pyplot.figure()
ax = fig.add_subplot(111)
ax.plot(x,y)
ax.legend(legendStrings, loc = 'best')
fig.savefig('himom.png')
# etc....
I've also run into this error. what seems to have fixed it is
while True:
fig = pyplot.figure()
ax = fig.add_subplot(111)
ax.plot(x,y)
ax.legend(legendStrings, loc = 'best')
fig.savefig('himom.png')
#new bit here
pylab.close(fig) #where f is the figure
running my loop stably now with fluctuating memory but no consistant increase
Answer from ninjasmith worked for me too - pyplot.close() enabled my loops to work.
From the pyplot tutorial, Working with multiple figures and axes:
You can clear the current figure with clf() and the current
axes with cla(). If you find this statefulness, annoying, don’t
despair, this is just a thin stateful wrapper around an object
oriented API, which you can use instead (see Artist tutorial)
If you are making a long sequence of figures, you need to be aware of
one more thing: the memory required for a figure is not completely
released until the figure is explicitly closed with close(). Deleting
all references to the figure, and/or using the window manager to kill
the window in which the figure appears on the screen, is not enough,
because pyplot maintains internal references until close() is called.
In my case, matplotlib version 3.5.0, As Hui Liu san says,
Following method can keep memory usage low
import matplotlib
print(matplotlib.__version__) #'3.5.0'
import matplotlib.pyplot as plt
plt.savefig('your.png')
# Add both in this order for keeping memory usage low
plt.clf()
plt.close()
I had a similar issue when I was using it from jupyter, putting plt.clf() and plt.close() in the loop did not work.
But this helped:
import matplotlib
matplotlib.use('Agg')
This disables interactive backend for matplotlib.