FuncAnimation doesn't display animation - python

I have code that is running on a different machine.
%matplotlib widget
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
n = 100
x = np.random.randn(n)
def update(curr):
if curr == n:
a.event_source.stop()
plt.cla()
bins = np.arange(-4, 4, 0.5)
plt.hist(x[:curr], bins=bins)
plt.axis([-4,4,0,30])
plt.gca().set_title('Sampling the Normal Distribution')
plt.gca().set_ylabel('Frequency')
plt.gca().set_xlabel('Value')
plt.annotate('n = {}'.format(curr), [3,27])
a = animation.FuncAnimation(plt.figure(), update, interval=100)
plt.show()
However, it gives me this every time
And some times: "UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. anim, that exists until you output the Animation using plt.show() or anim.save()."
I installed ipympl, restarted kernel, IDE, computer, removed "%matplotlib widget", but all this didn't help.
I hope you'll give me a hand
UPDATE:
I checked several examples of working code and found out 2 things:
All code including this one generates the correct animation, if you save it in any format (mp4, html), you can see it
If the animation is saved, then plt.show() will show the last frame, if not, then as in the picture above

I was having the same problem, and the only workaround I've found is to save the animation and then read it back from the file:
from IPython.display import Image
f = r'test_animation.gif'
writergif = animation.PillowWriter(fps=10)
a.save(f,writer=writergif)
plt.close()
Image(open('test_animation.gif','rb').read())

Related

why can´t I run my animation in python using jupiter´s notebook in vsc?

What´s up everyone!
Ultimately, I have been working with python,learning the basics of animations using matplotlib.
The code is quite simple:
import random
from itertools import count
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
plt.style.use('fivethirtyeight')
x_vals = []
y_vals = []
index = count()
def animate(i):
x_vals.append(next(index))
y_vals.append(random.randint(0, 5))
plt.cla() # cla: clear axis
plt.plot(x_vals,y_vals)
ani = FuncAnimation(plt.gcf(),animate, interval=1000)
plt.tight_layout()
plt.show()
When I compile the code, no error is found but for this comment:
/home/der/.local/lib/python3.8/site-packages/matplotlib/animation.py:973: UserWarning: Animation was deleted without rendering anything. This is most likely unintended. To prevent deletion, assign the Animation to a variable that exists for as long as you need the Animation.
warnings.warn(
<Figure size 432x288 with 0 Axes>
How can I fix it? Your help is invaluable to me since I have been scrumbling for a week.

How can I save the .svg output from an interactive() display with a "Save As" prompt?

I am an artist trying to wrap my head around generative/procedural design using NumPy and various assorted tools in jupyter notebook.
I have some code https://github.com/GreySoulX/Circle-generator/blob/main/BrokenCircles.ipynb (see below) that will generate a number of concentric circles of random radius and output them as SVG code. I can get it to display, and I can even get the SVG output with the basic code, but when put it all in a functions and call it with interactive() my saved files come out empty rather that what is shown in my notebook with widgets.VBox() .
Where can I fix this? Am I just missing this by a million miles?
import numpy as np
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt
from IPython.display import display, Markdown, clear_output
from ipywidgets import widgets
from ipywidgets import interact, interact_manual, interactive, Button
def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):
x_bounds = [-10, 10]
y_bounds = [-10, 10]
circles = []
for i in range(n_circles):
c = np.array([0, 0])
r = np.unique(np.sort(np.round(np.random.uniform(min_radius,max_radius,1),2)))
circles.append((c, r))
circles
circle_patches = []
for c, r in circles:
circle_patches.append(mpatches.Circle(c, r, fill=None, edgecolor='black'))
fig, ax = plt.subplots(figsize=(20, 20))
if not debug:
plt.grid(False)
plt.axis('off')
ax.set_aspect('equal')
ax.set_xlim(x_bounds)
ax.set_ylim(y_bounds)
collection = PatchCollection(circle_patches, match_original=True)
ax.add_collection(collection)
return fig, ax
w = interactive(make_circles,
n_circles=(1,300,1),
min_radius=(0.00, 2.0, 0.1),
max_radius=(2.0, 20, 0.5))
#------Button----------
button = widgets.Button(description='Save as...')
out = widgets.Output()
def on_button_clicked(_):
#link function with output
with out:
#what happens when we hit button
#clear_output()
print('Saving Files...')
plt.savefig('SomeFile.svg', bbox_inches = 'tight', pad_inches = 0)
plt.savefig('SomeFile.png', bbox_inches = 'tight', pad_inches = 0)
# linking button and function together using a button's method
button.on_click(on_button_clicked)
# displaying Circles and button
widgets.VBox([button,out,w])
#------End Button------------
What's happening
This is an issue with how the inline backend handles closing figures and what plt.savefig does internally. The way static figures are displayed in notebooks (i.e. when not using ipympl) is that at the end of cell execution (or in this case at the end of the callback from the slider) the current figure is closed and displayed.
However plt.savefig expects there to be a currently open figure as it calls plt.gcf (get current figure) internally which either grabs the most recently active figure or creates a new empty figure if no figures are active.
So when you did this not in functions the figure wasn't closed until the cell was finished executing and so plt.savefig was able to find the figure. However when you moved to functions it was no longer able to find the current figure.
There are two basic solutions to this.
Solutions
1. Global fig
You can lift figure to the global scope and use fig.savefig - this makes sure that both the plot updating method and the saving method are refering to the same fig.
def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):
global fig
...
fig, ax = plt.subplots()
....
def on_button_clicked(_):
global fig
...
fig.savefig('SomeFile.svg', bbox_inches = 'tight', pad_inches = 0)
2 - interactive backend
Use one of the interactive backends such as %matplotlib qt or %matplotlib ipympl. If you are working in a notebook or jupyterlab then i'd recommend ipympl which you can install with pip install ipympl.
With these backends the same closing of the figure does not automatically happen, so you can structure your code like this:
%matplotlib ipympl
fig, ax = plt.subplots()
def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):
ax.cla() # clear all the artists off the axes
...
# use the same axes for `add_collection`
def on_button_clicked(_):
# you can now use `plt.savefig` but I would still recommend using `fig.savefig`

How can I get matplotlib's imshow to refresh once every second?

Have a sensor that gives me an 8x8 grid of temps that I am to display on a live heatmap. I have made an 8x8 rand to simulate that data. This heatmap should be able to run until I tell it not to run anymore. I'm using python3 and matplotlib to attempt this visualization.
I've tried may ways to make this work, including clearing the screen, turning the plt into a figure, telling show() to not block, etc. You can see many of my attempts in the comments. It either displays once only, or never displays at all (e.g. ion() and plt.show(block=False) never display any data). I've hit my head against a wall for 2 whole work days and I can't figure out why it won't display properly.
import time
import socket
import matplotlib.pyplot as plt
import numpy as np
import random
first = True
randArray = []
#plt.ion()
plt.show(block=False)
#fig = plt.figure()
#str1 = ''.join(str(e) for e in amg.pixels)
#print (type(str1))
while True:
#create a bunch of random numbers
randArray = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
for x in randArray:
print(x)
#This is my attempt to clear.
if (first == False):
plt.clf()
first = False
#basical visualization
plt.imshow(randArray, cmap='hot', interpolation='nearest')
plt.draw()
plt.show()
#fig.canvas.draw()
#plt.canvas.draw()
#plt.display.update
print("Pausing...")
time.sleep(5)
I expect the code to generate a new set of numbers every 5 seconds, and then refresh the screen with the colors of those new numbers. This should be able to run for hours if I don't interrupt, but the screen never refreshes.
More: I have tried everything listed in the post "How to update a plot in matplotlib?" and everything they do just makes it so that no graph ever populates. The launcher acts like it's going to do something by showing up in the task bar, but then does nothing. I've tried it on a Mac and a Pi, both have the same issue. Maybe it's because that post is 8 years old, and this is python 3 not python 2? Maybe it's because I use imshow() instead of plot()? I haven't figured out how to make their code work on my machine either.
Edit: I've gotten it to work on the raspberry pi thanks to the first commenters recommendations. But now I'm left wondering.... what's wrong with my Mac??
This is a similar question to this one.
You could try to modify your code to something like this:
import time
import socket
import matplotlib.pyplot as plt
import numpy as np
import random
first = True
randArray = []
#plt.ion()
plt.show(block=False)
#fig = plt.figure()
#str1 = ''.join(str(e) for e in amg.pixels)
#print (type(str1))
fig = plt.figure()
for i in range(0,5):
#create a bunch of random numbers
randArray = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
for x in randArray:
print(x)
#This is my attempt to clear.
if (first == False):
plt.clf()
first = False
#basical visualization
ax = fig.add_subplot(111)
ax.imshow(randArray, cmap='hot', interpolation='nearest')
fig.canvas.draw()
fig.canvas.flush_events()
#fig.canvas.draw()
#plt.canvas.draw()
#plt.display.update
print("Pausing...")
time.sleep(2)
Have a nice day and get some rest :).
Try this one:
import matplotlib.pyplot as plt
import numpy as np
while True:
#create a bunch of random numbers
random_array = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
print(random_array)
#clear the image because we didn't close it
plt.clf()
#show the image
# plt.figure(figsize=(5, 5))
plt.imshow(random_array, cmap='hot', interpolation='nearest')
plt.colorbar()
print("Pausing...")
plt.pause(5)
#uncomment this line and comment the line with plt.clf()
# plt.close()
The magic is with the line plt.pause(5), it shows the image for five seconds. It's up to you if you want to close it (plt.close()) or clear it (plt.clf()). When you want to update constantly your plot, you don't use plt.show() or plt.draw(), you use plt.pause().
Uncomment some lines to try some variations... of course, some of them won't work.

What is the currently correct way to dynamically update plots in Jupyter/iPython?

In the answers to how to dynamically update a plot in a loop in ipython notebook (within one cell), an example is given of how to dynamically update a plot inside a Jupyter notebook within a Python loop. However, this works by destroying and re-creating the plot on every iteration, and a comment in one of the threads notes that this situation can be improved by using the new-ish %matplotlib nbagg magic, which provides an interactive figure embedded in the notebook, rather than a static image.
However, this wonderful new nbagg feature seems to be completely undocumented as far as I can tell, and I'm unable to find an example of how to use it to dynamically update a plot. Thus my question is, how does one efficiently update an existing plot in a Jupyter/Python notebook, using the nbagg backend? Since dynamically updating plots in matplotlib is a tricky issue in general, a simple working example would be an enormous help. A pointer to any documentation on the topic would also be extremely helpful.
To be clear what I'm asking for: what I want to do is to run some simulation code for a few iterations, then draw a plot of its current state, then run it for a few more iterations, then update the plot to reflect the current state, and so on. So the idea is to draw a plot and then, without any interaction from the user, update the data in the plot without destroying and re-creating the whole thing.
Here is some slightly modified code from the answer to the linked question above, which achieves this by re-drawing the whole figure every time. I want to achieve the same result, but more efficiently using nbagg.
%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
pl.clf()
pl.plot(pl.randn(100))
display.display(pl.gcf())
display.clear_output(wait=True)
time.sleep(1.0)
Here is an example that updates a plot in a loop. It updates the data in the figure and does not redraw the whole figure every time. It does block execution, though if you're interested in running a finite set of simulations and saving the results somewhere, it may not be a problem for you.
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import time
def pltsin(ax, colors=['b']):
x = np.linspace(0,1,100)
if ax.lines:
for line in ax.lines:
line.set_xdata(x)
y = np.random.random(size=(100,1))
line.set_ydata(y)
else:
for color in colors:
y = np.random.random(size=(100,1))
ax.plot(x, y, color)
fig.canvas.draw()
fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
pltsin(ax, ['b', 'r'])
time.sleep(1)
I put this up on nbviewer here.
There is an IPython Widget version of nbagg that is currently a work in progress at the Matplotlib repository. When that is available, that will probably be the best way to use nbagg.
EDIT: updated to show multiple plots
I'm using jupyter-lab and this works for me (adapt it to your case):
from IPython.display import clear_output
from matplotlib import pyplot as plt
import numpy as np
import collections
%matplotlib inline
def live_plot(data_dict, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
for label,data in data_dict.items():
plt.plot(data, label=label)
plt.title(title)
plt.grid(True)
plt.xlabel('epoch')
plt.legend(loc='center left') # the plot evolves to the right
plt.show();
Then in a loop you populate a dictionary and you pass it to live_plot():
data = collections.defaultdict(list)
for i in range(100):
data['foo'].append(np.random.random())
data['bar'].append(np.random.random())
data['baz'].append(np.random.random())
live_plot(data)
make sure you have a few cells below the plot, otherwise the view snaps in place each time the plot is redrawn.
If you don't want to clear all outputs, you can use display_id=True to obtain a handle and use .update() on it:
import numpy as np
import matplotlib.pyplot as plt
import time
from IPython import display
def pltsin(ax, *,hdisplay, colors=['b']):
x = np.linspace(0,1,100)
if ax.lines:
for line in ax.lines:
line.set_xdata(x)
y = np.random.random(size=(100,1))
line.set_ydata(y)
else:
for color in colors:
y = np.random.random(size=(100,1))
ax.plot(x, y, color)
hdisplay.update(fig)
fig,ax = plt.subplots(1,1)
hdisplay = display.display("", display_id=True)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
pltsin(ax, colors=['b', 'r'], hdisplay=hdisplay)
time.sleep(1)
plt.close(fig)
(adapted from #pneumatics)
I've adapted #Ziofil answer and modified it to accept x,y as list and output a scatter plot plus a linear trend on the same plot.
from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
def live_plot(x, y, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
plt.xlim(0, training_steps)
plt.ylim(0, 100)
x= [float(i) for i in x]
y= [float(i) for i in y]
if len(x) > 1:
plt.scatter(x,y, label='axis y', color='k')
m, b = np.polyfit(x, y, 1)
plt.plot(x, [x * m for x in x] + b)
plt.title(title)
plt.grid(True)
plt.xlabel('axis x')
plt.ylabel('axis y')
plt.show();
you just need to call live_plot(x, y) inside a loop.
here's how it looks:
The canvas.draw method of the figure dynamically updates its graphs, for the current figure:
from matplotlib import pyplot as plt
plt.gcf().canvas.draw()

Make a matplotlib animation, by clf()'ing each frame

i am trying to make the simplest matplotlib animation, using animation.FuncAnimation. I dont care about efficiency. i do not want to keep track of the plotted lines and update their data (in my desired application this would be annoying), i simply want to erase the plot before animating every frame. i thought somthing like this should work, but its not..
import matplotlib.animation as animation
fig = Figure()
def updatefig(i):
clf()
p = plot(rand(100))
draw()
anim = animation.FuncAnimation(fig, updatefig, range(10))
At least this seems to work:
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
def updatefig(i):
fig.clear()
p = plt.plot(np.random.random(100))
plt.draw()
anim = animation.FuncAnimation(fig, updatefig, 10)
anim.save("/tmp/test.mp4", fps=1)
The issue with the original code is Figure written with a capital F (should be figure).
Otherwise, I would suggest not to use the pylab style "everything in the same namespace" approach with matplotlib. Also, using the object-oriented interface instead of plt.draw, plt.plot, etc. will save a lot of trouble later on.

Categories