Why is matplotlib.animation slower with large windows that with small windows? - python

So, I wrote up a Langton's Ant program.
#!/bin/env python
import matplotlib.animation as ani
import matplotlib.pyplot as plt
import numpy as np
w,h = 100,100
d = np.array([0,1])
p = np.array([w//2,h//2])
grid = np.ones((w,h),dtype=int)
#MAIN
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(1,1,1)
img = ax.imshow(grid,vmin=-1,vmax=1)
tle = ax.text(0.5,0.95,"",bbox={'facecolor':'w','alpha':0.5,'pad':5},transform=ax.transAxes,ha="center")
ax.tick_params(axis='x',which='both',bottom=False,top=False,labelbottom=False,labeltop=False)
ax.tick_params(axis='y',which='both',right=False,left=False,labelright=False,labelleft=False)
def langtons_ant_animator(frame):
global w,h,d,p,grid
v = grid[p[0],p[1]]
grid[p[0],p[1]] = -v
d = [[0,v],[-v,0]] # d
p = (p+d) % (w,h)
img.set_data(grid)
tle.set_text(f'{frame: 9} Steps; Point ({p[0]: 3},{p[1]: 3}), Direction ({d[0]: 2},{d[1]: 2})')
return [tle,img]
animation = ani.FuncAnimation(
fig = fig,
func = langtons_ant_animator,
interval = 1,
blit = True,
)
plt.show(block=True)
While this program is running I can dynamically change the window size. If I make the size really small (1in by 1in) the Ant moves at rapid speeds, nearing 1000 steps a second. But if I leave the window at (10in by 10in) it updates at around 100 or so steps a second. You would think that the number of pixels being updated is what effects the speed, but this isn't the case; change the window to a 1000,1000 and the animation runs just as fast at 10in by 10in.
It seems that window size is the most important factor in animation speed. Why?
Why doesn't img take into consideration the underlying numpy array size and know exactly the pixels that need to be updated together?
Could this be done smarter to get faster speeds?
Why are pixels so slow to update in matplotlib in the first place?

Related

How'd I use the Pyplot function in Python?

I'm having trouble trying to display my results after the program is finished. I'm expecting to see a velocity vs position graph, but for some reason, it's not showing up. Is there something wrong with my code.
import numpy
from matplotlib import pyplot
import time,sys
#specifying parameters
nx=41 # number of space steps
nt=25 #number of time steps
dt=0.025 #width between time intervals
dx=2/(nx-1) #width between space intervals
c=1 # the speed of the initial wave
#boundary conditions
u = numpy.ones(nx)
u[int(0.5/dx):int(1/(dx+1))] = 2
un = numpy.ones(nx)
#initializing the velocity function
for i in range(nt):
un= u.copy()
for i in range(1,nx):
u[i]= un[i] -c*(dt/dx)*(u[i]-u[i-1])
pyplot.xlabel('Position')
pyplot.ylabel('Velocity')
pyplot.plot(numpy.linspace(0,2,nx),u)
There are a few things going on here
You don't need to write out the full name of the packages you are importing. You can just use aliasing to call those packages and use them with those aliases later on. This, for example.
import numpy as np
Your dx value initalization will give you 0 beause you are dividing 2 by 40 which will give you a zero. You can initialize the value of dx by making one of the values in that expression a float, so something like this.
dx=float(2)/(nx-1) #width between space intervals
As Meowcolm Law in the comments suggested in the comments, add pyplot.show() to
show the graph. This is what the edited version of your code will like
import numpy as np
import matplotlib.pyplot as plt
import time,sys
#specifying parameters
nx=41 # number of space steps
nt=25 #number of time steps
dt=0.025 #width between time intervals
dx=float(2)/(nx-1) #width between space intervals
c=1 # the speed of the initial wave
#boundary conditions
u = np.ones(nx)
u[int(0.5/dx):int(1/(dx+1))] = 2
un = np.ones(nx)
#initializing the velocity function
for i in range(nt):
un= u.copy()
for i in range(1,nx):
u[i]= un[i] -c*(dt/dx)*(u[i]-u[i-1])
plt.xlabel('Position')
plt.ylabel('Velocity')
plt.plot(np.linspace(0,2,nx),u)
plt.show()
You can add
%matplotlib inline
in order to view the plot inside the notebook. I missed this step following the same guide.

Update mayavi plot in loop

What I want to do is to update a mayavi plot in a loop. I want the updating of the plot to be done at a time specified by me (unlike, e.g., the animation decorator).
So an example piece of code I would like to get running is:
import time
import numpy as np
from mayavi import mlab
V = np.random.randn(20, 20, 20)
s = mlab.contour3d(V, contours=[0])
for i in range(5):
time.sleep(1) # Here I'll be computing a new V
V = np.random.randn(20, 20, 20)
# Update the plot with the new information
s.mlab_source.set(scalars=V)
However, this doesn't display a figure. If I include mlab.show() in the loop, then this steals the focus and doesn't allow the code to continue.
I feel what I should be using is a traits figure (e.g. this). I can follow the example traits application to run a figure which live-updates as I update the sliders. However, I can't get it to update when my code asks it to update; the focus now is 'stolen' by visualization.configure_traits().
Any pointers, or a link to appropriate documentation, would be appreciated.
EDIT
David Winchester's answer gets a step closer to the solution.
However, as I point out in the comments, I am not able to manipulate the figure with the mouse during the time.sleep() step. It is during this step that, in the full program, the computer will be busy computing the new value of V. During this time I would like to be able to manipulate the figure, rotating it with the mouse etc.
I thin Mayavi uses generators to animate data. This is working for me:
import time
import numpy as np
from mayavi import mlab
f = mlab.figure()
V = np.random.randn(20, 20, 20)
s = mlab.contour3d(V, contours=[0])
#mlab.animate(delay=10)
def anim():
i = 0
while i < 5:
time.sleep(1)
s.mlab_source.set(scalars=np.random.randn(20, 20, 20))
i += 1
yield
anim()
I used this post as reference ( Animating a mayavi points3d plot )
If you use the wx backend, you can call wx.Yield() periodically if you want to interact with your data during some long-running function. In the following example, wx.Yield() is called for every iteration of some "long running" function, animate_sleep. In this case, you could start the program with $ ipython --gui=wx <program_name.py>
import time
import numpy as np
from mayavi import mlab
import wx
V = np.random.randn(20, 20, 20)
f = mlab.figure()
s = mlab.contour3d(V, contours=[0])
def animate_sleep(x):
n_steps = int(x / 0.01)
for i in range(n_steps):
time.sleep(0.01)
wx.Yield()
for i in range(5):
animate_sleep(1)
V = np.random.randn(20, 20, 20)
# Update the plot with the new information
s.mlab_source.set(scalars=V)

Real Time temperature plotting with python

I am currently making a project which requires real time monitoring of various quantities like temperature, pressure, humidity etc. I am following a approach of making individual arrays of all the sensors and ploting a graph using matplotlib and drwnow.
HOST = "localhost"
PORT = 4223
UID1 = "tsJ" # S1
from tinkerforge.ip_connection import IPConnection
from tinkerforge.bricklet_ptc import BrickletPTC
import numpy as np
import serial
import matplotlib
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')
from drawnow import *
# creating arrays to feed the data
tempC1 = []
def makeafig():
# creating subplots
fig1 = plt.figure(1)
a = fig1.add_subplot(111)
#setting up axis label, auto formating of axis and title
a.set_xlabel('Time [s]', fontsize = 10)
a.set_ylabel('Temperature [°C]', fontsize = 10)
y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
a.yaxis.set_major_formatter(y_formatter)
title1 = "Current Room Temperature (Side1): " + str(temperature1/100) + " °C"
a.set_title(title1, fontsize = 10)
#plotting the graph
a.plot(tempC1, "#00A3E0")
#saving the figure
fig1.savefig('RoomTemperature.png', dpi=100)
while True:
ipcon = IPConnection() # Create IP connection
ptc1 = BrickletPTC(UID1, ipcon) # S1
ipcon.connect(HOST, PORT) # Connect to brickd
#setting the temperature from PTC bricklet
temperature1 = ptc1.get_temperature()
#processing data from a temperature sensor to 1st array
dataArray1=str(temperature1/100).split(',')
temp1 = float(dataArray1[0])
tempC1.append(temp1)
#making a live figure
drawnow(makeafig)
plt.draw()
This is the approach I found good on the internet and it is working. The only problem I am facing is It consumes more time if I made more arrays for other sensors and the plot being made lags from the real time when I compare it with a stopwatch.
Is there any good and efficient approach for obtaining live graphs that will be efficient with lot of sensors and doen't lag with real time.
Or any command to clear up the already plotted array values?
I'd be obliged if anyone can help me with this problem.
I'd like to ask that, does filling data in arrays continuosly makes the process slow or is it my misconception?
That one is easy to test; creating an empty list and appending a few thousand values to it takes roughly 10^-4 seconds, so that shouldn't be a problem. For me somewhat surprisingly, it is actually faster than creating and filling a fixed size numpy.ndarray (but that is probably going to depend on the size of the list/array).
I quickly played around with your makeafig() function, placing a = fig1.add_subplot(111) up to (including) a.plot(..) in a simple for i in range(1,5) loop, with a = fig1.add_subplot(2,2,i); that makes makeafig() about 50% slower, but differences are only about 0.1-0.2 seconds. Is that in line with the lag that you are experiencing?
That's about what I can test without the real data, my next step would be to time the part from ipcon=.. to temperature1=... Perhaps the bottleneck is simply the retrieval of the data? I'm sure that there are several examples on SO on how to time parts of Python scripts, for these kind of problems something like the example below should be sufficient:
import time
t0 = time.time()
# do something
dt = time.time() - t0

Slow tkinter GUI

I've written a simple GUI in python using pylabs and tkinter based on an example found here:
http://hardsoftlucid.wordpress.com/various-stuff/realtime-plotting/
used for sine wave generation.
Except I tweaked it to pull data through suds from a server on the internet. It's not working as I exactly anticipated as the GUI is somewhat slow. I think its due to the timer. I just started learning how to use matplotlib functions yesterday so I'm not aware of how every function works.
How can I speed it up? Right now the data comes in at 2-3 seconds which is fine, but I just want to increase the GUI responsiveness.
Here is my code:
import numpy as np
from matplotlib import pyplot as plt
plt.ion() # set plot to animated
url = "http://10.217.247.36/WSDL/v4.0/iLON100.WSDL"
client = Client(url, username='ilon', password='ilon', location = 'http://10.217.247.36/WSDL/iLON100.WSDL')
read = client.factory.create('ns0:E_xSelect')
read['xSelect'] = """//Item[starts-with(UCPTname, "Net/MB485/MAIN POWER/Fb/PowerSum")]"""
ydata = [0] * 50
ax1=plt.axes()
# make plot
line, = plt.plot(ydata)
plt.ylim([10,40])
# start data collection
while True:
x = client.service.Read(read).Item[0].UCPTvalue[0].value #data stream
x = float(x)
ymin = float(min(ydata))-10
ymax = float(max(ydata))+10
plt.ylim([ymin,ymax])
ydata.append(x)
del ydata[0]
line.set_xdata(np.arange(len(ydata)))
line.set_ydata(ydata) # update the data
plt.draw() # update the plot

Quitting matplotlib.pyplot animation gracefully

I have a script that plots data of some photometry apertures, and I want to plot them in an xy plot. I am using matplotlib.pyplot with python 2.5.
The input data is stored in around 500 files and read. I am aware that this is not the most efficient way of inputting the data but that's another issue...
Example code:
import matplotlib.pyplot as plt
xcoords = []
ycoords = []
# lists are populated with data from first file
pltline, = plt.plot(xcoords, ycoords, 'rx')
# then loop populating the data from each file
for file in filelist:
xcoords = [...]
ycoords = [...]
pltline.set_xdata(xcoords)
pltline.set_ydata(ycoords)
plt.draw()
As there are over 500 files, I will occasionally want to close the animation window in the middle of the plotting. My code to plot works but it doesn't exit very gracefully. The plot window does not respond to clicking the close button and I have to Ctrl+C out of it.
Can anyone help me find a way to close the animation window while the script is running whilst looking graceful (well more graceful than a series of python traceback errors)?
If you update the data and do the draw in a loop, you should be able to interrupt it. Here's an example (that draws a stationary circle and then moves a line around the perimeter):
from pylab import *
import time
data = [] # make the data
for i in range(1000):
a = .01*pi*i+.0007
m = -1./tan(a)
x = arange(-3, 3, .1)
y = m*x
data.append((clip(x+cos(a), -3, 3),clip(y+sin(a), -3, 3)))
for x, y in data: # make a dynamic plot from the data
try:
plotdata.set_data(x, y)
except NameError:
ion()
fig = figure()
plot(cos(arange(0, 2.21*pi, .2)), sin(arange(0, 2.21*pi, .2)))
plotdata = plot(x, y)[0]
xlim(-2, 2)
ylim(-2, 2)
draw()
time.sleep(.01)
I put in the time.sleep(.01) command to be extra sure that I could break the run, but in my tests (running Linux) it wasn't necessary.

Categories