Why matplotlib get slow when use plot() to draw lines? - python

I want to know why it will be very slow when I use matplotlib to draw lines? How to fix it?
Belows are the demo code. It used plot() to draw a line between two randomly generated points.
On my computer, 'END=100/200/500' results 'FPS=36.9/28.6/20'. I need to endless draw lines and it will get worse while time being. How to solve it?
Thanks!
import numpy as np
import matplotlib.pyplot as plt
import time
def draw_demo():
x = 100
plt.axis([0, x, 0, 1])
plt.ion()
last = 50
TIME = 5
END = 1000
time_start = time.time()
for i in range(0, END):
random_num = np.random.random()
if i > 70:
plt.axis([x - 100, x + 1, 0, 1])
x += 1
plt.plot([i, i + 1], [last, random_num])
last = random_num
plt.pause(0.0001)
print ('FPS:', END/(time.time()-time_start))
raw_input()
if __name__ == '__main__':
draw_demo()

Try something like:
import numpy as np
import matplotlib.pyplot as plt
import time
def draw_demo2(ax):
x = 100
ax.set_xlim([x-100, x + 250])
ax.set_ylim([0, 1])
END = 250
time_start = time.time()
ln, = ax.plot([], [])
x_data = []
y_data = []
for i in range(0, END):
random_num = np.random.random()
if i%100 == 99:
cur_xlim = ax.get_xlim()
ax.set_xlim(np.array(cur_xlim) + 100)
x += 1
x_data.append(x)
y_data.append(random_num)
ln.set_data(x_data, y_data)
ax.figure.canvas.draw_idle()
ax.figure.canvas.flush_events()
print ('FPS:', END/(time.time()-time_start))
if __name__ == '__main__':
draw_demo()
It might be worth truncating the x and y data buffers to the view range (as the lists are converted to arrays every time the screen is drawn.
If you need to go really fast look into blitting, however that does not interact well with changing the limits, redrawing the text is one of the slowest parts of drawing the figure.
Also try qt instead of tk, I saw a 4x speed up from that change.

matplotlib is getting slower as the script progresses because it is redrawing all of the lines that you have previously plotted - even the ones that have scrolled off the screen.

Related

Is there a way to iteratively add plots to Animation.FuncAnimation()?

I'm working on my master thesis right now and need to animate a high number of points moving. Those points will be representing predators and prey. The number of predators and preys should be changeable and probably around 500 each. For now i hard coded the animation function with 3 predators and 1 prey. is there a way to do it iteratively (e.g for 500 predators and 200 preys)?
EDIT: In google colab the animation is stretched is there a way to make it a proper square?
Thanks in advance!
My code so far (just have random movement for p&p so code is smaller):
import matplotlib
import random
import numpy as np
from matplotlib import pyplot
from matplotlib import animation
from matplotlib import rc
rc('animation', html='jshtml')
max_t=200 # max experiment time
num_pred = 3 # number of predators
num_prey = 1 # number of prey
pred_pos = np.zeros((2*num_pred, max_t)) # initializing x and y positions for pred based on time
prey_pos = np.zeros((2*num_prey, max_t)) # initializing x and y positions for prey based on time
# [pred1_x1, pred1_x2, pred1_x3,...
# pred1_y1, pred1_y2, pred1_y3,...
# pred2_x1, pred2_x2, pred2_x3,...
# pred2_y1, pred2_y2, pred2_y3,...]
for i in range(2*num_pred):
pred_pos[i][0] = pred_pos[i][0] + random.uniform(-1, 1)
for j in range(max_t-1):
pred_pos[i][j] = pred_pos[i][j-1] + random.uniform(-1, 1) # random movement
for i in range(2*num_prey):
prey_pos[i][0] = prey_pos[i][0] + random.uniform(-1, 1)
for j in range(max_t-1):
prey_pos[i][j] = prey_pos[i][j-1] + random.uniform(-1, 1)
#print(pred_pos)
#print(prey_pos)
fig=pyplot.figure()
ax = pyplot.axes(xlim=(-10, 10), ylim=(-10, 10))
pred_circle1=pyplot.Circle((pred_pos[0,0],pred_pos[1,0]),0.3,fc='b')
pred_circle2=pyplot.Circle((pred_pos[2,0],pred_pos[3,0]),0.3,fc='b')
pred_circle3=pyplot.Circle((pred_pos[4,0],pred_pos[5,0]),0.3,fc='b')
prey_circle1=pyplot.Circle((prey_pos[0,0],prey_pos[1,0]),0.3,fc='r')
def init():
pred_circle1.center=(pred_pos[0,0],pred_pos[1,0]) # pred_circle1.center=(x,y)
pred_circle2.center=(pred_pos[2,0],pred_pos[3,0])
pred_circle3.center=(pred_pos[4,0],pred_pos[5,0])
prey_circle1.center=(prey_pos[0,0],prey_pos[1,0])
ax.add_patch(pred_circle1)
ax.add_patch(pred_circle2)
ax.add_patch(pred_circle3)
ax.add_patch(prey_circle1)
return pred_circle1, pred_circle2, pred_circle3, prey_circle1,
def animate(i):
pred_circle1.center=(pred_pos[0,i],pred_pos[1,i])
pred_circle2.center=(pred_pos[2,i],pred_pos[3,i])
pred_circle3.center=(pred_pos[4,i],pred_pos[5,i])
prey_circle1.center=(prey_pos[0,i],prey_pos[1,i])
return pred_circle1, pred_circle2, pred_circle3, prey_circle1,
anim=animation.FuncAnimation(fig,animate, init_func=init,frames=max_t,blit=True)
anim
One way to achieve it is by knowing that your first add all the predators, then all the preys. So, ax.patches contains an ordered list of patches, and you can loop over them to update their positions:
import matplotlib
import random
import numpy as np
from matplotlib import pyplot
from matplotlib import animation
max_t=200 # max experiment time
num_pred = 15 # number of predators
num_prey = 4 # number of prey
pred_pos = np.zeros((2*num_pred, max_t)) # initializing x and y positions for pred based on time
prey_pos = np.zeros((2*num_prey, max_t)) # initializing x and y positions for prey based on time
# [pred1_x1, pred1_x2, pred1_x3,...
# pred1_y1, pred1_y2, pred1_y3,...
# pred2_x1, pred2_x2, pred2_x3,...
# pred2_y1, pred2_y2, pred2_y3,...]
for i in range(2*num_pred):
pred_pos[i][0] = pred_pos[i][0] + random.uniform(-1, 1)
for j in range(max_t-1):
pred_pos[i][j] = pred_pos[i][j-1] + random.uniform(-1, 1) # random movement
for i in range(2*num_prey):
prey_pos[i][0] = prey_pos[i][0] + random.uniform(-1, 1)
for j in range(max_t-1):
prey_pos[i][j] = prey_pos[i][j-1] + random.uniform(-1, 1)
fig=pyplot.figure()
ax = pyplot.axes(xlim=(-10, 10), ylim=(-10, 10))
# add an arbitrary number of preys and predators
for i in range(num_pred):
p = pyplot.Circle((pred_pos[2 * i, 0], pred_pos[2 * i + 1, 0]),0.3,fc='b')
ax.add_patch(p)
for i in range(num_prey):
p = pyplot.Circle((prey_pos[2 * i, 0], prey_pos[2 * i + 1, 0]),0.3,fc='r')
ax.add_patch(p)
def animate(i):
# update positions
for k in range(num_pred):
ax.patches[k].center=(pred_pos[2*k,i],pred_pos[2*k+1,i])
for k in range(num_prey):
ax.patches[num_pred + k].center=(prey_pos[2*k,i],prey_pos[2*k+1,i])
anim=animation.FuncAnimation(fig,animate, frames=max_t)
pyplot.show()

Auto-correlation data generated in python and MATLAB do not match for the exact same algorithm. Is there something I am missing?

I have a MATLAB code that is very well tested; I use it to generate an array of auto-correlation points for an array.
I have tried implementing the same algorithm in python and have generated an auto-correlation plot from the generated data. But somehow, these two plots do not overlap.
Python code:
import sys
import time
import matplotlib.pyplot as plt
def acf(arr_i, tau):
start = time.time()
max_acf = 0
acf_arr = []
# For lag = 0: array has maximum overlap with itself
for ele in arr_i:
max_acf += np.power(ele, 2)
# Introducing lag times
for t in range(0, tau):
temp = 0
sys.stdout.write("\r lag = {0} | Progress = {1} %".format(t, 100*t/tau))
sys.stdout.flush()
for i in range(len(arr_i)):
if i + t < len(arr_i):
temp += arr_i[i] * arr_i[i + t]
# After every lag divide by max
acf_arr.append(float(temp/max_acf))
print(str(' || ') + str(time.time()-start))
return acf_arr
f2 = open("X_traj.txt", "r+")
arr_traj = [int(i) for i in f2]
f2.close()
plt.plot(t_arr, acf(arr_traj, len(arr_traj)), alpha = 0.8)
plt.show()
I am not exactly sure how to attach the data file I am using here!

Matplotlib FuncAnimation not displaying any frames until animation is complete

I'm trying to animate a plot using matplotlib's FuncAnimation, however no frames of the animation are visible until the animation reaches the final frame. If I set repeat = True nothing is ever displayed. When I first run the code a matplotlib icon appears but nothing displays when I click on it until it shows me the final frame:
If I save the animation I see the animation display correctly so this leads me to think that my code is mostly correct so I hope this is a simple fix that I'm just missing.
Apologies if I'm dumping too much code but I'm not sure if there's anything that's not needed for the minimum reproducible example.
Here's the main code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from quantum_custom.constants import spin_down, spin_up, H00, H11, H
import quantum_custom.walk as walk
class QuantumState:
def __init__(self, state):
self.state = state
#"coin flips"
max_N = 100 #this will be the final number of coin flips
positions = 2*max_N + 1
#initial conditions
initial_spin = spin_down
initial_position = np.zeros(positions)
initial_position[max_N] = 1
initial_state = np.kron(np.matmul(H, initial_spin), initial_position) #initial state is Hadamard acting on intial state, tensor product with the initial position
quantum_state = QuantumState(initial_state)
#plot the graph
fig, ax = plt.subplots()
plt.title("N = 0")
x = np.arange(positions)
line, = ax.plot([],[])
loc = range(0, positions, positions // 10)
plt.xticks(loc)
plt.xlim(0, positions)
plt.ylim((0, 1))
ax.set_xticklabels(range(-max_N, max_N + 1, positions // 10))
ax.set_xlabel("x")
ax.set_ylabel("Probability")
def init():
line.set_data([],[])
return line,
def update(N):
next_state = walk.flip_once(quantum_state.state, max_N)
probs = walk.get_prob(next_state, max_N)
quantum_state.state = next_state
start_index = N % 2 + 1
cleaned_probs = probs[start_index::2]
cleaned_x = x[start_index::2]
line.set_data(cleaned_x, cleaned_probs)
if cleaned_probs.max() != 0:
plt.ylim((0, cleaned_probs.max()))
plt.title(f"N = {N}")
return line,
anim = animation.FuncAnimation(
fig,
update,
frames = max_N + 1,
init_func = init,
interval = 20,
repeat = False,
blit = True,
)
anim.save("animated.gif", writer = "ffmpeg", fps = 15)
plt.show()
Here's my quantum_custom.constants module.
#define spin up and spin down vectors as standard basis
spin_up = np.array([1,0])
spin_down = np.array([0,1])
#define our Hadamard operator, H, in terms of ith, jth entries, Hij
H00 = np.outer(spin_up, spin_up)
H01 = np.outer(spin_up, spin_down)
H10 = np.outer(spin_down, spin_up)
H11 = np.outer(spin_down, spin_down)
H = (H00 + H01 + H10 - H11)/np.sqrt(2.0) #matrix representation of Hadamard gate in standard basis
Here's my quantum_custom.walk module.
import numpy as np
from quantum_custom.constants import H00, H11, H
#define walk operators
def walk_operator(max_N):
position_count = 2 * max_N + 1
shift_plus = np.roll(np.eye(position_count), 1, axis = 0)
shift_minus = np.roll(np.eye(position_count), -1, axis = 0)
step_operator = np.kron(H00, shift_plus) + np.kron(H11, shift_minus)
return step_operator.dot(np.kron(H, np.eye(position_count)))
def flip_once(state, max_N):
"""
Flips the Hadamard coin once and acts on the given state appropriately.
Returns the state after the Hadamard coin flip.
"""
walk_op = walk_operator(max_N)
next_state = walk_op.dot(state)
return next_state
def get_prob(state, max_N):
"""
For the given state, calculates the probability of being in any possible position.
Returns an array of probabilities.
"""
position_count = 2 * max_N + 1
prob = np.empty(position_count)
for k in range(position_count):
posn = np.zeros(position_count)
posn[k] = 1
posn_outer = np.outer(posn, posn)
alt_measurement_k = eye_kron(2, posn_outer)
proj = alt_measurement_k.dot(state)
prob[k] = proj.dot(proj.conjugate()).real
return prob
def eye_kron(eye_dim, mat):
"""
Speeds up the calculation of the tensor product of an identity matrix of dimension eye_dim with a given matrix.
This exploits the fact that majority of values in the resulting matrix will be zeroes apart from on the leading diagonal where we simply have copies of the given matrix.
Returns a matrix.
"""
mat_dim = len(mat)
result_dim = eye_dim * mat_dim #dimension of the resulting matrix
result = np.zeros((result_dim, result_dim))
result[0:mat_dim, 0:mat_dim] = mat
result[mat_dim:result_dim, mat_dim:result_dim] = mat
return result
I know that saving the animation is a solution but I'd really like to have the plot display just from running the code as opposed to having to save it. Thanks!
As per Sameeresque's suggestion I tried using different backends for matplot lib. This was done by altering by import statements as follows.
import matplotlib
matplotlib.use("tkagg")
import matplotlib.pyplot as plt
Note that it's important to add the two additional lines before import matplotlib.pyplot as plt otherwise it won't do anything.

Bifurcation diagram in matplotlib

I'm trying to acquire the bifurcation diagram for the equation below:
(x is a function of t)
as:
And here is my snippet:
import numpy as np
import matplotlib.pyplot as plt
def pitch(r, x):
return r * x + np.power(x,3)- np.power(x,5)
n = 10000
r = np.linspace(-200, 200, n)
iterations = 1000
last = 100
x = 0
for i in range(iterations):
x = pitch(r,x)
if i >= (iterations - last):
plt.plot(r,x, ',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
But the generated plot is not what it is supposed to be:
Edit:
Here is my recent attempt:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.linspace(0,100)
s0=[-50]
r = np.linspace(-200, 200)
for i in r:
s = odeint(pitch,s0,t, args=(i,))
plt.plot(s,i,',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
With this error:
raise ValueError("x and y must have same first dimension") ValueError:
x and y must have same first dimension
Could you give me some advice to fix this problem?!
I found a link to this post and decided to post a few remarks that might be helpful to someone stumbling upon it in the future.
I did not analyze the equation in detail but it is clear from the first sight that something interesting would happen when r is close to 0.
So we could study the behavior of the system for r in [-10,10]
You are right to use odeint instead of solving the Cauchy problem using Euler method coded by yourself.
This equation has an attractor in that it soon "forgets" the initial condition and slides towards the attractor, yet the choice of the attractor depends on where in relation to 0 do we start. Large positive initial conditions would slide to the negative attractor and vice versa as - x^5 is the term that defines the behavior at large x.
What we need to do is for each r in the range put a mark at the attractor that the solution slides to for each initial condition.
We first create a canvas to put marks into:
diagram = np.zeros((200,200))
And then for each combination of (r,s0) we put a point on the canvas at (r,s[-1]).
Here is the complete code
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.arange(0,100,2)
s0=[-50]
N = 200 # Number of points along each side of the diagram
diagram = np.zeros((N,N))
rmin,rmax = -10,10
rrange = np.arange(rmin, rmax,(rmax-rmin)/N)
smin,smax = -5.0,5.0
srange = np.arange(smin,smax,2*(smax-smin)/N)
for i in rrange:
for s0 in srange:
s = odeint(pitch,[s0],t, args=(i,))
imgx = int((i-rmin)*N/(rmax-rmin))
imgy = int((s[-1]-smin)/(smax-smin)*N)
imgx = min(N-1,max(0,imgx)) # make sure we stay
imgy = min(N-1,max(0,imgy)) # within the diagram bounds
diagram[imgy,imgx] = 1
plt.title("Bifurcation diagram")
plt.imshow(np.flipud(diagram),cmap=cm.Greys,
extent=[rmin,rmax,smin,smax],aspect=(rmax-rmin)/(smax-smin))
plt.xlabel("r")
plt.ylabel("x")
plt.show()
And the resulting plot
When you zoom in into the region around 0 by setting (rmin,rmax) to (-0.5,0.5) you could see that the branches of the diagram do not start at 0
Instead as in the diagram drawn in the original post the branches start at roughly r=-0.25

How to clear a plot in a `while` loop when using PyQtGraph?

I am using PyQtGraph for a speedy visualization of my data acquisition. For this I am redrawing the data constantly using a while loop. A simplified version of this code is given by:
import time
import numpy
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
x = numpy.linspace(-2 * numpy.pi, 2 * numpy.pi, 1000)
y = numpy.cos(x)
# Plot
win = pg.GraphicsWindow()
win.resize(800, 800)
p = win.addPlot()
p.plot(x, y, pen = "y")
i = 0
while i < 5000:
start_time = time.time()
noise = numpy.random.normal(0, 1, len(y))
y_new = y + noise
p.plot(x, y_new, pen = "y", clear = True)
p.enableAutoRange("xy", False)
pg.QtGui.QApplication.processEvents()
i += 1
end_time = time.time()
print("It has been {0} seconds since the loop started".format(end_time - start_time))
win.close()
When I time each iteration I find that I am not properly clearing the graph. The iteration time just keeps on increasing, and I am slowing down my data acquisition. For the example above, the iteration time in the beginning is about 0.009 s whereas at the end it is about 0.04 s. I therefore have a memory-leak.
I know that in matplotlib I should be calling be clf() to properly clear the plot. Unfortunately I am not that familiar with PyQtGraph and thought the clear = True would take care of this issue. I am confident it should be possible as PyQtGraph was designed for this type of usage.
How should I clear the graph each iteration to make sure I am not slowing down my data acquisition?
When you call plot on you plotItem, you create a new plotDataItem. All these plotDataItems doesn't seem to clear up properly. You can instead try to use only one plotDataItem and update the contents of it. Do this by using
plot = p.plot(x, y, pen = "y")
and then inside your loop
plot.setData(x, y_new)

Categories