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()
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 have 3 lists which contain the x cordinates, y coordinates and z coordinates respectively. I am trying to track the position in 3D space. I use the below code:
fig = plt.figure()
ax = p3.Axes3D(fig)
def update(num, data, line):
line.set_data(data[:2, :num])
line.set_3d_properties(data[2, :num])
N = 4000
d3d=np.array([xdata,ydata,zdata])
line, = ax.plot(d3d[0, 0:1], d3d[1, 0:1], d3d[2, 0:1], color='blue')
ax.set_xlim3d([2.0, -2.0])
ax.set_xlabel('X')
ax.set_ylim3d([2.0, -2.0])
ax.set_ylabel('Y')
ax.set_zlim3d([0.0, 4.0])
ax.set_zlabel('Z')
ani = animation.FuncAnimation(fig, update, N, fargs=(d3d, line), interval=10000/N, blit=False)
plt.show()
With this I can successfully see the trajectory in blue color. However, I want to see the updated trajectory in blue and want to gray out he previous one:
I tried using below in update function so gray out he previous line:
def update(num, data, line):
line.set_data(data[:2, :num])
line.set_3d_properties(data[2, :num])
if line is not None:
line.set_color('gray')
but this just grays out the whole trajectory. Any help would be appreciated.
We can keep track of the plotted lines and just change their color.
import matplotlib.pyplot as plt
import matplotlib.animation as anim
import numpy as np
fig = plt.figure()
ax = fig.gca(projection="3d")
#random data
np.random.seed(12345)
d3d = np.random.random((3, 12))
line_list = []
#number of line segments to retain in blue before greying them out
line_delay = 4
def init():
ax.clear()
#you can omit the fixed scales, then they will be automatically updated
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 1)
def update(i):
#initializing the plot, emptying the line list
if not i:
init()
line_list[:] = []
#set line color to grey if delay number is exceeded
if len(line_list)>=line_delay:
line_list[-line_delay].set_color("grey")
#plot new line segment
newsegm, = ax.plot(*d3d[:, i:i+2], "blue")
line_list.append(newsegm)
ani = anim.FuncAnimation(fig, update, init_func=init, frames = np.arange(d3d.shape[1]), interval = 300, repeat=True)
plt.show()
This approach has the advantage that we can easily adapt it for better representation of the data - for instance, if we have a lot of data, we can make them fade and remove all invisible lines:
import matplotlib.pyplot as plt
import matplotlib.animation as anim
import numpy as np
fig = plt.figure()
ax = fig.gca(projection="3d")
#random data
np.random.seed(12345)
d3d = np.random.random((3, 40))
#defines the number of disappearing line segments
max_length = 20
line_list = []
def init():
ax.clear()
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 1)
def update(i):
if not i:
init()
line_list[:] = []
else:
#if not the first line segment, change color to grey,
line_list[-1].set_color("grey")
#then reduce gradually the alpha value for all line segments
diff2max = max(0, max_length-len(line_list))
[x.set_alpha((j+diff2max)/max_length) for j, x in enumerate(line_list)]
#delete line segments that we don't see anymore to declutter the space
if len(line_list)>max_length:
del_line = line_list.pop(0)
del_line.remove()
#plot new segment and append it to the list
newsegm, = ax.plot(*d3d[:, i:i+2], "blue")
line_list.append(newsegm)
ani = anim.FuncAnimation(fig, update, init_func=init, frames = np.arange(d3d.shape[1]), interval = 300, repeat=True)
plt.show()
Sample output:
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
I have a dataframe called benchmark_returns and strategy_returns. Both have the same timespan. I want to find a way to plot the datapoints in a nice animation style so that it shows all the points loading in gradually. I am aware that there is a matplotlib.animation.FuncAnimation(), however this typically is only used for a real-time updating of csv files etc but in my case I know all the data I want to use.
I have also tried using the crude plt.pause(0.01) method, however this drastically slows down as the number of points get plotted.
Here is my code so far
x = benchmark_returns.index
y = benchmark_returns['Crypto 30']
y2 = benchmark_returns['Dow Jones 30']
y3 = benchmark_returns['NASDAQ']
y4 = benchmark_returns['S&P 500']
fig, ax = plt.subplots()
line, = ax.plot(x, y, color='k')
line2, = ax.plot(x, y2, color = 'b')
line3, = ax.plot(x, y3, color = 'r')
line4, = ax.plot(x, y4, color = 'g')
def update(num, x, y, y2, y3, y4, line):
line.set_data(x[:num], y[:num])
line2.set_data(x[:num], y2[:num])
line3.set_data(x[:num], y3[:num])
line4.set_data(x[:num], y4[:num])
return line, line2, line3, line4,
ani = animation.FuncAnimation(fig, update, fargs=[x, y, y2, y3, y4, line],
interval = 1, blit = True)
plt.show()
You could try matplotlib.animation.ArtistAnimation. It operates similar to FuncAnimation in that you can specify the frame interval, looping behavior, etc, but all the plotting is done at once, before the animation step. Here is an example
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib.animation import ArtistAnimation
n = 150
x = np.linspace(0, np.pi*4, n)
df = pd.DataFrame({'cos(x)' : np.cos(x),
'sin(x)' : np.sin(x),
'tan(x)' : np.tan(x),
'sin(cos(x))' : np.sin(np.cos(x))})
fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(10,10))
lines = []
artists = [[]]
for ax, col in zip(axs.flatten(), df.columns.values):
lines.append(ax.plot(df[col])[0])
artists.append(lines.copy())
anim = ArtistAnimation(fig, artists, interval=500, repeat_delay=1000)
The drawback here is that each artist is either drawn or not, i.e. you can't draw only part of a Line2D object without doing clipping. If this is not compatible with your use case then you can try using FuncAnimation with blit=True and chunking the data to be plotted each time as well as using set_data() instead of clearing and redrawing on every iteration. An example of this using the same data from above:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib.animation import FuncAnimation
n = 500
nf = 100
x = np.linspace(0, np.pi*4, n)
df = pd.DataFrame({'cos(x)' : np.cos(x),
'sin(x)' : np.sin(x),
'tan(x)' : np.tan(x),
'sin(cos(x))' : np.sin(np.cos(x))})
fig, axs = plt.subplots(2, 2, figsize=(5,5), dpi=50)
lines = []
for ax, col in zip(axs.flatten(), df.columns):
lines.append(ax.plot([], lw=0.5)[0])
ax.set_xlim(x[0] - x[-1]*0.05, x[-1]*1.05)
ax.set_ylim([min(df[col].values)*1.05, max(df[col].values)*1.05])
ax.tick_params(labelbottom=False, bottom=False, left=False, labelleft=False)
plt.subplots_adjust(hspace=0, wspace=0, left=0.02, right=0.98, bottom=0.02, top=0.98)
plt.margins(1, 1)
c = int(n / nf)
def animate(i):
if (i != nf - 1):
for line, col in zip(lines, df.columns):
line.set_data(x[:(i+1)*c], df[col].values[:(i+1)*c])
else:
for line, col in zip(lines, df.columns):
line.set_data(x, df[col].values)
return lines
anim = FuncAnimation(fig, animate, interval=2000/nf, frames=nf, blit=True)
Edit
In response to the comments, here is the implementation of a chunking scheme using the updated code in the question:
x = benchmark_returns.index
y = benchmark_returns['Crypto 30']
y2 = benchmark_returns['Dow Jones 30']
y3 = benchmark_returns['NASDAQ']
y4 = benchmark_returns['S&P 500']
line, = ax.plot(x, y, color='k')
line2, = ax.plot(x, y2, color = 'b')
line3, = ax.plot(x, y3, color = 'r')
line4, = ax.plot(x, y4, color = 'g')
n = len(x) # Total number of rows
c = 50 # Chunk size
def update(num):
end = num * c if num * c < n else n - 1
line.set_data(x[:end], y[:end])
line2.set_data(x[:end], y2[:end])
line3.set_data(x[:end], y3[:end])
line4.set_data(x[:end], y4[:end])
return line, line2, line3, line4,
ani = animation.FuncAnimation(fig, update, interval = c, blit = True)
plt.show()
or, more succinctly
cols = benchmark_returns.columns.values
# or, for only a subset of the columns
# cols = ['Crypto 30', 'Dow Jones 30', 'NASDAQ', 'S&P 500']
colors = ['k', 'b', 'r', 'g']
lines = []
for c, col in zip(cols, colors):
lines.append(ax.plot(benchmark_returns.index, benchmark_returns[col].values, c=c)[0])
n = len(benchmark_returns.index)
c = 50 # Chunk size
def update(num):
end = num * c if num * c < n else n - 1
for line, col in zip(lines, cols):
line.set_data(benchmark_returns.index, benchmark_returns[col].values[:end])
return lines
anim = animation.FuncAnimation(fig, update, interval = c, blit=True)
plt.show()
and if you need it to stop updating after a certain time simply set the frames argument and repeat=False in FuncAnimation().
You can just update the data into the line element like so:
fig = plt.figure()
ax = fig.add_subplot(111)
liner, = ax.plot()
plt.ion()
plt.show()
for i in range(len(benchmark_returns.values)):
liner.set_ydata(benchmark_returns['Crypto 30'][:i])
liner.set_xdata(benchmark_returns.index[:i])
plt.pause(0.01)
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)