Save Matplotlib animation Tikzplotlib - python

I have a numerical simulation that I run with a short python code using animation.Funcanimation. Is is possible to use tikzplotlib to produce a .tex file including the whole simulation (and not just one picture) ? This would mean combining tikz and the animate package, for instance. For the moment my sole idea is to export each of the pictures and try to do so something directly with latex (but even this is not completely clear for me !).
Here is an example of short simulation :
import numpy as np
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
from matplotlib import animation
# Sites
M = 10
sites = np.array(range(M))
# Particles
N = 3
# Right hand side
def lotka(t,x):
u = x[0:M]
v = x[M:]
dudt = u-u*u-u*v
dvdt = v-u*v-v*v
dxdt = np.concatenate((dudt,dvdt))
return dxdt
# Initial conditions
u0 = np.zeros(M)
v0 = np.zeros(M)
# Segregated
u0[0] = N
v0[-1] = N
if M%2 == 0:
for i in range(int(M/2)):
u0[i] = N
v0[M-1-i] = N
else:
for i in range(int(np.floor(M/2))-1):
u0[i] = N
v0[M-1-i] = N
x0 = np.concatenate((u0,v0))
# Solving the equation
nt = 500
t = np.linspace(0,0.2,nt)
x = solve_ivp(lotka,[0,4],x0,t_eval=t)
# Getting each species from the solution of solve_ivp
u = np.zeros((nt,M))
v = np.zeros((nt,M))
for i in range(nt):
u[i] = x.y.T[i][0:M]
v[i] = x.y.T[i][M:]
# Animation
data = [u, v]
fig = plt.figure()
ax = plt.axes()
ax.grid()
lines = [ax.plot(sites,u[0])[0], ax.plot(sites,v[0])[0]]
time_template = 'time = % s'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
lines.append(time_text)
def animate(i,lines,data):
lines[0].set_ydata(data[0][i])
lines[1].set_ydata(data[1][i])
lines[2].set_text(time_template % t[i])
return lines
anim = animation.FuncAnimation(fig,
animate,
#frames=200,
fargs=(lines,data),
interval=1,
blit=True)
plt.show()

Related

FuncAnimation how to update text after each iteration

I am trying to create an animation of a Monte-Carlo estimation of the number pi, for each iteration I would like the numerical estimation to be in text on the plot, but the previous text is not removed and makes the values unreadable. I tried Artist.remove(frame) with no success. The plot is done with Jupiter Notebook.
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
#create necessary arrays
x = np.arange(0,N)
y = np.zeros(N)
#set initial points to zero
inHull = 0
def inCircle(point):
#the function is given a point in R^n
#returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
#iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
#determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
#we store areas in array y.
y[i] = (inHull*4)/(i+1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y)- 1, max(y)+0.5))
def update_plot(i):
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
ax.scatter(x[i], y[i], color='none')
Artist.remove(ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5))))
ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5)))
anim = FuncAnimation(fig,
update_plot,
frames=np.arange(0, len(x), data_skip),
init_func=init_func,
interval=20)
plt.show()
Thank you.
As you have already done in init_func, you should clear the plot in each iteration with ax.clear(). Then it is necessary to edit slighlty the plot function:
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
And finally you have to fix x axis limits in each iteration with ax.set_xlim(0, N).
Complete Code
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
# create necessary arrays
x = np.arange(0, N)
y = np.zeros(N)
# set initial points to zero
inHull = 0
def inCircle(point):
# the function is given a point in R^n
# returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
# iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
# determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
# we store areas in array y.
y[i] = (inHull*4)/(i + 1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
txt = ax.text(N*0.6, max(y) + 0.25, "")
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y) - 1, max(y) + 0.5))
def update_plot(i):
ax.clear()
ax.plot(x[:i + data_skip], y[:i + data_skip], color = 'k')
ax.scatter(x[i], y[i], color = 'none')
ax.text(N*0.6, max(y) + 0.25, "Estimation: " + str(round(y[i], 5)))
ax.set_xlim(0, N)
anim = FuncAnimation(fig,
update_plot,
frames = np.arange(0, len(x), data_skip),
init_func = init_func,
interval = 20)
plt.show()
Animation

Replot the same figure using matlibplot

I have the following code snippet:
import numpy as np
import matplotlib.pyplot as plt
N = 25
M = 10
def aFuncation(x):
return np.random.normal(100*np.exp(-x), 10.0)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = np.zeros(N)
ys = np.zeros(N)
stds = np.zeros(N)
for n in range(N):
avgC = np.zeros(M)
for m in range(M):
Cost = aFuncation(n)
avgC[m] = Cost
xs[n] = n
ys[n] = np.mean(avgC)
stds[n] = np.std(avgC)
# Plot the time series
ax.clear()
ax.fill_between(xs[:n], ys[:n] - stds[:n], ys[:n] + stds[:n], alpha=0.3, color='black')
ax.plot(xs[:n], ys[:n], color='red')
ax.set_xlim([0, N + 1])
ax.set_xlabel('Number of evolutions')
ax.set_ylabel('Expected future cost')
plt.show()
I am not sure why the plot is not updated after the first iteration (n=1). I have read several posts related to this topic. The posts suggest the use of plt.ion(), plt.clf(), plt.draw(), etc. Whenever more I read about this topic, I become more confused. I am unfortunately unable to find a solution to this problem.

Is there a simple way to animate several points in the same plot with FuncAnimation?

I was trying to do an animation for fun using the mathematical solution for the pendulum. I know that my script is a little bit messy but I would really hope to improve. Below is my attempt to produce an animation using FuncAnimation from Matplotlib. I generated the data information for 3 points but my gif only shows one.
I read the documentation examples and some answers here similar to my problem but I really did not understand how to apply a simple solution in my case. I also saw that it was possible to use a scatter plot for this case but still I was not able to do it.
I would be grateful if you could recommend me a solution considering that I am a newbie using Python. And any other recommendation regarding the script will be kindly appreciated.
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
from matplotlib.animation import FuncAnimation
%matplotlib inline
def solution(g, length, n, initial_angle, initial_velocity):
# g = 9.81
# l = 9.81
# n = 8
def model(u, t):
return (u[1], - (g / length) * np.sin(u[0] * np.pi / 180))
# Initial angle, and initial velocity
theta0 = [initial_angle, initial_velocity]
# Desired time interval
time = np.arange(0, 2 * n * np.pi, 0.1)
solution = odeint(model, theta0, time)
return solution[:,0]
data = []
for i in range(1, 4):
data.append(solution(9.81, 9.81 * i / 3, n, -17.5, 0))
data = np.array(data)
x_temp = np.sin(data * np.pi / 180)
y_temp = - np.cos(data * np.pi / 180)
# Coordinates for 3 pendulums
for i in range(0, 3):
x_temp[i] = (9.81 * i / 3) * x_temp[i]
y_temp[i] = (9.81 * i / 3) * y_temp[i]
# minimums and maximums based on the longest pendulum
x_min = x_temp[2].min()
x_max = x_temp[2].max()
y_min = y_temp[2].min()
fig, ax = plt.subplots()
ax = plt.axes(xlim=(x_min - 0.5, x_max + 0.5), ylim=(y_min - 0.5, 0))
point, = ax.plot([],[], 'go', lw=3)
# def init():
# point.set_data([], [])
# return point,
def animation_frames(i, x, y):
# point.set_data(x, y)
for j in range(0,3):
point.set_data(x[j][i], y[j][i])
return point,
animation = FuncAnimation(fig, animation_frames, frames=len(x_temp[0]), fargs=(x_temp, y_temp), interval=10)
animation.save('simple_pendulum.gif', writer='imagemagick')
Edit
I generated a simple case as recommended. The lists to be used just before calling the sublopts function are:
x_tem, y_temp
(array([[-0.98330796, -0.97717458, -0.95882857, -0.92843441, -0.88627296,
-0.83275034, -0.76840839, -0.69393471, -0.61017043, -0.51811348,
-0.41891573, -0.31387291, -0.20440652],
[-1.96661593, -1.9604803 , -1.94210039, -1.91155767, -1.86898966,
-1.81459239, -1.74862365, -1.67140657, -1.58333345, -1.48486936,
-1.37655514, -1.25900948, -1.13292978],
[-2.94992389, -2.94378752, -2.92539636, -2.89480454, -2.85210308,
-2.79742099, -2.73092681, -2.65283041, -2.56338497, -2.46288906,
-2.35168869, -2.23017916, -2.0988066 ]]),
array([[-3.11865443, -3.12058165, -3.12626739, -3.13542813, -3.14760548,
-3.16218704, -3.17843492, -3.19552102, -3.21256783, -3.22869299,
-3.2430556 , -3.2549015 , -3.26360506],
[-6.23730886, -6.2392401 , -6.24498567, -6.25440223, -6.26725439,
-6.28322007, -6.30189776, -6.32281583, -6.34544365, -6.36920427,
-6.39348856, -6.41767054, -6.44112336],
[-9.35596329, -9.35789587, -9.36366147, -9.37316418, -9.38624568,
-9.40268769, -9.42221517, -9.44450056, -9.46916879, -9.49580315,
-9.52395193, -9.55313566, -9.58285505]]))
I don't know exactly what your goal is but I tried to imitate your example of animating three points independently as close as possible. I only added different color and marker features, so that the points can be better distinguished:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
n=100
x_temp = [np.linspace(-10, -8, n),
np.linspace(-9, -6, n),
np.linspace(-6, -10, n)]
y_temp = [np.sin(x_temp[0]),
np.cos(x_temp[1]),
np.sin(x_temp[2])]
fig, ax = plt.subplots()
ax = plt.axes(xlim=(-11, -5), ylim=(- 1.5, 1.5))
points = []
for j, (col, mar) in enumerate(zip(["green", "blue", "red"], ["o", "x", "s"])):
newpoint, = ax.plot(x_temp[j][0], y_temp[j][0], color=col, marker=mar)
points.append(newpoint)
def animation_frames(i):
for j in range(0,3):
points[j].set_data(x_temp[j][i], y_temp[j][i])
animation = FuncAnimation(fig, animation_frames, frames=len(x_temp[0]), interval=30)
plt.show()
Sample output:

Real time live graphs in Jupyter Notebook

I have just started learning python to plot realtime gragh. I have tried solutions provided on stackoverflow but none of them are working. Below is my code and it isn't woorking. Please help
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 512
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
def animate(i):
try:
while True:
x, y = pg.position()
f.append(x)
except KeyboardInterrupt:
print('')
# f.append(15)
if len(f)<binSize :
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="Noisy")
else:
plt.cla()
plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Noisy")
ani = FuncAnimation(plt.gcf(),animate,interval=1);
So I have updated the code and trying to draw two subplots but after sometime
Upper graph stopped clearing the canvas (Mouse X coordinates)
Lower graph stopped updating the plot (FFT)
When data grows beyond the binSize, notebook freezes and plots update really slowly
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
def animate(i):
x, y = pg.position()
f.append(x)
n = len(f)
if n<binSize :
plt.sca(axs[0])
plt.cla()
plt.plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int')
# update the code third time
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
# axs[0].xlim(0,binSize) # this stopped the FFT graph to be plotted
# plt.cla()
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
# plt.xlim(t[0],t[-1])
# plt.legend()
# plt.sca(axs[1])
# plt.plot(freq[L],PSD[L],color='c',LineWidth=2,label="Mouse FFT")
# plt.xlim(0,300)
# plt.legend()
# plt.cla()
# plt.plot(f[-binSize:],color='c',LineWidth=1.5,label="Mouse")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
To make it faster you may reduce data like in other answer
f.pop(0)
I use also different method to update plot which works much faster on my computer.
I create empty plots at start
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
and later only update data in plots without clear() and plot() again
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
and
# replace data in plot
xdata = range(binSize)
ydata = f[-binSize:]
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
It needs only to run code which rescale plot
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
animate() has to also return new plots
# return plots
return p1, p2
And FuncAnimation() has to blit them
ani = FuncAnimation(..., blit=True)
EDIT:
Animation works much, much faster also because I run it normally python script.py, not in Jupuyter Notebook
EDIT:
when I run normally I found one problem which I could find solution: it doesn't update values/ticks on axes. Jupyter Notebook doesn't have this problem.
import numpy as np
import matplotlib.pyplot as plt
import pyautogui as pg
from matplotlib.animation import FuncAnimation
%matplotlib notebook
binSize = 256
f = []
t = 0
dt = 1
fig, axs = plt.subplots(2, 1)
# needs `,` to get first element from list
p1, = axs[0].plot([], [], color='c', LineWidth=1.5, label="MOUSE")
p2, = axs[1].plot([], [], color='r', LineWidth=2, label="FFT")
freq = np.arange(binSize)/(dt*binSize)
def animate(i):
x, y = pg.position()
n = len(f)
if n < binSize :
f.append(x)
# replace data in plot
xdata = range(len(f))
ydata = f
p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
#p1.set_ydata(ydata)
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f, binSize)
PSD = fhat * np.conj(fhat) / binSize
# replace data in plot
#xdata = range(binSize)
ydata = f[-binSize:]
#p1.set_data(xdata, ydata)
#p1.set_xdata(xdata)
p1.set_ydata(ydata)
# replace data in plot
xdata = freq[:(binSize//2)]
ydata = PSD[:(binSize//2)]
p2.set_data(xdata, ydata)
# rescale view
axs[0].relim()
axs[0].autoscale_view(True,True,True)
axs[1].relim()
axs[1].autoscale_view(True,True,True)
# return plots
return p1, p2
ani = FuncAnimation(plt.gcf(), animate, interval=dt, blit=True)
plt.show()
You should try this. Instead of clearing the plt clear axs[0] and so on. Also, instead of plotting on plt.plot, plot on axs[0].plot
%matplotlib notebook
binSize = 256
# fig(ax1,ax2) = plt.subplots(2,figsize=(12,6))
f = []
t = 0
dt = 1
fig,axs = plt.subplots(2,1)
plt.sca(axs[0])
plt.sca(axs[1])
def animate(i):
x, y = pg.position()
n = len(f)
if n<binSize :
f.append(x*100)
axs[0].clear()
axs[0].plot(f, color='c',LineWidth=1.5,label="MOUSE")
else:
f.pop(0)
f.append(x)
fhat = np.fft.fft(f,binSize)
PSD = fhat*np.conj(fhat)/binSize
freq = (1/(dt*binSize))*np.arange(binSize)
L = np.arange(1,np.floor(binSize/2),dtype='int') # index array of [1,2,3..... binsize/2] type int
axs[0].clear()
axs[0].plot(f[-binSize:], color='c',LineWidth=1.5,label="MOUSE")
axs[1].clear()
axs[1].plot(freq[L],PSD[L],color='r',LineWidth=2,label="FFT")
ani = FuncAnimation(plt.gcf(),animate,interval=dt)
plt.show()

Animating bisection method with matplotlib animation library

I am interested in math demonstrations. Currently I am working on visualizing numerical methods in python, in particular the bisection method. Below is the code I have written so far.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
def sgn(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def bisect(f,a,b):
fa = f(a)
fb = f(b)
p = a+(b-a)/2
fp = f(p)
if sgn(fa) == sgn(fp):
return p, fp, b, fb
else:
return a, fa, p, fp
def f(x):
return x**2-3
a, b = 1, 2
plt.figure()
plt.subplot(111)
a, fa, b, fb = bisect(f,a,b)
vf = np.vectorize(f)
x = np.linspace(a,b)
y = vf(x)
plt.plot(x, y, color='blue')
plt.plot([a,a], [0,fa], color='red', linestyle="--")
plt.plot([b,b], [0,fb], color='red', linestyle="--")
plt.grid()
plt.show()
I have three problems I wish to solve. First, I want to be able to call the bisect function multiple times and each time I would like to redraw the plot with the new data. Second, I would like to restart the animation after applying the bisect function some specified number of times. Third, I would like to retain the original axes of the figure before the bisection method is called i.e. I would like to keep the x-range as [1,2] and the y-range as $[-2,1]$. Any help will be much appreciated.
I found a solution to my problems through much trial and error.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
def sgn(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def bisect(f,a,b):
fa = f(a)
fb = f(b)
p = a+(b-a)/2
fp = f(p)
if sgn(fa) == sgn(fp):
return p, b
else:
return a, p
def bisection_method(f,a,b,n):
for i in range(n):
a,b = bisect(f,a,b)
return a,b
def f(x):
return x**2-3
xmin, xmax = 1, 2
yrange = f(xmin), f(xmax)
ymin, ymax = min(yrange), max(yrange)
vf = np.vectorize(f)
x = np.linspace(xmin,xmax)
y = vf(x)
epsilon = 0.1
# Initialize figure
fig = plt.figure()
ax = plt.axes(xlim=(xmin-epsilon,xmax+epsilon), ylim=(ymin,ymax))
curve, = ax.plot([],[], color='blue')
left, = ax.plot([],[],color='red')
right, = ax.plot([],[],color='red')
# Figure reset between frames
def init():
left.set_data([],[])
right.set_data([],[])
curve.set_data([],[])
return left, right, curve,
# Animation of bisection
def animate(i):
a, b = bisection_method(f,xmin,xmax,i)
left.set_data([a,a],[ymin,ymax])
right.set_data([b,b],[ymin,ymax])
curve.set_data(x,y)
return left, right, curve,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=15, interval=700, blit=True)
plt.grid()
plt.show()
You can simply change your code to:
plt.plot([a,a], [0,fa], color='red', linestyle="--",hold=TRUE) which would basically allow you to plot multiple points without resetting the plot and once you have plotted a number of times you can reset using hold=FALSE. Hope this makes sense.

Categories

Resources