I'm trying to add to an animated plot of a random variable the dates in the x axes.
I've tried different things but the code is working just with a static x-axes array..
I made a small function to update the dates array T and random var array y the I called shift().
To see how the basic code (no dates) is behaving you need to uncomment every line that is followed by "# uncomment 1". Viceversa uncomment every line that has "# uncomment 0".
I don't know why I can't plot the dates in the x-axes.
This below is the code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import pandas as pd
import time
import datetime
plt.rcParams.update({'axes.facecolor':'lightgray'})
plt.rcParams.update({'figure.facecolor':'gray'})
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
# ax = plt.axes()
ax = plt.axes(xlim=(0, 2), ylim=(-8, 8))
line, = ax.plot([], [], lw=2)
def shift(y_arr,y_i,cont_cascata):
print("cont_cascata:",cont_cascata)
if type(y_arr)==list:
y_arr.pop(0)
y_arr = y_arr+[y_i]
if type(y_arr) is np.ndarray:
print("np.array..")
y_arr = np.delete(y_arr, 0) # togliamo primo
y_arr = np.append(y_arr, y_i) # aggiungiamo ultimo
return y_arr
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
# pd._libs.tslibs.timestamps.Timestamp
def ts_array(n):
t0 = pd.Timestamp(2018,1,1,12,30)
T = []
for i in range(n):
t_i = t0+pd.Timedelta(minutes=i)
T = T+[t_i]
return T
# tarr = ts_array(n=100)
def animate(i):
global y,x,T
n = 100
if i==0:
y = np.round(np.random.normal(loc=0, scale=2, size=n), decimals=2)
x = np.linspace(0, 2, n) # uncomment 0
T = ts_array(n) # uncomment 1
y_i = np.round(np.random.normal(loc=0,scale=2),decimals=2)
t_i = T[-1]+pd.Timedelta(minutes=1) # uncomment1
y = shift(y_arr=y,y_i=y_i, cont_cascata=i)
T = shift(y_arr=T,y_i=t_i,cont_cascata=i) # uncomment 1
T = pd.DatetimeIndex(T) # uncomment 1
T = T.to_pydatetime() # uncomment 1
# line.set_data(x, y) # uncomment 0
line.set_data(T,y) # uncomment 1
time.sleep(0.5)
return line,
print("animate")
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
What should I do to make this code work properly? Thanks
You need to adjust the xlim of the axes to account for date time. Inside animate, just after line.set_data(T,y), try adding this:
ax.set_xlim(T.min(), T.max())
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.dates as mdates
import pandas as pd
import time
import datetime
plt.rcParams.update({'axes.facecolor': 'lightgray'})
plt.rcParams.update({'figure.facecolor': 'gray'})
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure() # include
ax = plt.axes() # include
# ax = plt.axes(xlim=(0, 2), ylim=(-8, 8))
line, = ax.plot([], [], lw=2)
## tilt dates
plt.setp(ax.xaxis.get_majorticklabels(), rotation=35)
def shift(y_arr, y_i, cont_cascata):
# print("cont_cascata:",cont_cascata)
if type(y_arr) == list:
y_arr.pop(0)
y_arr = y_arr + [y_i]
if type(y_arr) is np.ndarray:
# print("np.array..")
y_arr = np.delete(y_arr, 0) # togliamo primo
y_arr = np.append(y_arr, y_i) # aggiungiamo ultimo
return y_arr
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
line.axes.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M")) # "%Y-%m-%d %H:%M:%S"
return line,
# animation function. This is called sequentially
# pd._libs.tslibs.timestamps.Timestamp
def ts_array(n):
t0 = pd.Timestamp(2018, 1, 1, 12, 00)
T = []
for i in range(n):
t_i = t0 + pd.Timedelta(minutes=i)
T = T + [t_i]
return T
def animate(i):
global y, x, T
print("i:", i)
n = 10
if i == 0:
y = np.round(np.random.normal(loc=0, scale=2, size=n), decimals=2)
x = np.linspace(6, 2, n) # uncomment 0
T = ts_array(n) # uncomment 1
y_i = np.round(np.random.normal(loc=6, scale=2), decimals=2)
t_i = T[-1] + pd.Timedelta(minutes=1) # uncomment1
y = shift(y_arr=y, y_i=y_i, cont_cascata=i)
T = shift(y_arr=T, y_i=t_i, cont_cascata=i) # uncomment 1
T = pd.DatetimeIndex(T) # uncomment 1
T = T.to_pydatetime() # uncomment 1
# line.set_data(x, y) # uncomment 0
line.set_data(T, y) # uncomment 1
ax.relim(visible_only=True)
ax.autoscale()
# ax.autoscale_view(True,True,True)
# time.sleep(0.5)
return line,
print("animate")
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=500) # blit = True
plt.show()
Related
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())
I'm simulating real-time data by plotting values against system time every interval using FuncAnimation. I want to plot the y-axis (positional data, essentially a saw-tooth) against the current time of the animation interval over a range from the system time to 12 hours from then (i.e. 5pm- 5am). When I set this limit, no line is being drawn on the graph. What am I doing wrong?
fig, ax = plt.subplots(figsize=(10, 6))
xs = []
ys = []
random.seed(None, 2)
getcontext().prec = 3
gateStart = random.randint(0, 100)
waiting = False
returning = False
paused = False
currentTime = dt.datetime.today()
# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
global gateStart, waiting, returning, paused
print(gateStart)
if gateStart == 100:
returning = True
elif gateStart == 0:
returning = False
if returning:
gateStart = round(gateStart - 0.1, 1)
else:
gateStart = round(gateStart + 0.1, 1)
# Add x and y to lists
xs.append(dt.datetime.now())
ys.append(gateStart)
# Draw x and y lists
ax.clear()
ax.plot(xs, ys)
# Format plot
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
endTime = currentTime + datetime.timedelta(hours=12)
plt.xlim([currentTime, endTime])
fig.autofmt_xdate()
plt.subplots_adjust(bottom=0.30)
plt.title('Longwall Data')
plt.ylabel('Shearer Position')
plt.ylim(0, 100)
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=1)
plt.show()
The reason for this is that the range of time series data handled by matplotlib is large, see this for matplotlib dates that appear to be too small to draw the data you are dealing with. So I draw the numbers on the x-axis as variable i, and the current time in string format as a list. For the sake of sample creation, my code is set to milliseconds. In your case it will be ts.append(dt.datetime.now().strftime('%H')).
Is this answer what you intend?
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.dates as mdates
import random
import datetime as dt
from IPython.display import HTML
import decimal
import numpy as np
from matplotlib.animation import PillowWriter
xs = []
ys = []
ts = []
random.seed(2021, 2)
decimal.getcontext().prec = 3
gateStart = random.randint(0, 100)
waiting = False
returning = False
paused = False
currentTime = dt.datetime.today()
fig = plt.figure(figsize=(14, 6))
ax = plt.axes(xlim=(0,100),ylim=(0, 100))
line, = ax.plot([], [], 'r-', lw=3)
ax.set_ylabel('Shearer Position')
ax.set_title('Longwall Data')
# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
global gateStart, waiting, returning, paused
if gateStart == 100:
returning = True
elif gateStart == 0:
returning = False
if returning:
gateStart = round(gateStart - 1, 1)
else:
gateStart = round(gateStart + 1, 1)
# print(dt.datetime.now())
# Add x and y to lists
xs.append(i)
ys.append(gateStart)
ts.append(dt.datetime.now().strftime('%f')) # Microsecond(6)
# Draw x and y lists
line.set_data(xs, ys)
# Format plot
ax.set_xticks(np.arange(len(ts)))
ax.set_xticklabels(ts, rotation=90)
ani = FuncAnimation(fig, animate, fargs=(xs, ys), interval=200, repeat=False)
# ani.save('realtime_plot_anim.gif', writer='pillow')
plt.show()
# jupyter lab
# plt.close()
# HTML(ani.to_html5_video())
I have a dataframe that I want to animate (line chart) using matplotlib. My x and y values:
here x = df.index and y = df['Likes']
x y
0 200000
1 50000
2 1000000
.so on.. ....
Code I tried:
from matplotlib import pyplot as plt
from matplotlib import animation
import pandas as pd
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
fig = plt.figure()
ax = plt.axes(xlim=(0, 18), ylim=(6514, 209124))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(df.index[i], df['Likes'][i])
return line,
anim = animation.FuncAnimation(fig, animate, frames=len(df['Likes']), init_func=init, interval=300, blit=True)
plt.show()
I have tried this, but it is showing blank output with no error message. I am using python 3.83, windows machine. Can I do this using numpy? Almost all of the examples used numpy data in FuncAnimation.
I have solved it myself, I have used code of "vkakerbeck" from github as a guide to add more data points:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
dg = df['Likes']
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, len(dg))
ax.set_ylim(0, dg.max() * 1.04) # multiplied with 1.04 to add some gap in y-axis
line, = ax.plot(0, 0)
This part is for formatting
ax.set_xlabel('Part No')
ax.set_ylabel('Number of Likes')
ax.set_title('Likes in Call of Duty 2019')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
fig = plt.gcf()
fig.set_size_inches(12.8, 7.2) # 720p output
I have used this from that guide to add more data points to make the animation less jumpy:
x = np.array(dg.index)
y = np.array(dg)
def augment(xold, yold, numsteps):
xnew = []
ynew = []
for i in range(len(xold) - 1):
difX = xold[i + 1] - xold[i]
stepsX = difX / numsteps
difY = yold[i + 1] - yold[i]
stepsY = difY / numsteps
for s in range(numsteps):
xnew = np.append(xnew, xold[i] + s * stepsX)
ynew = np.append(ynew, yold[i] + s * stepsY)
return xnew, ynew
XN, YN = augment(x, y, 3)
augmented = pd.DataFrame(YN, XN)
ylikes = augmented[0].reset_index() # Index reset to avoid key error
Main Function:
def animation_frame(i):
x_data.append(augmented.index[i])
y_data.append(ylikes[0][i])
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
plt.cla()
plt.tight_layout()
anima = animation.FuncAnimation(fig, func=animation_frame, frames=len(augmented), interval=80)
plt.show()
Export as mp4
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, bitrate=1000)
anima.save('lines3.mp4', writer=writer)
I am trying to plot an animation of a physics system. I've worked out the equations and it plots, but there's a second plot I don't want that keeps showing up and I can't get it to stop.
import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
# Input constants
m = 10 # mass (kg)
L = 4 # length (m)
g = 9.81 # gravity (m/s^2)
dt = 0.1 # time step size (seconds)
t_max = 40 # max sim time (seconds)
num_steps = 1 + int(t_max/dt)
theta_0 = np.pi/2 # initial angle (radians)
theta_dot_0 = 0 # initial angular velocity (rad/s)
state0 = [theta_0,theta_dot_0]
# Get timesteps
time_index = np.arange(0, t_max + dt, dt)
def derivatives(state, time_index, L=L, m=m, g=g):
theta_dot = state[1]
theta_ddot = -g*np.sin(state[0])/L
return theta_dot,theta_ddot
output = integrate.odeint(derivatives, state0, time_index)
theta = output[:,0]
theta_dot = output[:,1]
fig = plt.figure()
ax = fig.add_subplot(111, autoscale_on=True, xlim=(-2*L, 2*L), ylim=(-2*L, 2*L))
ax.set_aspect('equal')
ax.grid()
line, = ax.plot([], [], 'o-', lw=2)
time_template = 'time = %.1fs'
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
x = L*np.sin(theta)
y = -L*np.cos(theta)
def init():
# create empty object to put the points in
line.set_data([], [])
time_text.set_text('')
return line, time_text
def animate(t):
x_t = [0, x[t]]
y_t = [0, y[t]]
# add the point to the line
line.set_data(x_t, y_t)
# add the time text to plot
# time_template will be the text next to thetime
time_text.set_text(time_template % (t*dt))
return line, time_text
# we don't want the range of the steps to start at 0
# start it at one
ani = animation.FuncAnimation(fig, animate, range(1, num_steps),
interval=dt*1000, blit=True, init_func=init)
rc('animation', html='jshtml')
#ani.save('pendulum.mp4', fps=15)
ani
Here's the output:
The plot I want to get rid of is the one I circled with red. This is the entirety of my code, so it should be completely reproducible.
I tried several variations of trimming the plotting code but I wasn't able to debug why it's happening.
How can I get rid of this second plot?
A simple plt.close() before your call to ani will do the job.
Last few lines:
ani = animation.FuncAnimation(fig, animate, range(1, num_steps),
interval=dt*1000, blit=True, init_func=init)
rc('animation', html='jshtml')
#ani.save('pendulum.mp4', fps=15)
plt.close()
ani
Demo:
More info at this link.
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()