How to draw only one, the newest point selected by pick_event? - python

I want to pick (add) marker to the curve. The marker may change the position many times, however eventually I need to plot only the newest (updated) marker and remove the old.
Any ideas?
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots()
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * 2 * np.pi * t)for i in range(10):
pt, = ax1.plot(t, s, picker=5)
def onpick(event):
if event.artist != pt:
return True
if not len(event.ind):
return True
ind = event.ind[0]
ax1.plot(t[ind], s[ind], '|r', markersize='20')
fig.canvas.draw()
return True
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

Instead of calling a new plot() and creating a new artist at every click, simply create an empty artist at initialization stage, and update its coordinates in onpick():
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots()
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * 2 * np.pi * t)
pt, = ax1.plot(t, s, picker=5)
mark, = ax1.plot([], [], '|r', markersize='20')
def onpick(event):
if event.artist != pt:
return True
if not len(event.ind):
return True
ind = event.ind[0]
mark.set_data(t[ind], s[ind])
fig.canvas.draw()
return True
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
EDIT: same principle using N curves and N markers
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots()
t = np.arange(0.0, 1.0, 0.01)
ss = [np.sin(2 * 2 * np.pi * t),
np.cos(3 * 2 * np.pi * t),
np.sin(0.5 * 2 * np.pi * t)]
cs = ['b','r', 'g']
ms = ['|','o','D']
lines = [ax1.plot(t,s,'-',color=c, picker=5)[0] for s,c in zip(ss,cs)]
markers = [ax1.plot([],[],lw=0, marker=m, ms=20, color=c)[0] for m,c in zip(ms,cs)]
def onpick(event):
point_idx = event.ind[0]
art_idx = None
for i,l in enumerate(lines):
if event.artist == l:
art_idx = i
break
if art_idx is not None:
markers[art_idx].set_data(t[point_idx], ss[art_idx][point_idx])
fig.canvas.draw()
return True
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

Related

Wrong matplotlib animation

I have the following code that should draw a cycloid with animation and save it to a gif
but after running the program, a white square appears that covers everything, I can't find the reason cycloid_animation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation, PillowWriter
plt.rcParams['animation.html'] = 'html5'
R = 1
def circle(a, b, r):
# (a,b): the center of the circle
# r: the radius of the circle
# T: The number of the segments
T = 100
x, y = [0]*T, [0]*T
for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
x[i] = a + r*np.cos(theta)
y[i] = b + r*np.sin(theta)
return x, y
# Calculate the cycloid line
thetas = np.linspace(0,4*np.pi,100)
cycloid_x = R*(thetas-np.sin(thetas))
cycloid_y = R*(1-np.cos(thetas))
cycloid_c = R*thetas
fig = plt.figure()
lns = []
trans = plt.axes().transAxes
for i in range(len(thetas)):
x,y = circle(cycloid_c[i], R, R)
ln1, = plt.plot(x, y, 'g-', lw=2)
ln2, = plt.plot(cycloid_x[:i+1] ,cycloid_y[:i+1], 'r-', lw=2)
ln3, = plt.plot(cycloid_x[i], cycloid_y[i], 'bo', markersize=4)
ln4, = plt.plot([cycloid_c[i], cycloid_x[i]], [R,cycloid_y[i]], 'y-', lw=2)
tx1 = plt.text(0.05, 0.8, r'$\theta$ = %.2f $\pi$' % (thetas[i]/np.pi), transform=trans)
lns.append([ln1,ln2,ln3,ln4,tx1])
plt.xlim(0,15)
plt.ylim(0,3)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.axes().set_aspect('equal')
ani = animation.ArtistAnimation(fig, lns, interval=50)
#ani.save('cycloid_ArtistAnimation.mp4',writer='ffmpeg')
ani.save('cycloid_ArtistAnimation.gif',writer='pillow')
ani
Each time you call plt.axis() you are creating a new axis on top of the figure. Since what you want is to get the current axis and then apply the transformations, after creating the figure you should call plt.gca() to get the current axis and use that instead.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation, PillowWriter
plt.rcParams['animation.html'] = 'html5'
R = 1
def circle(a, b, r):
# (a,b): the center of the circle
# r: the radius of the circle
# T: The number of the segments
T = 100
x, y = [0]*T, [0]*T
for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
x[i] = a + r*np.cos(theta)
y[i] = b + r*np.sin(theta)
return x, y
# Calculate the cycloid line
thetas = np.linspace(0,4*np.pi,100)
cycloid_x = R*(thetas-np.sin(thetas))
cycloid_y = R*(1-np.cos(thetas))
cycloid_c = R*thetas
fig = plt.figure()
lns = []
trans = plt.gca().transAxes #<=== HERE
for i in range(len(thetas)):
x,y = circle(cycloid_c[i], R, R)
ln1, = plt.plot(x, y, 'g-', lw=2)
ln2, = plt.plot(cycloid_x[:i+1] ,cycloid_y[:i+1], 'r-', lw=2)
ln3, = plt.plot(cycloid_x[i], cycloid_y[i], 'bo', markersize=4)
ln4, = plt.plot([cycloid_c[i], cycloid_x[i]], [R,cycloid_y[i]], 'y-', lw=2)
tx1 = plt.text(0.05, 0.8, r'$\theta$ = %.2f $\pi$' % (thetas[i]/np.pi), transform=trans)
lns.append([ln1,ln2,ln3,ln4,tx1])
plt.xlim(0,15)
plt.ylim(0,3)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.gca().set_aspect('equal') #<=== And HERE
ani = animation.ArtistAnimation(fig, lns, interval=50)
#ani.save('cycloid_ArtistAnimation.mp4',writer='ffmpeg')
ani.save('cycloid_ArtistAnimation.gif',writer='pillow')

Plot confidence interval of a duration series

I measured the duration of 6000 requests.
I got now an Array of 6000 elements. Each element represents the duration of a connection request in milliseconds.
[3,2,2,3,4,2,2,4,2,3,3,4,2,4,4,3,3,3,4,3,2,3,5,5,2,4,4,2,2,2,3,5,3,2,2,3,3,3,5,4........]
I want to plot the confidence interval in Python and in a clearly arranged manner.
Do you have any Idea how I should plot them?
From what I understood this code should answer your question
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from statistics import NormalDist
X = np.random.sample(100)
data = ((X - min(X)) / (max(X) - min(X))) * 3 + 3
confidence_interval = 0.95
def getCI(data, ci):
normalDist = NormalDist.from_samples(data)
z = NormalDist().inv_cdf((1 + ci) / 2.)
p = normalDist.stdev * z / ((len(data) - 1) ** .5)
return normalDist.mean, normalDist.mean - p, normalDist.mean + p
avg, lower, upper = getCI(data, confidence_interval)
sns.set_style("whitegrid")
plt.figure(figsize=(8, 4))
sns.histplot(data, bins = 10)
plt.axvspan(lower, upper, facecolor='r', alpha=0.2)
plt.axvline(avg, color = 'b', label = 'Average')
plt.ylabel("Operations")
plt.xlabel("Connection Request Duration (ms)")
plt.show()
For boxplot:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from statistics import NormalDist
X = np.random.sample(100)
data = ((X - min(X)) / (max(X) - min(X))) * 3 + 3
confidence_interval = 0.95
def getCI(data, ci):
normalDist = NormalDist.from_samples(data)
z = NormalDist().inv_cdf((1 + ci) / 2.)
p = normalDist.stdev * z / ((len(data) - 1) ** .5)
return normalDist.mean, normalDist.mean - p, normalDist.mean + p
avg, lower, upper = getCI(data, confidence_interval)
sns.set_style("whitegrid")
plt.figure(figsize=(8, 4))
sns.boxplot(data = data, orient = "h")
plt.axvspan(lower, upper, facecolor='r', alpha=0.4)
plt.axvline(avg, color = 'b', label = 'Average')
plt.ylabel("Operations")
plt.xlabel("Connection Request Duration (ms)")
plt.yticks([0],["Server Retry Request Delay"])
plt.savefig("fig.png")
plt.show()
For Multiple Plots:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from statistics import NormalDist
X1, X2 = np.random.sample(100), np.random.sample(100)
data1, data2 = ((X1 - min(X1)) / (max(X1) - min(X1))) * 3 + 3, ((X2 - min(X2)) / (max(X2) - min(X2))) * 2 + 3
confidence_interval = 0.95
def getCI(data, ci):
normalDist = NormalDist.from_samples(data)
z = NormalDist().inv_cdf((1 + ci) / 2.)
p = normalDist.stdev * z / ((len(data) - 1) ** .5)
return normalDist.mean, normalDist.mean - p, normalDist.mean + p
sns.set_style("whitegrid")
avg1, lower1, upper1 = getCI(data1, confidence_interval)
avg2, lower2, upper2 = getCI(data2, confidence_interval)
fig = plt.figure(figsize=(12, 6))
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex = ax1, sharey = ax1)
sns.boxplot(data = data1, orient = "h", ax = ax1)
ax1.axvspan(lower1, upper1, facecolor='r', alpha=0.4)
ax1.axvline(avg1, color = 'b', label = 'Average')
sns.boxplot(data = data2, orient = "h", ax = ax2)
ax2.axvspan(lower2, upper2, facecolor='r', alpha=0.4)
ax2.axvline(avg2, color = 'b', label = 'Average')
ax2.set_xlabel("Connection Request Duration (ms)")
plt.setp(ax1.get_xticklabels(), visible=False)
plt.setp(ax1.get_yticklabels(), visible=False)
plt.setp(ax2.get_yticklabels(), visible=False)
fig.text(0.08, 0.5, "Operations", va='center', rotation='vertical')
plt.show()

matplotlib animation: datas appears on the plot at the beginning

I am trying to calculate one of the basic decay simulation and plot the results as animation. Without animation results fine but when i try to create animation both line appears fully developed at the first time step. The change over time is not as it should be. What am I doing wrong? I'm open to suggestions.
Original plot:
plot
Here the code:
# I135 Xe135 decay.
"""
EQUATIONS:
dIdt = (-Lambda_I * N_I)
dXedt = ((-Lambda_Xe * N_Xe) + (Lambda_I * N_I))
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from scipy.integrate import odeint
def model(z,t):
"""
INITIAL CONDITIONS:
I(0) = 1e8 atoms
Xe(0) = 0 atom
PARAMETERS:
Lambda_I
Lambda_Xe
"""
Lambda_I = (np.log(2) / 6.57)
Lambda_Xe = (np.log(2) / 9.2)
N_I = z[0]
N_Xe = z[1]
dIdt = (-Lambda_I * N_I)
dXedt = ((-Lambda_Xe * N_Xe) + (Lambda_I * N_I))
return [dIdt,dXedt]
z0 = [1e8, 0.0] # initial conditions for N_Xe and N_I
n = 10000
max_time = 100
t = np.linspace(0,max_time,n)
N_I = np.zeros(n)
N_Xe = np.zeros(n)
# Solution
for i in range(n):
z = odeint(model,z0,t)
z0 = z[1]
N_I[i] = z0[0]
N_Xe[i] = z0[1]
# Graph and animation
fig, ax = plt.subplots(figsize=(8,8))
ax.set_ylim(0, 1e8)
ax.set_xlim(0, max_time)
line1, = ax.plot(N_I, 'b-', linewidth=2)
line2, = ax.plot(N_Xe,'g-.', linewidth=2)
plt.rcParams['font.size'] = '14'
plt.minorticks_on()
plt.tick_params(axis="both", which="major", length=12, labelsize=12, width=1, color="black")
plt.tick_params(axis="both", which="minor", length=6, labelsize=10, width=0.8, color="black")
plt.title('I-135 Xe-135 Decay Sim.', fontsize=18)
plt.xlabel('Time (h)', fontsize=16)
plt.ylabel('N', fontsize=16)
plt.legend(['I-135','Xe-135'],prop={'size': 12})
plt.grid(color = 'black', linestyle = '--', linewidth = 0.6)
def animate(j):
line1.set_xdata(np.linspace(0,j,n))
line2.set_xdata(np.linspace(0,j,n))
return [line1,line2,]
ani = animation.FuncAnimation(fig, animate, frames=max_time, blit=True, interval=10, save_count=10)
plt.show()
f = r"C://Users/USER_NAME/Desktop/animation.gif"
writergif = animation.PillowWriter(fps=30)
ani.save(f, writer=writergif)

How to create an animation with a filled 'span?

I've created an animated plot of a wavefunction, psi:
def psi(x, t):
real = 0.4*np.cos(0.4*x - 0.08*t) + 0.6*np.cos(0.6*x - 0.18*t)
imag = 0.4*np.sin(0.4*x - 0.08*t) + 0.6*np.sin(0.6*x - 0.18*t)
square = real**2 + imag**2
return real, imag, square
I've then animated it successfully, however on adding the axvspan fill I've encountered an issue:
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(2,1)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
def animate(i):
y1, y2, y3 = psi(x, t=i/2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
spline = UnivariateSpline(x, y3-max(y3)/2, s=0)
r1, r2 = spline.roots()
ax2.axvspan(r1, r2, facecolor='b', alpha=0.5)
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line,
anim = animation.FuncAnimation(fig, animate, frames=600, interval = 100, blit=False, repeat=False)
It starts like
and it ends like .
Every iteration of the animation function, the fill increases across the page having started off as filling half the graph (I'd include a gif but at the moment that's a struggle I'm having with anaconda). I'm working under the assumption this is because the axes don't clear properly, however with the blit=false I assumed this wouldn't be a problem?
As asked for - the full psi function is detailed below:
n = 15
amp_scale = np.linspace(0, 0.8, n)
amp_init = norm.pdf(amp_scale, 0.4, 0.2)
#normalise wavefunction to prob=1
amp = []
for i in range(n):
amp_val = amp_init[i]/sum(amp_init)
amp.append(amp_val)
k = np.linspace(1.4, 2.6, n)
def psi (x, t=1, n=1, a = 1, k = 1, m = 1):
psi_real = 0
psi_imag = 0
for i in range(n):
a_val = a[i]
k_val = k[i]
w = (k_val**2)/(2*m)
psi_real+=a_val*np.cos(k_val*x - w*t)
psi_imag+=a_val*np.sin(k_val*x - w*t)
psi_squared = psi_real**2 + psi_imag**2
return psi_real, psi_imag, psi_squared
In your current code, a new axvspan() is continually added, never removed. You could explicitly remove the old span inside animate(). Or, similarly to what happens to the lines, update the position. A span is internally represented as a polygon, of which the coordinates can be set via .set_xy().
The function psi in the post doesn't seem to be the same as the function that generated the example plots. This also made that I couldn't get to work to calculate the spline and the roots. I replaced them by some simpler positions to show how the span can be updated during the animation.
The code also adds explicit x and y limits, as they weren't set in the question's code.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
from scipy.interpolate import UnivariateSpline
def psi(x, t):
# the function from the question is adapted to more resemble the plot
real = (0.4 * np.cos(0.4 * x - 0.08 * t) + 0.6 * np.cos(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
imag = (0.4 * np.sin(0.4 * x - 0.08 * t) + 0.6 * np.sin(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
square = real ** 2 + imag ** 2
return real, imag, square
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
span1 = ax2.axvspan(0, 0, facecolor='b', alpha=0.2)
ax1.set_xlim(x[0], x[-1]/2)
ax1.set_ylim(-1, 1)
ax2.set_ylim(0, 1.1)
def animate(i):
y1, y2, y3 = psi(x, t=i / 2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
# this didn't work for me, spline.roots() gave me a long array of values
spline = UnivariateSpline(x, y3 - max(y3) / 2, s=0)
r1, r2 = spline.roots()[[0, -1]] # [[0, -1]] takes the first and the last
# r1, r2 = i - 50, i + 50
span1.set_xy([[r1, 0], [r1, 1], [r2, 1], [r2, 0], [r1, 0]])
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line, span1, ax2.legend_,
anim = animation.FuncAnimation(fig, animate, frames=600, interval=100, blit=False, repeat=False)
plt.plot()
The resulting end frame looks like (note that a slightly different function is used):

Beginner-Make a frame based video in python from figures

I plot figures in a for loop which is a loop for my time, basically at each time step I plot a surf out of my data as below:
for time_step in range(0,nt):
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.arange(xmin, xmax+dx, dx)
z = np.arange(zmin, zmax+dz, dz)
X, Z = np.meshgrid(x, z)
ax.plot_surface(X, Z, w1[time_step])
plt.show()
Suppose that w1[time_step] changes in the loop and is sth different at each time step, all other assumptions you can have. I plot but don't know only how to make them into a video.
I have done it matlab, but I want to do sth similar in Python
Matplotlib as some animation features you might want to use. Check the following recipe (that I collected from here):
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import time
def generate(X, Y, phi):
R = 1 - np.sqrt(X**2 + Y**2)
return np.cos(2 * np.pi * X + phi) * R
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
xs = np.linspace(-1, 1, 50)
ys = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(xs, ys)
Z = generate(X, Y, 0.0)
wframe = None
tstart = time.time()
for phi in np.linspace(0, 360 / 2 / np.pi, 100):
oldcol = wframe
Z = generate(X, Y, phi)
wframe = ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)
# Remove old line collection before drawing
if oldcol is not None:
ax.collections.remove(oldcol)
plt.pause(.001)
print('FPS: %f' % (100 / (time.time() - tstart)))
Just replace the wireframe plot for whatever you want (and also use your data obviously) and you should have what you are looking for.

Categories

Resources