Animation with matplotlib where points are dynamically added to a graph - python

I've written a simple code which generates random points (x0, y0) between certain values using a while loop. After the coordinates of each point are set, that point is drawn in an empty graph which is showed at the end of the while loop.
However, I would like to set up an animation with matplotlib which would allow me to see the initial graph and the points progressively added to it as the code is calculating them. I've looked for some examples but the ones I found are mainly concerned with waves and so on and I guess I need a slightly different approach.
This is the basic code:
from numpy import *
from pylab import *
import random
figure(figsize=(8,6), dpi=150)
x = np.linspace(-1, 4.5, 250)
h=5
a=0.5
b=4
ylim(-0.5,5.5)
xlim(-1,5.0)
i= 0
while i< 500:
R1 = random.random()
R2 = random.random()
x0 = (b - a)*R1 + a
y0 = h*R2
scatter(x0, y0, 10, color="red")
i = i + 1
show()
Thanks for your help!
EDIT: ANIMATION CODE
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
import matplotlib.animation as animation
import random
fig = plt.figure(figsize=(8,6), dpi=150)
x = np.linspace(-2, 4.5, 250)
h=4
a=1
b=3
hlines(y=h, xmin=1, xmax=3, linewidth=1.5)
vlines(x=a, ymin=0, ymax=4, linewidth=1.5)
vlines(x=b, ymin=0, ymax=4, linewidth=1.5)
ylim(-2.5,10.5)
xlim(-2.5,4.5)
grid()
def data_gen():
i = 0
while i< 1:
R1 = random.random()
R2 = random.random()
x0 = (b - a)*R1 + a
y0 = h*R2
i = i + 1
yield x0, y0
line, = plot([], [], linestyle='none', marker='o', color='r')
ax = gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
xdata, ydata = [], []
def run(data):
x0,y0 = data
xdata.append(x0)
ydata.append(y0)
line.set_data(xdata, ydata)
return line,
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=0.5,
repeat=False)
plt.show()

I do not know if this is exactly what you are looking for; in any case, you can generate random points inside the run function and there plot them. You do not need neither blit = True nor clear the axis from one frame to another.
Here is my code:
from pylab import *
from matplotlib.animation import FuncAnimation
import random
fig = plt.figure(figsize=(8,6), dpi=150)
x = np.linspace(-2, 4.5, 250)
h=4
a=1
b=3
hlines(y=h, xmin=a, xmax=b, linewidth=1.5)
vlines(x=a, ymin=0, ymax=h, linewidth=1.5)
vlines(x=b, ymin=0, ymax=h, linewidth=1.5)
ylim(-2.5,10.5)
xlim(-2.5,4.5)
grid()
ax = gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
def run(i):
R1 = random.random()
R2 = random.random()
x0 = (b - a)*R1 + a
y0 = h*R2
ax.scatter(x0, y0, 10, color='red')
ani = FuncAnimation(fig = fig, func = run, frames = 500, interval = 10, repeat = False)
plt.show()
which produces this animation:
(I cut this animation to 100 points in order to get a lighter file, less than 2 MB; the code above produces an animation wiht 500 points)

Related

Matplotlib Animation : Graph can't appear

Recently I have managed to do the animation. But now I can't make the tangent line went as I want (not even displayed yet). The formula of the tangent line is y=(2/r)(sqrt(1-((r^2)/4))-1)x +r. The formula is obtained from 2 circles equation (C1 and C2). C1(blue) : x^2+y^2=r^2, and C2(green) : (x-1)^2+y^2=1. My goal is to obtain this kind of animation as and my current animation goes like this .
How should the code looks like when the animation looks like the reference one (the first one)? Any comments and answers would be very helpful for me as a beginner, I appreciate it, Thank you.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots(1)
line, = ax.plot([], [], lw=2)
line2, = ax.plot([], [], lw=2)
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(1)
r2 = np.sqrt(4)
# compute x1 and x2
x1 = 1+r*np.cos(theta)
y1 = r*np.sin(theta)
x2 = r2*np.cos(theta)
y2 = r2*np.sin(theta)
# Move left y-axis and bottim x-axis to centre, passing through (0,0)
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
# Eliminate upper and right axes
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
# Show ticks in the left and lower axes only
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
def init():
line.set_data([], [])
return line,
def init2():
line.set_data([], [])
return line2,
def animate(i):
x2 = np.sqrt(i)*np.cos(theta)
y2 = np.sqrt(i)*np.sin(theta)
line.set_data(x2, y2)
return line,
def animate2(i):
x3 = np.linspace(0,r2**2,100)
y3 = ((2/r2)*(np.sqrt(1-(r2**2)/4)-1)*x3)+r2
line.set_data(x3, y3)
return line,
# create the figure
ax.plot(x1,y1)
ax.set_aspect(1)
plt.grid()
anim = animation.FuncAnimation(fig, animate, init_func=init,
interval=1000, blit=False,\
frames=np.hstack([range(0),
range(4)[::-1]]))
anim2 = animation.FuncAnimation(fig, animate2, init_func=init2,
interval=1000, blit=False)
plt.show()
f = r"D:/UNPAR/Semester 2/Pemrograman Komputer/Project/SK.gif"
writergif = animation.PillowWriter(fps=30)
anim.save(f, writer=writergif)
The animation functions need to be combined into one. We will combine them into an initialization function and an animation function.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots(1)
line, = ax.plot([], [], lw=2, color='green')
line2, = ax.plot([], [], lw=2, color='red')
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(1)
r2 = np.sqrt(4)
# compute x1 and x2
x1 = 1+r*np.cos(theta)
y1 = r*np.sin(theta)
x2 = r2*np.cos(theta)
y2 = r2*np.sin(theta)
# create the figure
ax.plot(x1,y1)
ax.set_aspect(1)
plt.grid()
def init():
line.set_data([], [])
line2.set_data([], [])
return line,line2
def animate(i):
x2 = np.sqrt(i)*np.cos(theta)
y2 = np.sqrt(i)*np.sin(theta)
print(max(y2))
line.set_data(x2, y2)
x3 = [0, 4-(0.1*i)]
y3 = [max(y2), 0]
line2.set_data(x3, y3)
return line,line2
anim = animation.FuncAnimation(fig, animate, init_func=init, interval=1000, blit=False,frames=np.arange(10,0,-1))
plt.show()

Continuously change direction of shifting matplotlib animation?

I've been playing with the animation module from matplotlib and I realized I couldn't efficiently make a sine wave loop between two limits (in this case between -180° and 180°).
Like this...
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 0.04), ylim=(-1.5, 1.5))
# initialize moving plots
line1, = ax.plot([], [], linewidth=2, label='sine')
line2, = ax.plot([], [], label='cosine')
ax.legend()
ax.grid()
def animate(i):
step = np.pi/30
# loop by hand...
if i < 30:
phase = i*step
elif 30 <= i < 90:
phase = -i*step
elif 90 <= i < 150:
phase = i*step
elif 150 <= i < 210:
phase = -i*step
else:
phase = i*step
x = np.linspace(0, 0.04, 1000)
y1 = np.sin( 2*np.pi*50*x - phase )
y2 = 0.5*np.cos( 2*np.pi*50*x + phase )
line1.set_data(x, y1)
line2.set_data(x, y2)
print('i:',i) # debug i
return line1, line2
anim = animation.FuncAnimation(fig, animate, interval=250, blit=True)
plt.show()
The reason is because I'm using the i variable, that is used for the frames count and only increases with time. Is there a way to loop indefinitely without writing if conditions until the end of time?
From this answer I found that is posible to refresh the data from the plot, and I've manage to make it loop almost like I wanted.
Adapted example... (workaround not complete)
import matplotlib.pyplot as plt
import numpy as np
def Yvalue(t, phase):
"""Function to plot"""
w = 2*np.pi*50
return np.sin(w*t + phase)
plt.ion() # You probably won't need this if you're embedding things in a tkinter plot...
step = np.pi/30 # steps for phase shifting
t = np.linspace(0, 0.04) # x values
y1 = Yvalue(t, 0) # y values
# starts figure
fig = plt.figure()
ax = plt.axes(xlim=(0, 0.04), ylim=(-1.5, 1.5))
# Returns a tuple of line objects, thus the comma
line1, = ax.plot(t, y1, linewidth=2, label='sine')
# static plot (cosine)
ax.plot(t, np.cos(2*np.pi*50*t), label='cosine static')
ax.legend()
ax.grid()
# initial values
phase = 0
direction = 1 # 1: shifting plot to left; 0: shifting plot to right
UpperLimit = np.pi
LowerLimit = -np.pi
# magic begins...
for something in range(210):
# while 1:
if direction and phase < UpperLimit:
phase += step
direction = 1
else:
phase -= step
direction = 0
# condition that helps to return to left shifting
if phase < LowerLimit:
direction = 1
line1.set_ydata( Yvalue(t, phase) )
fig.canvas.draw()
The problem with is that it doesn't allow me to close the window like it would be with the animation module. Therefore the program must be killed manually when changing the for loop by the while loop.
You would usually not use the animating function itself to calculate its animating parameter. Instead you would provide that parameter as argument to it using the frames argument.
In this case you would want the animating function to take the phase as argument. To create the phase, which is a kind of sawtooth function you can use numpy like
a = np.linspace(0,1, 30, endpoint=False)
phase = np.concatenate((a, 1-a, -a, a-1))*np.pi
Complete example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 0.04), ylim=(-1.5, 1.5))
line1, = ax.plot([], [], linewidth=2, label='sine')
line2, = ax.plot([], [], label='cosine')
ax.legend()
ax.grid()
x = np.linspace(0, 0.04, 1000)
a = np.linspace(0,1, 30, endpoint=False)
phase = np.concatenate((a, 1-a, -a, a-1))*np.pi
def animate(phase):
y1 = np.sin( 2*np.pi*50*x - phase )
y2 = 0.5*np.cos( 2*np.pi*50*x + phase )
line1.set_data(x, y1)
line2.set_data(x, y2)
return line1, line2
anim = animation.FuncAnimation(fig, animate, frames=phase, interval=50, blit=True)
plt.show()
I don't know if I understand your problem because I don't see problem to use second method (used in for loop) inside animate
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 0.04), ylim=(-1.5, 1.5))
# initialize moving plots
line1, = ax.plot([], [], linewidth=2, label='sine')
line2, = ax.plot([], [], label='cosine')
ax.legend()
ax.grid()
# -------------------------------------------------
def func1(t, phase):
"""Function to plot"""
w = 2*np.pi*50
return np.sin( w*t + phase)
def func2(t, phase):
"""Function to plot"""
w = 2*np.pi*50
return np.sin( w*t - phase)
# -------------------------------------------------
t = np.linspace(0, 0.04)
step = np.pi/30
UpperLimit = np.pi
LowerLimit = -np.pi
direction = 1
phase = 0
def animate(i):
global direction
global phase
if direction:
phase += step
if phase >= UpperLimit:
direction = 0
else:
phase -= step
if phase < LowerLimit:
direction = 1
line1.set_data(t, func1(t, phase))
line2.set_data(t, func2(t, phase))
return line1, line2
anim = animation.FuncAnimation(fig, animate, interval=250, blit=True)
plt.show()
Or even without variable direction
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 0.04), ylim=(-1.5, 1.5))
# initialize moving plots
line1, = ax.plot([], [], linewidth=2, label='sine')
line2, = ax.plot([], [], label='cosine')
ax.legend()
ax.grid()
# -------------------------------------------------
def func1(t, phase):
"""Function to plot"""
w = 2*np.pi*50
return np.sin( w*t + phase)
def func2(t, phase):
"""Function to plot"""
w = 2*np.pi*50
return np.sin( w*t - phase)
# -------------------------------------------------
t = np.linspace(0, 0.04)
step = np.pi/30
UpperLimit = np.pi
LowerLimit = -np.pi
phase = 0
def animate(i):
global phase
global step
phase += step
if phase >= UpperLimit or phase <= LowerLimit:
step = -step
line1.set_data(t, func1(t, phase))
line2.set_data(t, func2(t, phase))
return line1, line2
anim = animation.FuncAnimation(fig, animate, interval=250, blit=True)
plt.show()

Creating animation in python from Octave code

I am new to coding and have been trying to create an animation in python from a previously generated code in Octave (below). So far I have not had any success, besides plotting an unanimated scatter plot. Any tips or help would be greatly appreciated.
clear;
N = 100;
NT = 200;
for i=1:N
x(i) = 0;
y(i) = 0;
end
plot(x,y,'o');
axis([0 20 -10 10]);
for k = 1 : NT
x = x + normrnd(0.1,0.1,1,N);
y = y + normrnd(0,0.1,1,N);
temp = mod(k,1);
if temp == 0
plot(x,y,'s');
axis([0 20 -10 10]);
drawnow
end
end
Here is one of the many attempts that did not work (below).
import numpy as np
import matplotlib as plt
from pylab import *
from matplotlib import animation
import random
n=100
nt=2000
m=20
mu=0
sigma=0.1
p=100
fig = plt.figure()
ax = plt.axes(xlim=(-10, 10), ylim=(-10, 10))
scat, = ax.plot([], [])
# initialization function: plot the background of each frame
def init():
x = 0
y = 0
return scat,
# animation function. This is called sequentially
def animate(i):
for k in range (1,nt):
x = x+ np.random.normal(mu, sigma, n)
return scat,
for i in range (1,nt):
y = y+ np.random.normal(mu, sigma, n)
return scat,
anim = animation.FuncAnimation(fig, animate,
init_func=init,frames=200, interval=20, blit=True)
plt.show()
you could do the animation using Octave of Python.
For the python script I think that one of the problems was to set a return within a loop, therefore several times for one function def animate. Looking at the animation doc https://matplotlib.org/api/animation_api.html I edited your example and got this which I think might be usefull:
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
n=100
sigma=0.1
nt=2000
fig, ax = plt.subplots()
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
def init():
ax.set_xlim( 0, 20)
ax.set_ylim(-10, 10)
return ln,
def update(frame):
global xdata
global ydata
xdata = xdata + np.random.normal(0.1, sigma, n)
ydata = ydata + np.random.normal(0.0, sigma, n)
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
You could also do the animation using octaves print command to generate several pngs and use gimp to produce a gif or embbed in LaTeX the pngs using animate.
Best,
Jorge

Connector patch between subplots with animation not visible (matplotlib)

I am using an artist animation method with 5 subplots. There is one static plot on the left, with 3 smaller animated imshow plots to the right (the colorbar is the 5th). I have successfully used ConnectionPatch to connect subplots to show where the data is coming from, but only on static plots. No matter what I try, I can't seem to get the patches to show up on the animation. I've tried to include the patch in the image artist list, tried to update the figure with the artist instead of the axis (which I guess doesn't make much sense), among other things. It will be very difficult to extract a working example due to the complexity of the plot, but maybe someone has a tip.
Could setting the facecolor to 'white' with the animation savefig_kwargs be covering up the connector lines? If so, how do I change the z order of the patch/facecolor?
Without a minimal working example, I can only tell you that it is possible to use a ConnectionPatch in an animation. However, as seen below, one has to recreate it for every frame.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import matplotlib.gridspec as gridspec
from matplotlib.patches import ConnectionPatch
import matplotlib.animation
plt.rcParams["figure.figsize"] = np.array([6,3.6])*0.7
x = np.linspace(-3,3)
X,Y = np.meshgrid(x,x)
f = lambda x,y: (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)+1.5
Z = f(X,Y)
bins=np.linspace(Z.min(), Z.max(), 16)
cols = plt.cm.PuOr((bins[:-1]-Z.min())/(Z.max()-Z.min()))
gs = gridspec.GridSpec(2, 2, height_ratios=[34,53], width_ratios=[102,53])
fig = plt.figure()
ax=fig.add_subplot(gs[:,0])
ax2=fig.add_subplot(gs[0,1])
ax3=fig.add_subplot(gs[1,1])
ax.imshow(Z, cmap="PuOr")
rec = plt.Rectangle([-.5,-.5], width=9, height=9, edgecolor="crimson", fill=False, lw=2)
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[9.5,4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_artist(conp)
ax.add_artist(rec)
im = ax3.imshow(Z[:9,:9], cmap="PuOr", vmin=Z.min(), vmax=Z.max())
ticks = np.array([0,4,8])
ax3.set_yticks(ticks); ax3.set_xticks(ticks)
ax2.hist(Z[:9,:9].flatten(), bins=bins)
def ins(px,py):
global rec, conp, histpatches
ll = [px-.5,py-.5]
rec.set_xy(ll)
conp.remove()
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[px+9.5,py+4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_patch(conp)
data = Z[px:px+9,py:py+9]
im.set_data(data)
ax3.set_xticklabels(ticks+px)
ax3.set_yticklabels(ticks+py)
ax2.clear()
ax2.set_ylim(0,60)
h, b_, patches = ax2.hist(data.flatten(), bins=bins, ec="k", fc="#f1a142")
[pat.set_color(cols[i]) for i, pat in enumerate(patches)]
def func(p):
px,py = p
ins(px, py)
phi = np.linspace(0.,2*np.pi)
r = np.sin(2*phi)*20+np.pi/2
xr = (r*np.cos(phi)).astype(np.int8)
yr = (r*np.sin(phi)).astype(np.int8)
plt.subplots_adjust(top=0.93,bottom=0.11,left=0.04,right=0.96,hspace=0.26,wspace=0.15)
frames = np.c_[xr+20, yr+20]
ani = matplotlib.animation.FuncAnimation(fig, func, frames=frames, interval=300, repeat=True)
plt.show()

How to draw vertical lines interactively in matplotlib?

I have a time series plot and I need to draw a moving vertical line to show the point of interest.
I am using the following toy example to accomplish the same. However, it prints all the lines at the same time while I wanted to show these vertical line plotting one at a time.
import time
ion() # turn interactive mode on
# initial data
x = arange(-8, 8, 0.1);
y1 = sin(x)
y2 = cos(x)
line1, = plt.plot(x, y1, 'r')
xvals = range(-6, 6, 2);
for i in xvals:
time.sleep(1)
# update data
plt.vlines(i, -1, 1, linestyles = 'solid', color= 'red')
plt.draw()
If I understood well, you want to use the animation tools of matplotlib. An example (adapted from the doc):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
X_MIN = -6
X_MAX = 6
Y_MIN = -1
Y_MAX = 1
X_VALS = range(X_MIN, X_MAX+1) # possible x values for the line
def update_line(num, line):
i = X_VALS[num]
line.set_data( [i, i], [Y_MIN, Y_MAX])
return line,
fig = plt.figure()
x = np.arange(X_MIN, X_MAX, 0.1);
y = np.sin(x)
plt.scatter(x, y)
l , v = plt.plot(-6, -1, 6, 1, linewidth=2, color= 'red')
plt.xlim(X_MIN, X_MAX)
plt.ylim(Y_MIN, Y_MAX)
plt.xlabel('x')
plt.ylabel('y = sin(x)')
plt.title('Line animation')
line_anim = animation.FuncAnimation(fig, update_line, len(X_VALS), fargs=(l, ))
#line_anim.save('line_animation.gif', writer='imagemagick', fps=4);
plt.show()
Resulting gif looks like this:
Could you try calling plt.draw after plt.vlines? plt.draw is used to interactively redraw the figure after its been modified.

Categories