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

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

Related

Why doesn't the show() function in matplotlib.pyplot work more than once for the same Axes object?

Out of pure curiosity, I would love to know why the final plt.show() does not display both plots on ax. Only the first plt.show() seems to do anything, because only the plot of y = sin(x) shows up. Here is the code sample:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(1, 100, 10)
ax.plot(x, np.sin(x))
plt.show()
ax.plot(x, x)
plt.show()
Appreciate any help on this, because it bugs me to not understand why this is the case, even after a lot of searches. PS: I know that the code is useless and dumb, but I would still like to know for future use.
Your code
## load libraries
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(1, 100, 10)
## assign first plot
ax.plot(x, np.sin(x))
#plt.show()
## assign second plot
ax.plot(x, x)
## render the plots
plt.show()
One reason why plt.show() didn't 'show' more than once.
You are using subplots.
Your plots are on the same axes
plt.show() display all open figures. Your ax.plot(x, np.sin(x)) will be shown and the figure closed. The second is on the same ax and will not be shown anymore.
Documentation: matplotlib.pyplot.show()
[Alternate]
If however you call plt.plot() separately, (without subplots axes), you would get two plots; each with its own dimensions.
PS: below works in Jupyter (mybinder)
## load libraries
import matplotlib.pyplot as plt1
import numpy as np
x = np.linspace(1, 100, 10)
## first plot
plt1.plot(x, np.sin(x))
## render first plot
plt1.show()
Followed by
## second plot
plt1.plot(x, x)
## render the plot
plt1.show()
Clarity from the documentation: matplotlib.pyplot is a state-based interface to matplotlib
[Updated]
#JohanC lumping up the explanatory code and the alternate code.
The explanation 1, 2, and 3 remains.
The alternate code remain. or
OP can put the two plots on their own ax, and have plt.show each.
I didn't intend including this before. However, for completeness:
## load libraries
import matplotlib.pyplot as plt
import numpy as np
## tuple of desired two axes
## unpack AxesSubplot on two rows
fig, (ax1, ax2), = plt.subplots(nrows=2)
## assign variable
x = np.linspace(1, 100, 10)
## assign first plot
ax1.plot(x, np.sin(x))
#plt.show()
## assign second plot
ax2.plot(x, x)
## render the plots
## Rendering might not be 'smooth' in Jupyter
plt.show()

Update a chart in realtime with matplotlib

I'd like to update a plot by redrawing a new curve (with 100 points) in real-time.
This works:
import time, matplotlib.pyplot as plt, numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
t0 = time.time()
for i in range(10000000):
x = np.random.random(100)
ax.clear()
ax.plot(x, color='b')
fig.show()
plt.pause(0.01)
print(i, i/(time.time()-t0))
but there is only ~10 FPS, which seems slow.
What is the standard way to do this in Matplotlib?
I have already read How to update a plot in matplotlib and How do I plot in real-time in a while loop using matplotlib? but these cases are different because they add a new point to an existing plot. In my use case, I need to redraw everything and keep 100 points.
I do not know any technique to gain an order of magnitude. Nevertheless you can slightly increase the FPS with
update the line data instead of creating a new plot with set_ydata (and/or set_xdata)
use Figure.canvas.draw_idle() instead of Figure.canvas.draw() (cf. this question).
Thus I would recommand you to try the following:
import time
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
t0 = time.time()
x = np.random.random(100)
l, *_ = ax.plot(x, color='b')
fig.show()
fig.canvas.flush_events()
ax.set_autoscale_on(False)
for i in range(10000000):
x = np.random.random(100)
l.set_ydata(x)
fig.canvas.draw_idle()
fig.canvas.flush_events()
print(i, i/(time.time()-t0))
Note that, as mentioned by #Bhargav in the comments, changing matplotlib backend can also help (e.g. matplotlib.use('QtAgg')).
I hope this help.

Matplotlib (or mplfinance) two animation.FuncAnimation with different intervals

In python matplotlib finance
Is it possible to have two different figures with animation.FuncAnimation in mplfinance where one has 12 axis with different style and another figure has two planes one for bar and another for volume.
Another reason is that both figures having different intervals for refresh
There are two ways that I can think of to do this. I will show examples using simple matplotlib plots so as to focus on the animation function(s). If you understand the mplfinance animation example then you will be able make the analogous changes to make this work with mplfinance.
The two approaches are:
Maintain more than one plot with a single func animation. If different update frequencies are needed, use modulo to update one or more of the plots. The disadvantage here is that the update period of each plot must be some multiple of the update period of the fastest plot.
Create two func animations. This requires that each func animation be assigned to a different variable (and each such variable must remain in scope, i.e. not be deleted or destroyed, for the duration of animation).
Approach 1 Example: Single Func Animation maintaining two plots:
"""
A simple example of TWO curves from one func animation
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2,sharey=ax1)
x = np.arange(0, 2*np.pi, 0.01)
line1, = ax1.plot(x, np.sin(x))
line2, = ax2.plot(x, 0.5*np.sin(2.5*(x)))
def animate(i):
line2.set_ydata(0.5*np.sin(2.5*(x + i/5.0))) # update the data
if i%3 == 0: # modulo: update line1 only every third call
line1.set_ydata(np.sin(x + i/10.0)) # update the data
return line1,line2
ani1 = animation.FuncAnimation(fig, animate, np.arange(1, 200), interval=250)
plt.show()
Approach 2 Example: Two Func Animations maintaining two plots:
"""
A simple example of TWO animated plots
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2,sharey=ax1)
x = np.arange(0, 2*np.pi, 0.01)
line1, = ax1.plot(x, np.sin(x))
line2, = ax2.plot(x, 0.5*np.sin(2.5*(x)))
def animate1(i):
line1.set_ydata(np.sin(x + i/10.0)) # update the data
return line1,
def animate2(i):
line2.set_ydata(0.5*np.sin(2.5*(x + i/5.0))) # update the data
return line2,
ani1 = animation.FuncAnimation(fig, animate1, np.arange(1, 200), interval=250)
ani2 = animation.FuncAnimation(fig, animate2, np.arange(1, 200), interval=75)
plt.show()
If you create a script with either of the above examples and run under python (but do NOT run in a notebook or IDE, because that may or may not work) both cases should give an animation that looks something like this:

Matplotlib Animation for Plotting Points Being Connected Given Arrays of X and Y values to be coordinates

I have two arrays containing x and y values. Each array has 1274 values in it. I essentially want to create a matplotlib animation where these points are being plotted and also connected by a line. I tried doing this with FuncAnimation, but ran into a lot of trouble. Imagine that x and y are the two arrays that I'm referring to. Rest of the code is what I tried so far.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from os import getcwd, listdir
gif_path = getcwd() + "/gifs"
fig = plt.figure()
graph, = plt.plot([], [], 'o')
def animate(i):
if i > len(x) - 1:
i = len(x) - 1
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, interval=200)
ani.save(f"{gif_path}/sample_region.gif", writer="imagemagick")
Any help would kindly be appreciated. Thanks.
Your code seems to be following an example from matplotlib, you just need a few extra changes:
x = range(100)
y = np.random.rand(100)
fig, ax = plt.subplots()
ax.set_xlim(0, 100)
ax.set_ylim(0, 1)
graph, = plt.plot([], [], '-')
def init():
return graph,
def animate(i):
graph.set_data(x[:i],y[:i])
return graph,
ani = FuncAnimation(fig, animate, frames=range(len(x)), interval=50, save_count=len(x),
init_func=init, blit=True)
ani.save('ani.gif', writer='PillowWriter')
This produces this GIF. The changes are:
set up an Axes on fig with set axis limits
change the o to -
add an init function for initializing the animation
add a frames argument to pass indexes used to select data
update animate to handle those frames
But I tried making this with an array of 1200 points, and it didn't seem like my computer could complete it... You can try but you might need to trim the data or plot more data each frame.

Showing subplots at each pass of a loop

I would essentially like to do the following:
import matplotlib.pyplot as plt
import numpy as np
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
for i in range(10):
ax1.scatter(i, np.sqrt(i))
ax1.show() # something equivalent to this
ax2.scatter(i, i**2)
That is, each time a point is plotted on ax1, it is shown - ax2 being shown once.
You cannot show an axes alone. An axes is always part of a figure. For animations you would want to use an interactive backend. Then the code in a jupyter notebook could look like
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
frames = 10
x = np.arange(frames)
line1, = ax1.plot([],[], ls="", marker="o")
line2, = ax2.plot(x, x**2, ls="", marker="o")
ax2.set_visible(False)
def animate(i):
line1.set_data(x[:i], np.sqrt(x[:i]))
ax1.set_title(f"{i}")
ax1.relim()
ax1.autoscale_view()
if i==frames-1:
ax2.set_visible(True)
fig2.canvas.draw_idle()
ani = FuncAnimation(fig1, animate, frames=frames, repeat=False)
plt.show()
If you want to change plots dynamically I'd suggest you don't redraw the whole plot every time, this will result in very laggy behavior. Instead you could use Blit to do this. I used it in a previous project. Maybe it can help you too if you just take the parts from this you need:
Python project dynamically updating plot

Categories