Related
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
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.
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:
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()
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.