Variable definition with function name as prefix - python

I have never seen this use before, in the following code, there is a definition data_gen.t = 0, data_gen is not a class, looks we could use function as prefix and define the variable as fun.*, what is the point here? I use a different definition such as xx = 0 instead of data_gen.t = 0, and replace t = data_gen.t with t = xx, everything works well.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def data_gen():
t = data_gen.t
cnt = 0
while cnt < 1000:
cnt+=1
t += 0.05
yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)
data_gen.t = 0
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_ylim(-1.1, 1.1)
ax.set_xlim(0, 5)
ax.grid()
xdata, ydata = [], []
def run(data):
# update the data
t,y = data
xdata.append(t)
ydata.append(y)
xmin, xmax = ax.get_xlim()
if t >= xmax:
ax.set_xlim(xmin, 2*xmax)
ax.figure.canvas.draw()
line.set_data(xdata, ydata)
return line,
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=10,
repeat=False)
plt.show()

Related

Unable to refresh plt.axhline() in matplotlib

I'm just trying to make a live graph using matplotlib.However I couldn't find a way to draw-remove-redraw axhline(). My aim is to show a horizontal line of newest value of Y axis values and of course remove the recent horizontal line.
`
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
import time
from random import randrange
style.use("fivethirtyeight")
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
#ax1 = plt.subplot()
second = 1
xs = list()
ys = list()
ann_list = []
a = 0
ten = 10
def animate(i):
global second
global a, ten
random = randrange(ten)
ys.append(random)
xs.append(second)
second += 1
ax1.plot(xs, ys, linestyle='--', marker='o', color='b')
plt.axhline(y = ys[-1], linewidth=2, color='r', linestyle='-')
if len(xs) > 2:
plt.axhline(y = ys[-2], linewidth=2, color='r', linestyle='-').remove()
if len(ys) > 20 and len(xs) > 20:
ax1.lines.pop(0)
ys.pop(0)
xs.pop(0)
a += 1
ax1.set_xlim(a, (21 + a))
# ax1.set_ylim(0, 200)
ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()
`
expecting that to only show the newest y axis values with a horizontal line. However horizontal lines doesn't vanish away.
In your code, this:
plt.axhline(y = ys[-2], linewidth=2, color='r', linestyle='-').remove()
doesn't remove the previous axhline; it adds a new axhline at y=ys[-2] and then immediately removes it. So, it effectively does nothing.
You have to remove the same line you inserted with plt.axhline. Save the object returned by this function somewhere, and remove it when the next frame is animated.
Here's a solution with a bit of default mutable argument abuse.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
import time
from random import randrange
style.use("fivethirtyeight")
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
second = 1
xs = list()
ys = list()
ann_list = []
a = 0
ten = 10
def animate(i, prev_axhline=[]):
global second
global a, ten
random = randrange(ten)
ys.append(random)
xs.append(second)
second += 1
ax1.plot(xs, ys, linestyle='--', marker='o', color='b')
if prev_axhline:
prev_axhline.pop().remove()
prev_axhline.append(plt.axhline(y = ys[-1], linewidth=2, color='r', linestyle='-'))
if len(ys) > 20 and len(xs) > 20:
ax1.lines.pop(0)
ys.pop(0)
xs.pop(0)
a += 1
ax1.set_xlim(a, (21 + a))
# ax1.set_ylim(0, 200)
ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()

Python matplotlib.animation Jupyter Notebook

I use Windows 10 / 64 / Google chrome
I found a good set-up for animation over Jupyter with the call %matplotlib notebook as here :
import numpy as np
import scipy.stats as st
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
For exemple, this one is working pretty well :
n = 100
X = st.norm(0,1).rvs(200)
number_of_frames = np.size(X)
def update_hist(num, second_argument):
plt.cla()
plt.hist(X[:num], bins = 20)
plt.title("{}".format(num))
plt.legend()
fig = plt.figure()
hist = plt.hist(X)
ani = animation.FuncAnimation(fig, update_hist, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
But, weirdly the code below doesn't work while it's the same structure, it puzzles me :
X = np.linspace(-5,5, 150)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n, second_argument):
#plt.cla()
plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
plt.plot(X, [fen(sample_sort[:n],h[n],x) for x in X], label = "densité")
plt.title("n = {}".format(n))
fig = plt.figure(6)
plot = plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
Thanks for your help, best regards.
EDIT : You don't have the funciton fen(sample_sort[:n],h[n],x) it is a function from float to float taking a x in argument and returning a flot. The argument sample_sort[:n],h[n] it is just maths things I'm trying to understand some statistics anyway, you can remplace with line with what you want np.cos(N[:n]) for exemple.
EDIT : New code according to the suggestion :
N_max = 100
X = np.linspace(-5,5, N_max )
number_of_frames = np.size(X)
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X, np.array([fen(sample_sort[:n],h[n],x) for x in X]))
ax.set_title("n = {}".format(n))
return lines
fig = plt.figure()
ax = plt.axes(xlim=(-4, 4), ylim=(-0.01, 1))
ax.plot(X, np.array([f(x) for x in X]), 'y-', lw=2, label="d")
lines, = ax.plot([], [], 'b--', lw=3, label="f")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, repeat = False )
plt.show()
EDIT 2:
I found a code over internet which does exactly what I would like
# Fermi-Dirac Distribution
def fermi(E: float, E_f: float, T: float) -> float:
return 1/(np.exp((E - E_f)/(k_b * T)) + 1)
# Create figure and add axes
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
# Get colors from coolwarm colormap
colors = plt.get_cmap('coolwarm', 10)
# Temperature values
T = np.array([100*i for i in range(1,11)])
# Create variable reference to plot
f_d, = ax.plot([], [], linewidth=2.5)
# Add text annotation and create variable reference
temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24)
# Set axes labels
ax.set_xlabel('Energy (eV)')
ax.set_ylabel('Fraction')
# Animation function
def animate(i):
x = np.linspace(0, 1, 100)
y = fermi(x, 0.5, T[i])
f_d.set_data(x, y)
f_d.set_color(colors(i))
temp.set_text(str(int(T[i])) + ' K')
temp.set_color(colors(i))
# Create animation
ani = animation.FuncAnimation(fig, animate, frames=range(len(T)), interval=500, repeat=False)
# Ensure the entire plot is visible
fig.tight_layout()
# show animation
plt.show()
What I want to draw is a curve at random because the actual state of the function is unknown. The basic structure looks like this, so please modify it based on this.
import numpy as np
import scipy.stats as st
# %matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# from IPython.display import HTML
# from matplotlib.animation import PillowWriter
X = np.linspace(-5,5, 100)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X[:n], h[:n])
lines2.set_data(X[:n], h[:n]*-1)
ax.set_title("n = {}".format(n))
return lines, lines2
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-1, 1))
lines, = ax.plot([], [], 'y-', lw=2, label="densité")
lines2, = ax.plot([], [], 'b--', lw=3, label="densité2")
ani = animation.FuncAnimation(fig, update_plot, frames=number_of_frames, repeat=False )
plt.show()
# ani.save('lines_ani2.gif', writer='pillow')
# plt.close()
# HTML(ani.to_html5_video())

Animated lissajous curve not visible python

I am very new to animating in python so please bear with me.
So I am trying to make this Lissajous curve animate like the one on this website
I do have code of the lissajous curve stationary if needed. I thought by changing the pi/2 (in the code it's f) to be smaller and bigger would replicate it but the graph doesn't appear. Thank you in advance.
Attempt:
# Import our modules
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
a= 1
A = 1
B = 1
b = 3
c = 1
D = 1
fig = plt.figure()
f=3.14/2
while f > -3.14/2:
f-=1
xdata, ydata = [], []
ax = plt.gca()
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
t = 0.1*i
x = A*np.sin(a*t+f) + c
y = B*np.sin(b*t) + D
xdata.append(x)
ydata.append(y)
line.set_data(xdata, ydata)
# ax.set_facecolor('xkcd:black')
return line,
anim = FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
anim.save('abclogo.gif', writer='pillow')
You have to include the axes limits
ax = plt.gca()
ax.set_xlim(0,2)
ax.set_ylim(0,2)
line, = ax.plot([], [], lw=2)
Alternately, and slightly more efficient would be to not use append inside the animate function by doing the following:
# Import our modules
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
a= 1
A = 1
B = 1
b = 3
c = 1
D = 1
f=3.14/2
while f > -3.14/2:
f-=1
seq=np.arange(0,200,1)
x = A*np.sin(a*0.1*seq+f) + c
y = B*np.sin(b*0.1*seq) + D
fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
def animate(i):
line.set_data(x[:i], y[:i])
return line,
anim = FuncAnimation(fig, animate, frames=len(x),interval=25, blit=True)
anim.save('abclogo.gif', writer='imagemagick')
plt.show()
Edit 2:
FuncAnimation doesn't offer a lot of control. For e.g. you won't be able to access axes elements and modify them. You can achieve better control by making use of for loop as shown here:
import numpy as np
import matplotlib.pyplot as plt
import math
###initializing the parameters##################
M=1
N=2
########setup the plot################
fig, ax = plt.subplots()
t = np.arange(0, 1000, 1)
x = np.sin(M*0.1*t)
y = np.sin(N*0.1*t+math.pi/2.0)
ax.set_xlim(-1.25,1.25)
ax.set_ylim(-1.25,1.25)
##################
for i in t:
phi=np.arange(0,10*math.pi,math.pi/50.)
#phase shifting to give the impression of rotation
y = np.sin(N*0.1*t+phi[i])
line, = ax.plot(x[:i],y[:i],c='black')
plt.pause(0.01)
#remove the track
line.remove()
del line
The animation is shown here

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()

Combine a static plot and an animated plot in matplotlib python

I have a static plot which calculates in one step and a dynamically updating plot (animated). My codes displays it correctly but in different windows, how can I combine it in one plot window.
I am getting solutions for animating two plots simultaneously but not anything with one static and another dynamic
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import math
E50 = 5000
c = 5
phi = math.radians(30)
sig3 = 100
a = c/math.tan(phi)
# deviatoric load
qa = (sig3+a)*(2*math.sin(phi))/(1-math.sin(phi))
print(qa)
ultimateLoad = 200
def hyperbola():
stress = []
strain = []
q = 0
while q < ultimateLoad:
stress.append(q)
eps1 = (qa/(2*E50)) * (q/(qa-q))
strain.append(eps1)
q +=10
return strain, stress
def plotHyperbola():
strain, stress = hyperbola()
plt.plot(strain, stress ,'bo', linewidth=5, label='Existing Kernel' )
def data_gen():
load = 0
while load < ultimateLoad:
load += 10
# finally this yield function should give x any that needs to be plotted
yield load/5000, load
def init():
ax.set_ylim(-1.1, 300)
ax.set_xlim(0, 0.1)
del xdata[:]
del ydata[:]
line.set_data(xdata, ydata)
return line,
fig, ax = plt.subplots()
line, = ax.plot([], [], 'ro', lw=2)
ax.grid()
xdata, ydata = [], []
def run(data):
# update the data
t, y = data
xdata.append(t)
ydata.append(y)
xmin, xmax = ax.get_xlim()
plotHyperbola()
if t >= xmax:
ax.set_xlim(xmin, 2*xmax)
ax.figure.canvas.draw()
line.set_data(xdata, ydata)
return line,
# interval control the time in between each iteration
# repeat whether the whole process needs to be repeated
ani = animation.FuncAnimation(fig, run, data_gen, interval=1,
repeat=False, init_func=init)
plt.show()

Categories