How to speed up chaco image plot - python

I am trying to animate a bunch of 2D images with chaco, but unfortunately it does not seem to be as fast as my application needs it. At the moment I am building a chaco Plot and using img_plot, e.g.:
pd = ArrayPlotData()
pd.set_data("imagedata", myarray)
plot = Plot(pd)
plot.img_plot("imagedata", interpolation="nearest")
And to update the image, I use the following:
pd.set_data("imagedata", my_new_array)
This works, however is not fast enough. Is there any way to speed it up? Any lower-level function that allows a faster update of the image?

Here's an example of how I do animations in Chaco using a timer. Usually the trick (as J Corson said) is to load your data into an array and then just use an index to take successive slices of the array.
from chaco.api import ArrayPlotData, Plot
from enable.api import ComponentEditor
import numpy as np
from pyface.timer.api import Timer
from traits.api import Array, Bool, Event, HasTraits, Instance, Int
from traitsui.api import ButtonEditor, Item, View
class AnimationDemo(HasTraits):
plot = Instance(Plot)
x = Array
y = Array
run = Bool(False)
go = Event
idx = Int
def _x_default(self):
x = np.linspace(-np.pi, np.pi, 100)
return x
def _y_default(self):
phi = np.linspace(0, 2 * np.pi, 360)
y = np.sin(self.x[:, np.newaxis] + phi[np.newaxis, :]) - \
0.1 * np.sin(13 * self.x[:, np.newaxis] - 7 * phi[np.newaxis, :])
return y
def _plot_default(self):
plot_data = ArrayPlotData(y=self.y[:, 0], x=self.x)
plot = Plot(plot_data)
plot.plot(('x', 'y'))
return plot
def _go_fired(self):
if not self.run:
self.run = True
else:
self.run = False
def _run_changed(self):
if self.run:
self.timer.Start()
else:
self.timer.Stop()
def _run_default(self):
self.timer = Timer(5, self._timer_tick)
return False
def _timer_tick(self):
if not self.run:
raise StopIteration
else:
if self.idx >= 360:
self.idx = 0
self.plot.data.set_data('y', self.y[:, self.idx])
self.idx += 1
traits_view = View(
Item('plot', editor=ComponentEditor(), show_label=False),
Item('go', editor=ButtonEditor(label="Start/Stop"), show_label=False),
)
if __name__ == "__main__":
ad = AnimationDemo()
ad.edit_traits()
I get something like this:

This is just a thought, but would adding every image initially into your ArrayPlotData solve your problem? Then you aren't adding a new image at each step in your animation, and just calling img_plot() on the next series. For example, if your images are stored in a numpy array called images[nt, nx, ny]:
pd = ArrayPlotData()
for index in range(images.shape[0]): #Assuming you want to iterate over nt
pd.set_data('', images[index,:,:], generate_name = True)
plot = Plot(pd)
This automatically names each image 'series1', 'series2', etc.
Then you can call:
plot.img_plot('series1', interpolation = 'nearest') #or 'series2' etc.
for every image in your animation without having to call set_data().
You can get a sorted list of your image names ['series1, 'series2', ...] to iterate over using:
from natsort import natsorted #sort using natural sorting
names = natsorted(pd.list_data())
Would that help with the bottleneck?

Related

plot multiple curves on same plot inside function

I have a following function with takes 2 arguments psi,lam and returns 1 array y.
lam=np.arange(0,1,0.1)
psi=np.deg2rad(np.arange(0,361,1))
def test(psi,lam):
y=[]
for i in range(len(lam)):
sin_psi = np.sin(psi)
cos_psi = np.cos(psi)
sin_beta = lam*sin_psi
cos_beta = np.sqrt(1.0 - sin_beta**2)
ssin_pb = sin_psi*sin_beta
y.append((lam*(cos_psi/cos_beta)**2 - ssin_pb)/cos_beta + cos_psi)
plt.plot(psi,y[i])
return y
I would like the function to return range(len(lam))=10 plots of y on the vertical axis against psi on x axis.
However, it seems to be only plotting the same curve multiple times. Not sure what I am missing?
import matplotlib.pyplot as plt
import numpy as np
lam=np.arange(0,1,0.1)
psi=np.deg2rad(np.arange(0,361,1))
def test(angle,var):
sin_psi = np.sin(psi)
cos_psi = np.cos(psi)
sin_beta = var*sin_psi
cos_beta = np.sqrt(1.0 - sin_beta**2)
ssin_pb = sin_psi*sin_beta
return ((var*(cos_psi/cos_beta)**2 - ssin_pb)/cos_beta + cos_psi)
for i in lam:
plt.plot(psi,test(psi,i))
plt.show()
I moved the variable outside of the function, this way you may also use it for other cases. The only other thing is that you should call plt.show() after you're done drawing.
Your code has several problems the main being that the return function was inside the loop interrupting it after the first iteration. Imitating your code structure as closely as possible, we can rewrite the code as:
import numpy as np
import matplotlib.pyplot as plt
def test(psi,lam):
y=[]
for curr_lam in lam:
sin_psi = np.sin(psi)
cos_psi = np.cos(psi)
sin_beta = curr_lam*sin_psi
cos_beta = np.sqrt(1.0 - sin_beta**2)
ssin_pb = sin_psi*sin_beta
val = (curr_lam * (cos_psi/cos_beta)**2 - ssin_pb)/cos_beta + cos_psi
y.append(val)
plt.plot(psi, val)
plt.show()
return y
lam=np.arange(0, 1, 0.1)
psi=np.deg2rad(np.arange(0,361,1))
y = test(psi, lam)
print(y)
Sample output:
As Johan mentioned in the comments, you should also directly iterate over list/arrays. If you need to combine arrays, use
for x1, x2 in zip(arr1, arr2):
If you absolutely need the index value, use
for i, x in enumerate(arr):

Animation using matplotlib query

I am trying to animate a plot of two distinct points (blue and green points) moving about the complex unit circle using Python's Matplotlib library. The problem I am having is that the animation does not remove and update the previous data points but rather sequentially smears it on the unit sphere as in the accompanying image. Hence the animation is just a smudging of the various data points as shown in the image. What I am trying to achieve is two distinct points moving about the unit circle as a function of time.
The following is the part of my code where I call 'animation.FuncAnimation' using data in arrays which I call 'A' and 'B'.
##Python Code for Executing Animation##
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
#Example Data
A = array([0., 0.03435915, 0.06328989, 0.0880305, 0.14199928, 0.2044361, 0.26287941, 0.32484623])
B = array([ 1.75, 1.71564086, 1.69358362, 1.68499179, 1.68255084, 1.67808712, 1.66169597, 1.64407287])
# Total time.
T = 1.0
# Number of steps.
NS = 100
# Time step size
dt = T/NS
t = np.linspace(0.0, NS*dt, NS+1)
# So here are a few utility functions for multiplying scalars and vectors.
# a scalar times a vector returns a vector
def scale_vector(scale, vector):
result = [0]*len(vector)
for i in range(len(result)):
result[i] = scale * vector[i]
return result
# dot product of two vectors = sum(x[0]*y[0] + ... + x[n-1]*y[n-1])
def vector_dot(vector1, vector2):
result = 0
for i in range(len(vector1)):
result += vector1[i] * vector2[i]
return result
# return real part of a vector
def real_vector(vector):
return map(lambda x: x.real, vector)
# return imaginary part of a vector
def imag_vector(vector):
return map(lambda x: x.imag, vector)
## Creating complex unit circle
r = []
im = []
def main():
# Generate numbers around the complex unit circle.
N = 128
theta = scale_vector(2*pi/N, range(N))
exp_theta = map(lambda x: exp(1j * x), theta)
real_part = real_vector(exp_theta)
imag_part = imag_vector(exp_theta)
r.append(real_part)
im.append(imag_part)
# And wait until the user is done with it.
done = raw_input("done? ")
if __name__ == "__main__":
main()
#Form two arrays which have the real and imaginary components of the unit circle
r2 = r[0][:]
im2 = im[0][:]
##Code for Animation##
Aan = np.zeros([len(A),2], float)
for i in range(2):
for j in range(len(A)):
if i == 0:
Aan[j][i] = math.cos(A[j])
elif i == 1:
Aan[j][i] = math.sin(A[j])
Ban = np.zeros([len(B),2], float)
for i in range(2):
for j in range(len(B)):
if i == 0:
Ban[j][i] = math.cos(B[j])
elif i == 1:
Ban[j][i] = math.sin(B[j])
##Plots and animation
fig = figure()
plt.title('Phase Space')
plt.xlabel('Re')
plt.ylabel('Im')
#Plots complex unit circle
plot1 = plt.plot(r2,im2, color = 'g',alpha = 0.4)
#Animation functions
def animate(i):
plot(Aan[i, 0], Aan[i, 1], color='blue', marker= 'o')
plot(Ban[i, 0], Ban[i, 1], color='orange', marker= 'o')
ani = animation.FuncAnimation(fig, animate, interval=101)
show()
Can anyone advise on how this problem could be solved?
Plot creates a new object on the canvas which is not cleared automatically at the next plot.
If you would like to redraw the figure, you can call the cla method and plot the data again.
Or you can update the previously plotted data as it is described in the last example of animation API documentation.

Jello effect when displaying a filtered digital signal

I wish to display a mesured signal in real time with some basic filtering (band stop, band pass).
signal is stored in a numpy array (numpy.array)
a matplotlib graph is displaying the numpy array (matplotlib.animation.FuncAnimation)
for each FuncAnimation frame, some filters are applied on the signal (scipy.signal.butter, scipy.signal.filtfilt)
When I do it this way, I get some kind of jello effet (sorry for bad gif quality)
Even if it's not exactly what's going on, let's pretend I implemented this algorithm in pseudo-code:
signal = np.array(some_data)
plot = FuncAnimation(init_function, update_function)
for each frame:
- shift signal on the left # make space and discard oldest samples
- add new samples on the right
- filtered_signal = filter(signal) # using signal.butter and signal.filtfilt
- update_plot(filtered_signal) # FuncAnimation update function
I'm looking for techniques to get rid of this unwanted effect. Any idea?
EDIT 1
Without animation, this is what append for 20 consecutive filtered signals.
First plot is signal (raw signal)
Second plot contains the 20 last filtered_signal
Third plot contains the 20 last filtered_signal shifted for better visualization
EDIT 2
signal is a fixed-size buffer containing N=1000 samples
filtered_signal is created from scratch for each frame
sampling frequency is fs=250Hz
2 filters are applied: a 50Hz notch and a [0.5Hz, 120Hz] band pass
EDIT 3
Here is a full working example:
First plot is the raw signal
Second plot is the filtered signal (band pass [0.5Hz, 120Hz])
Source code:
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
import matplotlib.animation as animation
import time
N = 1000
fs = 250
last_update = time.time()
sample_id = 0
def all_samples(length=10000):
# generate a dummy signal
st = 1.0 / fs
t = np.arange(length) * st
sig1 = 1*np.sin(2*np.pi * 2*t) + 2
sig2 = 0.25*np.sin(2*np.pi * 3*t) + 4
sig3 = 2*np.sin(2*np.pi * 4*t) + 5
return sig1 + sig2 + sig3
def band_pass(low_cut, high_cut, order=2):
# compute butterworth b, a coefficients
band = np.array([low_cut, high_cut])
Wn = band / (float(fs/2.0))
b, a = signal.butter(order, Wn, 'bandpass')
return b, a
def filter(raw_signal):
# apply filter
b, a = band_pass(0.5, 120)
return signal.filtfilt(b, a, raw_signal)
def init():
# init function for FuncAnimation blit=True
global axe_raw, line_raw
global axe_filt, line_filt
line_filt.set_visible(False)
line_raw.set_visible(False)
axe_raw.set_xlim(0, 1000)
axe_raw.set_ylim(5, 15)
axe_filt.set_xlim(0, 1000)
axe_filt.set_ylim(-5, 5)
return line_raw, line_filt,
def update(n):
global raw_signal, axe_raw, line_raw
global axe_filt, line_filt
global last_update, fs, sample_id
if n == 1:
line_raw.set_visible(True)
line_filt.set_visible(True)
# add new samples
now = time.time()
sample_count = int((now - last_update) * fs)
raw_signal = np.roll(raw_signal, -sample_count)
raw_signal[-sample_count:] = all_samples[sample_id:sample_id + sample_count]
last_update = now
sample_id += sample_count
# update plot (raw + filtered)
line_raw.set_ydata(raw_signal)
line_filt.set_ydata(filter(raw_signal))
return line_raw, line_filt
all_samples = all_samples()
raw_signal = np.zeros(N)
# matplotlib animation
figure = plt.figure()
axe_raw = figure.add_subplot(211)
axe_filt = figure.add_subplot(212)
line_raw, = axe_raw.plot(raw_signal)
line_filt, = axe_filt.plot(np.zeros(N))
anim = animation.FuncAnimation(figure,
update,
init_func=init,
interval=5,
blit=True)
plt.show()

How to index List/ numpy array in order to plot the data with matplotlib

I have a function f(x,t) = cos(t)*t + x and i want to display the change of the result over the width x and time t at discretised time steps t_i and discretised width steps x_j.
Now I am a while here on SX and feel really embarrassed to only can post such little code or in other words nothing (since nothing worked I have done...):
Nevertheless if someone has the time to help, I`d appreciate it.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as pyplot
from astropy.io.ascii.latex import AASTex
def func(xi, ti):
res = np.cos(ti)*ti + xi
return res
timeSpacing = 100
timeStart = 0
timeEnd = 1
time = np.linspace(timeStart, timeEnd, timeSpacing)
widthSpacing = 300
widthStart = 0
widthEnd = 3
width = np.linspace(widthStart, widthEnd, widthSpacing)
resultList = [None]*timeSpacing
resultListInner = [None]*widthSpacing
for i, ithTime in enumerate(time):
for j, jthWidth in enumerate(width):
aas = np.zeros_like(width)
aas.fill(ithTime)
resultListInner[j] = ithTime, jthWidth, func(jthWidth, aas)
resultList[i] = resultListInner
So how do I correctly index the list and array and plot my data using matplotlib?
My plot should look like this:
where in my case the aperature should be the width x, the sky annulus is my time t and the RMS is my func(x,t).
A couple of points:
Numpy provides a very nice function for doing differences of array elements: diff
Matplotlib uses plot_wireframe for creating a plot that you would want (also using Numpy's meshgrid)
Now, combining these into what you may want would look something like this.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
def func(xi, ti):
res = np.cos(ti)*np.sin(xi)
return res
timeSpacing = 20
timeStart = 0
timeEnd = 1
time = np.linspace(timeStart, timeEnd, timeSpacing)
widthSpacing = 50
widthStart = 0
widthEnd = 3
width = np.linspace(widthStart, widthEnd, widthSpacing)
X,T = np.meshgrid(width,time)
F = func(X,T)
DF = np.diff(np.diff(F,axis=0),axis=1)
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.plot_wireframe(X[:-1,:-1],T[:-1,:-1],DF)
plt.show()
Note that diff is applied twice: once in each dimension axis= . I have also changed the toy function you provided to something that actually looks decent in this case.
For your more general use, it seems that you would want to just collect all of your F data into a 2D array, then proceed from the DF = line.

python/matplotlib - parasite twin axis scaling

Trying to plot a spectrum, ie, velocity versus intensity, with lower x axis = velocity, on the upper twin axis = frequency
The relationship between them (doppler formula) is
f = (1-v/c)*f_0
where f is the resulting frequency, v the velocity, c the speed of light, and f_0 the frequency at v=0, ie. the v_lsr.
I have tried to solve it by looking at http://matplotlib.sourceforge.net/examples/axes_grid/parasite_simple2.html , where it is solved by
pm_to_kms = 1./206265.*2300*3.085e18/3.15e7/1.e5
aux_trans = matplotlib.transforms.Affine2D().scale(pm_to_kms, 1.)
ax_pm = ax_kms.twin(aux_trans)
ax_pm.set_viewlim_mode("transform")
my problem is, how do I replace the pm_to_kms with my function for frequency?
Anyone know how to solve this?
The solution I ended up using was:
ax_hz = ax_kms.twiny()
x_1, x_2 = ax_kms.get_xlim()
# i want the frequency in GHz so, divide by 1e9
ax_hz.set_xlim(calc_frequency(x_1,data.restfreq/1e9),calc_frequency(x_2,data.restfreq/1e9))
This works perfect, and much less complicated solution.
EDIT : Found a very fancy answer.
EDIT2 : Changed the transform call according to the comment by #u55
This basically involves defining our own conversion/transform. Because of the excellent AstroPy Units equivalencies, it becomes even easier to understand and more illustrative.
from matplotlib import transforms as mtransforms
import astropy.constants as co
import astropy.units as un
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
from mpl_toolkits.axes_grid.parasite_axes import SubplotHost
class Freq2WavelengthTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = False
has_inverse = True
def __init__(self):
mtransforms.Transform.__init__(self)
def transform_non_affine(self, fr):
return (fr*un.GHz).to(un.mm, equivalencies=un.spectral()).value
def inverted(self):
return Wavelength2FreqTransform()
class Wavelength2FreqTransform(Freq2WavelengthTransform):
input_dims = 1
output_dims = 1
is_separable = False
has_inverse = True
def __init__(self):
mtransforms.Transform.__init__(self)
def transform_non_affine(self, wl):
return (wl*un.mm).to(un.GHz, equivalencies=un.spectral()).value
def inverted(self):
return Freq2WavelengthTransform()
aux_trans = mtransforms.BlendedGenericTransform(Wavelength2FreqTransform(), mtransforms.IdentityTransform())
fig = plt.figure(2)
ax_GHz = SubplotHost(fig, 1,1,1)
fig.add_subplot(ax_GHz)
ax_GHz.set_xlabel("Frequency (GHz)")
xvals = np.arange(199.9, 999.9, 0.1)
# data, noise + Gaussian (spectral) lines
data = np.random.randn(len(xvals))*0.01 + np.exp(-(xvals-300.)**2/100.)*0.5 + np.exp(-(xvals-600.)**2/400.)*0.5
ax_mm = ax_GHz.twin(aux_trans)
ax_mm.set_xlabel('Wavelength (mm)')
ax_mm.set_viewlim_mode("transform")
ax_mm.axis["right"].toggle(ticklabels=False)
ax_GHz.plot(xvals, data)
ax_GHz.set_xlim(200, 1000)
plt.draw()
plt.show()
This now produces the desired results:
Your "linear function" is a "simple scaling law" (with an offset). Just replace the pm_to_kms definition with your function.

Categories

Resources