matplotlib - plt.figure() freezes - python

I have function that renders some plot and then saves it to png file. Simplified code:
def render_plot(self, parameter1, parameter2):
dates = get_my_dates()
values = get_my_values()
fig = plt.figure() # freezes here when calling render_plot for the 2nd or 3rd time!
ax = fig.add_subplot(111)
... # performing some calculations and drawing plots
ax.plot_date(dates, values, '-', marker='o')
plt.savefig("media/plot.png")
plt.cla()
plt.clf()
plt.close()
Function freezes at line "fig = plt.figure()" (100% CPU usage - infinite loop?) but only when calling function 2nd or 3rd time, works fine for the first time and rendering good looking plot. What could be the reason?

fig = plt.figure() will cause the freeze for my PyQt5 as well.
I don't exactly know the reasons, but I have found a nice workaround that works for me.
Workaround:
from matplotlib. Figure import Figure
fig1 = Figure()
ax1 = fig1.add_subplot()

This is probably not the reason but first, you do not need
ax=fig.add_subplot(111)
Try just
ax = plt.gca()
Then, comment
plt.close()
It may help. Just a guess.

Related

Matplotlib ArtistAnimation: Plot entire figure in each step

I have an existing function I use for plotting, which I call repeatedly in my program.
I want to use matplotlib's ArtistAnimation to save each plot as an "artist" that is shown in one step of the animation.
I know how to use ArtistAnimation to show individual elements of the plot in the animation, but not the entire plot.
Here's a simplified example:
import random
def my_plot():
fig, ax = plt.subplots()
ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
plt.show()
return ax
ims = []
fig = plt.figure()
for _ in range(5):
ax = my_plot()
ims.append((ax,))
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
This runs without error, but the resulting video is just blank. The same happens if I return a list of the artists created by ax.plot().
I assume the problem is that I'm calling plt.figure/plt.subfigure multiple times. But I'm not sure how to avoid that. Do I need to create one figure up front and pass that to each call of my_plot? Seems a bit ugly.
Instead of saving the axes, you need to save the plots as a list. (Or maybe you don't want to do this and want to save the axes? If that's the case, let me know and I'll delete this. I don't think saving the axes will work though, since the animation works by setting the saved items within a figure visible and invisible, and neither the axes nor the figure will hide/reveal a subset of the plots for each frame in this way.)
import matplotlib.pyplot as plt
from matplotlib import animation
import random
def my_plot(ax):
p0, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
p1, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
return [p0, p1] # return a list of the new plots
ims = []
fig = plt.figure()
ax = fig.add_subplot(111) # fig and axes created once
for _ in range(10):
ps = my_plot(ax)
ims.append(ps) # append the new list of plots
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
GIF below, but here is some vertical spacing so you can scroll the annoying flashing lines of the page while reading the code
. . . . . . . . . . . . . .
Thanks to tom's answer, I found the main reasons why my animations didn't work and only showed the first frame: I called plt.show() in each iteration. Apparently, after the first call, the animations stop working. Removing plt.show() and only creating one figure solved the problem:
import matplotlib.pyplot as plt
from matplotlib import animation
import random
def my_plot():
patch = []
patch.extend(plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
patch.extend(plt.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
# no plt.show() here!
return patch
ims = []
fig = plt.figure() # fig created only once
for _ in range(10):
patch = my_plot()
ims.append(patch)
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})
Not sure how I could both plot and show the plots directly and create an animation. Maybe using plt.draw() instead? But that doesn't show anything in my PyCharm IDE...
Anyways, I can live with either or.

Python Matplotlib Update Plot in the Background

I am using Matplotlib to plot a real time event in Anaconda prompt.
When I update plot by plt.draw() or plt.show(), I loose control of the thing I am doing. Plot window acts like its clicked and this blocks my other control on the command prompt.
I tried adding
plt.show(block=False)
but it didnt help.
The code is like below,
fig, ax = plt.subplots()
plt.ion()
plt.show(block=False)
while(True):
ax.plot(y_plt_points,x_plt_points,'ro')
plt.draw()
plt.pause(0.01)
This link has an example of real time plotting with matplotlib. I think the main takeaway is that you don't need to use plt.show() or plt.draw() on every call to plot. The example uses set_ydata instead. Simalarly set_xdata can be used to update your x_axis variables. Code below
import matplotlib.pyplot as plt
import numpy as np
# use ggplot style for more sophisticated visuals
plt.style.use('ggplot')
def live_plotter(x_vec,y1_data,line1,identifier='',pause_time=0.1):
if line1==[]:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
fig = plt.figure(figsize=(13,6))
ax = fig.add_subplot(111)
# create a variable for the line so we can later update it
line1, = ax.plot(x_vec,y1_data,'-o',alpha=0.8)
#update plot label/title
plt.ylabel('Y Label')
plt.title('Title: {}'.format(identifier))
plt.show()
# after the figure, axis, and line are created, we only need to update the y-data
line1.set_ydata(y1_data)
# adjust limits if new data goes beyond bounds
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1
When I run this function on the example below I don't have any trouble using other applications on my computer
size = 100
x_vec = np.linspace(0,1,size+1)[0:-1]
y_vec = np.random.randn(len(x_vec))
line1 = []
i=0
while i<1000:
i=+1
rand_val = np.random.randn(1)
y_vec[-1] = rand_val
line1 = live_plotter(x_vec,y_vec,line1)
y_vec = np.append(y_vec[1:],0.0)
I think this is what you are looking for.
I had a similar issue, fixed it by replacing:
plt.pause(0.01)
with
fig.canvas.flush_events()
A more detailed explanation found here:
How to keep matplotlib (python) window in background?

MatPlotLib's ion() and draw() not working

I am trying to plot figures in real time using a for loop. I have the following simple code:
import matplotlib.pyplot as plt
plt.ion()
plt.figure()
for i in range(100):
plt.plot([i], [i], 'o')
plt.draw()
plt.pause(0.0001)
This code does not show the figure until it has finished computing, which I don't want. I want it to draw the figure after every loop. If I replace plt.draw() with plt.show, multiple figures are output in real time, but I want them all to appear in the same figure. Any ideas?
EDIT:
I downloaded PyCharm with Anaconda and everything works fine. I guess it's a problem with Spyder since I tried a few different versions of it without success. If anyone has any clue what is causing this problem in Spyder, let me know!
Adapted for your case from : Python realtime plotting
import matplotlib.pyplot as plt
import numpy as np
import time
fig = plt.figure()
ax = fig.add_subplot(111)
# some X and Y data
x = [0]
y = [0]
li, = ax.plot(x, y,'o')
# draw and show it
fig.canvas.draw()
plt.show(block=False)
# loop to update the data
for i in range(100):
try:
x.append(i)
y.append(i)
# set the new data
li.set_xdata(x)
li.set_ydata(y)
ax.relim()
ax.autoscale_view(True,True,True)
fig.canvas.draw()
time.sleep(0.01)
except KeyboardInterrupt:
plt.close('all')
break
This solution example has worked for me on multiple machines. Try adjusting plt.pause(...)
import matplotlib.pyplot as plt
import numpy as np
F = lambda x: np.sin(2*x)
plt.ion()
x = np.linspace(0, 1, 200)
plt.plot(x, F(x))
for i in range(100):
if 'ax' in globals(): ax.remove()
newx = np.random.choice(x, size = 10)
ax = plt.scatter(newx, F(newx))
plt.pause(0.05)
plt.ioff()
plt.show()
Hey I was having the same problem, I checked other questions and my issue was solved when I plugged a pause into my solution. Here's some example code that worked for me.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.arange(0, 4*np.pi, 0.1)
y = [np.sin(i) for i in x]
plt.plot(x, y, 'g-', linewidth=1.5, markersize=4)
plt.pause(0.0001)
plt.plot(x, [i**2 for i in y], 'g-', linewidth=1.5, markersize=4)
plt.pause(0.0001)
plt.plot(x, [i**2*i+0.25 for i in y], 'r-', linewidth=1.5, markersize=4)
plt.pause(0.0001)
The solution was posted here:
Matplotlib ion() and subprocesses
The problem - and the solution - is highly dependent on the plot.draw() function within the Python environment and back end, and may even vary in different product releases. It manifests itself in different ways depending on the environment. The problem shows up in many places on stackoverflow with some solutions working for some people and not for others.
The gold standard on my Windows laptop is running the Python from the command line - no IDE, just plain vanilla Python3. draw() as shown in the example always works fine there.
If I try it in Jupyter notebook on the same machine, no amount of draw(), plot.pause(), plot.show(), or any other suggestion works. I tried %matplotlib with notebook, widget and ipympl. Nothing gets drawn until complete end of cell code execution.
Some other sources on stackoverflow suggested using figure.canvas.flush_events(). I had some success with that and investigated further.
The best solution turned out to be to run the draw() at the figure.canvas level instead of the axes or plot level.
You can get the figure by creating your plot with command:
fig, graph, = plt.subplots()
or, if you've already created the plot, as in the code at the top of the ticket, put the following outside the loop:
fig = plt.gcf() #get current figure
Inside the loop, instead of plt.draw(), use
fig.canvas.draw()
It's proven reliable in my Jupyter Notebook environment even when running multiple axes/plots across multiple figures. I can drop in sleep() statements and everything appears when expected.
Your mileage may vary.

Python (Matplotlib) - show multiple figures (plots) with an x and/or y offset (so without overlapping)

I'm trying to show multiple figures at once, but with an offset so I don't have to move the first figure to check that it showed all the figures (plots).
So here's an example:
from pylab import *
figure(0)
plot()
figure(1)
plot()
show()
These figures are shown on top of each other, but I want them to look like this when I run my program:
EDIT:
Any suggestions?
I usually do this with Figure.add_subplot:
fig = figure(0)
ax = fig.add_subplot(211)
ax.plot(...)
ax = fig.add_subplot(212)
ax.plot(...)
show()
If you're wondering what the magic 211 and 212 mean, see this question.
If you're using the tkagg backend, you can do:
import matplotlib.pyplot as plt
for i in range(5):
fig = plt.figure()
fig.canvas._tkcanvas.master.geometry('800x600+{:d}+{:d}'.format(70*i,70*i))
plt.show()
I think that the same treatment could be used for others backends...
Regards

Figure GUI freezing

I am fairly new in python, and I am trying to have a plot, based on data stored in a file. This file may be updated at any time, so I am trying to make the drawing updated every 3 seconds (so I don't use all the CPU). My problem is that the GUI freezes after the lunch.
#!/usr/bin/python
# _*_ coding: utf8 _*_
import matplotlib.pyplot as plt
import numpy as np
import time
plt.ion()
plt.figure()
i=0
while 1:
taille=0
fichier=np.loadtxt('data/US.SAVE')
fichier1=np.loadtxt('data/cond.SAVE')
taille1=np.size(fichier1[:,1])
taille=np.size(fichier[:,1])
min=min(fichier[0,0],fichier1[0,0]);
fichier[:,0]=fichier[:,0]-min
fichier1[:,0]=fichier1[:,0]-min
if (taille != taille1) :
printErrors("TAILLE DE FICHIERS DIFFERENTES")
nb_chunks=np.size(fichier1[1,:])
nb_inputs=np.size(fichier[1,:])
plt.subplot(3,1,1)
plt.bar(fichier[:,0],fichier[:,1],align='center',width=0.0001, facecolor='b', label="US")
x1,x2,y1,y2 = plt.axis()
x1=x1-0.0001
plt.axis([x1, x2, y1, 1.2])
plt.legend(ncol=3,prop={'size':9})
plt.title("US ")
plt.ylabel('Activation')
plt.xlabel('Time')
plt.subplot(3,1,2)
plt.bar(fichier1[:,0],fichier1[:,1],align='center',width=0.0001, facecolor='b', label="response")
plt.axis([x1, x2, y1, 1.2])
plt.legend(ncol=3,prop={'size':9})
plt.title("Response ")
plt.ylabel('Activation')
plt.xlabel('Time')
plt.subplot(3,1,3)
plt.bar(fichier[:,0]-fichier1[:,0],fichier1[:,1],align='center',width=0.0001, facecolor='b', label="Error")
plt.axis([x1, x2, y1, 1.2])
plt.legend(ncol=3,prop={'size':9})
plt.title("Error")
plt.ylabel('Activation')
plt.xlabel('Time')
plt.draw()
name1='data/Conditionnement.eps'
plt.savefig(name1,dpi=256)
plt.draw()
del fichier,fichier1,min
i=i+1
time.sleep(3)
plt.show()
I did not find any other topic on a file based drawing.
You want to use the plt.pause(3) function instead of time.sleep(). pause includes the necessary calls to the gui main loop to cause the figure to re-draw.
also see: Python- 1 second plots continous presentation, matplotlib real-time linear line, pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs,
On top of the answer of #tcaswell (that solve the problem), I suggest to rethink the script in a more OO way.
I have tried this:
plt.ion()
plt.figure()
plt.show()
while True:
x=np.arange(10)
y=np.random.rand(10)
plt.subplot(121)
plt.plot(x,y)
plt.subplot(122)
plt.plot(x,2*y)
plt.draw()
plt.pause(3)
but it does not work (it looks like it opens a gui at plt.figure and then at each loop.
A solution like this:
plt.ion()
fig, ax = plt.subplots(nrows=2, ncols=1)
plt.show()
while True:
x=np.arange(10)
y=np.random.rand(10)
ax[0].plot(x,y)
ax[1].plot(x,2*y)
plt.draw()
plt.pause(3)
is much more efficient (axes are created only once), neater (at the end matplotlib is OO) and potentially less prone to memory leaks.
Besides, from your most I gather that at each loop you read in the files again and then plot the new lines. If this is the case, you want to clear first the content of the axes before redrawing. In my simple case you can clear the axes with
for a in ax:
a.clear()

Categories