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

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:

Related

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 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.

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

matplotlib: multiple plots on one figure

I have some code:
import matplotlib.pyplot as plt
def print_fractures(fractures):
xpairs = []
ypairs = []
plt.figure(2)
plt.subplot(212)
for i in range(len(fractures)):
xends = [fractures[i][1][0], fractures[i][2][0]]
yends = [fractures[i][1][1], fractures[i][2][1]]
xpairs.append(xends)
ypairs.append(yends)
for xends,yends in zip(xpairs,ypairs):
plt.plot(xends, yends, 'b-', alpha=0.4)
plt.show()
def histogram(spacings):
plt.figure(1)
plt.subplot(211)
plt.hist(spacings, 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
plt.show()
histogram(spacings)
print_fractures(fractures)
This code will produce the following output:
My questions are:
1) Why are two separate figures being created? I thought the subplot command would combine them into one figure. I thought it might be the multiple plt.show() commands, but I tried commenting those out and only calling it once from outside my functions and I still got 2 windows.
2) How can I combine them into 1 figure properly? Also, I would want figure 2 axes to have the same scale (i.e. so 400 m on the x axis is the same length as 400 m on the y-axis). Similarly, I'd like to stretch the histogram vertically as well - how is this accomplished?
As you observed already, you cannot call figure() inside each function if you intend to use only one figure (one Window). Instead, just call subplot() without calling show() inside the function. The show() will force pyplot to create a second figure IF you are in plt.ioff() mode. In plt.ion() mode you can keep the plt.show() calls inside the local context (inside the function).
To achieve the same scale for the x and y axes, use plt.axis('equal'). Below you can see an illustration of this prototype:
from numpy.random import random
import matplotlib.pyplot as plt
def print_fractures():
plt.subplot(212)
plt.plot([1,2,3,4])
def histogram():
plt.subplot(211)
plt.hist(random(1000), 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
histogram()
print_fractures()
plt.axis('equal')
plt.show()

Categories