How gradually plot a curve in console - python

So the following code creates a gif file.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0, 10, 100)
y = np.sin(x)
fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
def update(num, x, y, line):
line.set_data(x[:num], y[:num])
line.axes.axis([0, 10, 0, 1])
return line,
ani = animation.FuncAnimation(fig, update, len(x), fargs=[x, y, line],
interval=25, blit=True)
ani.save('test.gif')
plt.show()
When I run it, it shows the final result of the animation in the console.
However, I would like to see the entire animation in the console.
How can this be done?
It should also work when there is a large number of frames.
EDIT:
I am using Python 3.8.5 and Spyder 4.2.1. I would like to use the 'plots' pane of Spyder.

Well I dont have enough reputation for a comment
But as far as i know ani.save() is blocking try to switch the last two lines.
plt.show(block=False)
ani.save("test.gif")
The window will close as soon ani.save() is done if you don't want that change block to True but it will not save until you closed the window

Related

Trying to make a gif in Python

I'm approximating functions with Fourier Series and I would like to make, hopefully in a simple way, an animated gif of the different approximations. With a for and plt.savefig commands I generate the different frames for the gif but I haven't been able to animate them. The idea is to obtain something like this
This can be done using matplotlib.animation.
EDIT
I'm going to show you how to plot even powers of x like x^2, x^4 and so on. take a look at following example :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Setting up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-10, 10), ylim=(-10, 1000))
line, = ax.plot([], [], lw=2)
# initialization method: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation method. This method will be called sequentially
pow_ = 2
def animate(i):
global pow_
x = np.linspace(-10, 10, 1000)
y = x**pow_
pow_+=2
line.set_data(x, y)
return line,
# 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=200, blit=True)
# if you want to save this animation as an mp4 file, uncomment the line bellow
# anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()

Matplotlib live graph capable of handling long times between data updates

I've noticed that every solution to plotting continuously updating data (I've found) with a continuously increasing length has one huge setback - If the data isn't there immediately, the matplotlib window freezes (says not responding). Take this for example:
from matplotlib import pyplot as plt
from matplotlib import animation
from random import randint
from time import sleep
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
line, = ax.plot([])
x = []
y = []
def animate(i):
x.append(i)
y.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate,
frames=200, interval=20, blit=True)
plt.show()
This code works fine without the data acquisition for loop in the animate function, but with it there, the graph window freezes. Take this as well:
plt.ion()
x = []
for i in range(1000):
x.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
plt.plot(x)
plt.pause(0.001)
Also freezes. (Thank god for that, because using this method it's borderline impossible to close as the graph keeps popping up in front of everything. I do not recommend removing the sleep)
This too:
plt.ion()
x = []
for i in range(1000):
x.append(randint(0,10))
for i in range(100000000):
# Do calculations to attain next data point
pass
plt.plot(x)
plt.draw()
plt.pause(0.001)
plt.clf()
Also this: (copied from https://stackoverflow.com/a/4098938/9546874)
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
for i in range(100000000):
# Do calculations to attain next data point
pass
fig.canvas.draw()
fig.canvas.flush_events()
This is a huge problem, as it's naive to think all the data will come at consistent intervals. I just want a graph that updates when data comes, and doesn't implode in the downtime. Keep in mind the interval between data could change, it could be 2 seconds, or 5 minutes.
EDIT:
After further testing, the FuncAnimation one can be used, but it's very hacky, and is still a bit broken. If you increase the interval to above the expected time of animate, it will work, but every time you pan or zoom the graph, all the data disappears until the next update. So once you have a view, you can't touch it.
Edit:
Changed sleep to a for loop for clarity
Updated Answer:
The problem is that data aquisition or generation and the matplotlib window run on the same thread so that the former is blocking the latter. To overcome this move the data aquisition into a seperate process as shown in this example. Instead of processes and pipes you can also use threads and queues.
See this example with sleep usage, it's working well:
=^..^=
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x)
def animate(i):
y_data = 0
for j in range(10):
y_data = np.random.uniform(-1, j, 1)
line.set_ydata(y_data)
plt.pause(1)
return line,
ani = animation.FuncAnimation(
fig, animate, interval=2, blit=True, save_count=50)
plt.ylim(-2, 11)
plt.show()

Python: matplotlib - loop, clear and show different plots over the same figure

I want to see how a plot varies with different values using a loop. I want to see it on the same plot. But i do not want to remains of the previous plot in the figure. In MATLAB this is possible by creating a figure and just plotting over the same figure. Closing it when the loop ends.
Like,
fh = figure();
%for loop here
%do something with x and y
subplot(211), plot(x);
subplot(212), plot(y);
pause(1)
%loop done
close(fh);
I am not able to find the equivalent of this in matplotlib. Usually all the questions are related to plotting different series on the same plot, which seems to come naturally on matplotlib, by plotting several series using plt.plot() and then showing them all finally using plt.show(). But I want to refresh the plot.
There are essentially two different ways to create animations in matplotlib
interactive mode
Turning on interactive more is done using plt.ion(). This will create a plot even though show has not yet been called. The plot can be updated by calling plt.draw() or for an animation, plt.pause().
import matplotlib.pyplot as plt
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,17)
ax1.set_ylim(-400,3000)
plt.ion()
for i in range(15):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
plt.pause(0.1)
plt.ioff()
plt.show()
FuncAnimation
Matplotlib provides an animation submodule, which simplifies creating animations and also allows to easily save them. The same as above, using FuncAnimation would look like:
import matplotlib.pyplot as plt
import matplotlib.animation
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,18)
ax1.set_ylim(-400,3000)
def update(i):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=14, repeat=False)
plt.show()
An example to animate a sine wave with changing frequency and its power spectrum would be the following:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
x = np.linspace(0,24*np.pi,512)
y = np.sin(x)
def fft(x):
fft = np.abs(np.fft.rfft(x))
return fft**2/(fft**2).max()
fig, (ax1,ax2) = plt.subplots(nrows=2)
line1, = ax1.plot(x,y)
line2, = ax2.plot(fft(y))
ax2.set_xlim(0,50)
ax2.set_ylim(0,1)
def update(i):
y = np.sin((i+1)/30.*x)
line1.set_data(x,y)
y2 = fft(y)
line2.set_data(range(len(y2)), y2)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=60, repeat=True)
plt.show()
If you call plt.show() inside the loop you will see the plot for each element on the loop as long as you close the window containing the figure. The process, will be plot for the first element, then if you close the window you will see the plot for the second element in the loop, etc

How to update tick labels in animated matplotlib graph

How can I get the tick labels to update once changed in an animated graph?
Here is just a simple example of what I need. I realize I can set blit=False and have it work, however the project I am working on requires blit=True for performance reasons.
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
import time
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x, np.sin(x))
def animate(i):
line.set_ydata(np.sin(x + i/10.0))
if i > 30:
ax.tick_params(axis='y', colors='red') ### How do I get my graph to reflect this change?
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
interval=100, blit=True)
plt.show()
I got it working with the monkey patch suggested in this post.
Hope this works for wandering souls (like me) that spent A LOT of time trying to solve this problem.

Redrawing legend when the figure is closed

Using matplotlib, I'm trying to execute a callback function when a figure is closed, which redraws the figure legend. However, when I call ax.legend(), it seems to block any further code being executed. So in the code below, 'after' is never printed.
Could someone explain why this is? And is it possible for me to run code after the legend() call, but before the figure closes? The ultimate goal is to save two different versions of a figure when it is closed, redrawing the legend in between saves. Thanks.
from __future__ import print_function
import matplotlib.pyplot as plt
def handle_close(evt):
f = evt.canvas.figure
print('Figure {0} closing'.format(f.get_label()))
ax = f.get_axes()
print('before')
leg = ax.legend() # This line causes a problem
print('after') # This line (and later) is not executed
xs = range(0, 10, 1)
ys = [x*x for x in xs]
zs = [3*x for x in xs]
fig = plt.figure('red and blue')
ax = fig.add_subplot(111)
ax.plot(xs, ys, 'b-', label='blue plot')
ax.plot(xs, zs, 'r-', label='red plot')
fig.canvas.mpl_connect('close_event', handle_close)
ax.legend()
plt.show()
Ok, sorry, I have figured it out. f.get_axes() returns a list of axes objects. So the later call to ax.legend() doesn't work correctly.
Changing to the lines below fixes the problem:
axs = f.get_axes()
for ax in axs:
leg = ax.legend()
I'm still not sure why this didn't produce some kind of interpreter error though.

Categories