I am making a animated bar plot for basic bubble sort . It runs pretty good. But doesn't repeat itself (loop). I am trying it in jupyter notebook , I added %matplotlib qt,
Why won't my animFunc repeat although I have set the repeat to True .
x=["1","2","3","4","5","6","7","8","9","10"]
y=[7,8,5,3,1,9,4,2,10,6]
temp=0
def animator_X():
for a in range(len(y)-1):
for b in range(len(y)-a-1):
if y[b]>y[b+1]:
temp = y[b]
y[b]=y[b+1]
y[b+1]=temp
yield y
fig,ax = plt.subplots(figsize=(7,5))
def init():
ax.clear()
y=[7,8,5,3,1,9,4,2,10,6]
plt.bar(x,y,color=['blue'])
def animX(i):
ax.clear()
plt.bar(x,y,color=['blue'])
return plt
animx = FuncAnimation(fig,animX,frames=animator_X,interval=1000,init_func=init,repeat=True)
plt.show()
You aren't resetting the main y variable when it repeats the init function after a run.
Try:
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x=["1","2","3","4","5","6","7","8","9","10"]
y=[7,8,5,3,1,9,4,2,10,6]
temp=0
def animator_X():
for a in range(len(y)-1):
for b in range(len(y)-a-1):
if y[b]>y[b+1]:
temp = y[b]
y[b]=y[b+1]
y[b+1]=temp
print(y)
yield y
fig,ax = plt.subplots(figsize=(7,5))
def init():
global y
ax.clear()
y=[7,8,5,3,1,9,4,2,10,6]
plt.bar(x,y,color=['blue'])
def animX(i):
ax.clear()
plt.bar(x,y,color=['blue'])
return plt
anim = animation.FuncAnimation(fig,animX,frames=animator_X,interval=100,init_func=init)
plt.show()
That code will run in sessions launched from here. Go there and press launch binder. When it comes up, you can paste in the code.
I suspect in OP's code the addition of the global y line in the init() function will fix the OP's version.
Further explanation
It does keep repeating with the code posted in the OP because the kernel keeps running on that cell after the first pass.
A y object that is local solely to the init function is getting reset within the scope of the init() function when it repeats after the first pass. I don't know enough about how FuncAnimation() decides to update/and what it displays then to tell you why OP code without updating y in the main scope results in it showing the init() state and doesn't instead flash to the init state and then back to the sorted state. The kernel is still running and so maybe it is flashing between those two yet the init() dominates for some reason? That's speculation because the FuncAnimation() is so specialized that it doesn't display what is put in print statements inside the init or main function that gets animated, and so probing what's going on separate from the plot, in a simplistic manner, is not easy.
Related
So I have a function that scatter-plots some data and does so by creating new figures. The maximum amount of figures allowed at a time is 20 to avoid memory overload. If the user wants to plot a data-set with 6 variables to be exact, then there would be 30 different figures. Is there a way to wait until the user deletes the necessary amount of figures before adding more?
This is what I've though of:
import matplolib.pyplot as plt
... # some code
# this below is inside a loop structure
f = plt.figure
# add some stuff to the figure
plt.show(block=False)
Halt() # checks to see if there are too many figures
Where Halt() is defined as such:
def halt():
first = True
while plt.gcf().number > 20: # are there more than 20 figures
if first:
# show message
first = False
# time.sleep(100)
The only problem with this is that it "freezes" the program, not allowing the user to exit out of any of the figures, as it is "not responding". I've also tried the time.sleep() but that does not seem work either.
Does anyone know of a good way to loop until a condition is met?
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.show.html says:
If False ensure that all windows are displayed and return immediately. In this case, you are responsible for ensuring that the event loop is running to have responsive figures.
How to do this, you ask? Well, the documentation is at https://matplotlib.org/users/interactive_guide.html#explicitly-spinning-the-event-loop .
After some fiddling around, I made the following which plots 20 figures with maximum 5 at the same time:
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
def plot_stuff(exponent, titlenum):
x = np.linspace(0.0, 1.0)
f = plt.figure()
ax = f.add_subplot(1, 1, 1)
ax.set_title('{} - {}'.format(titlenum, exponent))
ax.plot(x, x**exponent)
def get_fighandles():
fignumbers = plt.get_fignums()
return [plt.figure(fign) for fign in fignumbers]
N_figs_eventually_plotted = 20
N_figs_max_simultaneous = 5
N=0
while N < N_figs_eventually_plotted:
if len(get_fighandles()) < N_figs_max_simultaneous:
N += 1
# put here whichever update is needed when you can add new figures
plot_stuff(np.random.random(), N)
plt.show(block=False)
print('hi')
for fig in get_fighandles():
print(fig.canvas)
fig.canvas.flush_events()
fig.canvas.draw_idle() # might not be needed, but here it's fast
sleep(0.1)
# note: solution terminates when the last figure is plotted, you might want something to prevent this (for instance a plt.show(block=True) when the last figure is plotted)
There might be some subtle concurrency bugs (for instance, if you close a figure after the loop reads the figure handles but before it flushes the events), but I do not see how you can avoid that for your use case.
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)
I am using ipython notebook and trying to use the following function to export seaborn distplots. It works just fine if I call the function and execute with only one variable at a time. If I call the function in a loop, it continues to build on top of the distplot from the previous function call.
My desired output would be for the function to output a new displot every time it is called in a loop. Is there a way to force evaluation or a new distplot?
def graph_extraversion (x):
file_name = "extraversion_" + str(x) + ".png"
sns_plot = sns.distplot(Personalities[Personalities.labels1 ==x].extraversion)
sns_plot = sns.distplot(df.extraversion)
fig = sns_plot.get_figure()
fig.savefig(file_name)
new_stat = Personalities[Personalities.labels1 ==x].extraversion.describe()
extraversion_drift = extraversion_median - new_stat[1]
drift = extraversion_drift / extraversion_std
if (drift >= 1) | (drift <= -1):
return "1 std deviation or more"
else:
return "Less than one std deviation"
This what is what the distplot looks like after one call
This is two calls later in a loop.
Again this works just fine with a single call and execution but when looped it keeps building.
So this has to do with matplotlib and closing figures.
additional code required is an import:
import matplotlib.pyplot as plt
Then at the end of the func:
plt.close(fig)
This should help with any looping with both seaborn and matplotlib
I have a plot that allows user to click on a data point. Then it generates a continuous loop that was supposed to be closed by clicking on the close button. The example below will trigger a print message of "Still Looping" every 1second. Then I created a button hoping to close the loop by changing loopBool to True. But it doesn't work because once the loop starts I am not able to interact with the figure window. How can I solve this? Many thanks
import numpy as np
from matplotlib import pyplot as plt
import time
import matplotlib.widgets as widgets
fig, ax = plt.subplots()
# it is not always column 0 and 1
sctPlot = ax.scatter([-0.3,0,0.3], [0.3,0.3,0.3], c="blue", picker = 2, s=[50]*3)
fig.subplots_adjust(bottom=0.3, left=0.1)
plt.grid(False)
plt.axis([-0.6, 0.6, -0.6, 0.6])
loopBool = True
def closeLooping(event):
global loopBool
loopBool = False
def looping(event):
global loopBool
while (loopBool == True):
print "Still Looping!"
time.sleep(1)
print "Stop!!"
axCloseButton = plt.axes([0.1, 0.15, 0.2, 0.06])
bClose = Button(axCloseButton, "Close", color = axcolor, hovercolor = '0.975')
bClose.on_clicked(closeLooping)
fig.canvas.mpl_connect('pick_event', looping)
It seems to me that a process stuck in an infinite loop is in a sense incompatible with a GUI. GUIs themselves perform a loop, checking for and responding to events happening. The best solution would probably be to remove your infinite loop, by transforming that part of code to an event-based one.
However, I also found a solution to your actual problem. I'm not very familiar with the programming constructs involved, so I can't tell you how efficient or elegant this solution is. The point is to run your infinite loop in a separate thread, thereby preventing your main python process from being stuck in the loop. This will keep the GUI responsive. However, this can lead to problems if you want to interrupt your program during its run.
The code:
import time
import threading #this is new
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.widgets as widgets
fig, ax = plt.subplots()
# it is not always column 0 and 1
sctPlot = ax.scatter([-0.3,0,0.3], [0.3,0.3,0.3], c="blue", picker = 2, s=[50]*3)
fig.subplots_adjust(bottom=0.3, left=0.1)
plt.grid(False)
plt.axis([-0.6, 0.6, -0.6, 0.6])
loopBool = True
def closeLooping(event):
global loopBool
loopBool = False
def looping(event):
global loopBool
while (loopBool == True):
print("Still Looping!")
time.sleep(1)
print("Stop!!")
def looping_pre(event): #this is new
thread = threading.Thread(target=looping, args=(event,))
#thread.daemon = True #might or might not be needed
thread.start()
axCloseButton = plt.axes([0.1, 0.15, 0.2, 0.06])
bClose = widgets.Button(axCloseButton, "Close", hovercolor = '0.975')
bClose.on_clicked(closeLooping)
plt.show() #this is new
fig.canvas.mpl_connect('pick_event', looping_pre) #this is changed
Note that I changed a few things because your exact code didn't seem to run properly for me. I removed color=axcolor from the Button call; and I added a plt.show() before the event connect, otherwise a figure window didn't appear for me (neither through ipython, nor with python).
The relevant addition is the threading module and the looping_pre front-end, which calls the looping function as a separate Thread. For this reason the 'pick_event' doesn't call looping, but rather looping_pre.
This code will (when running in ipython) show the figure window, start looping on a click to the data, then stop looping on button click. However, when I push ctrl+c, the loop keeps on going, since it is a separate thread. I only managed to kill it by using a reset, thereby removing the value of the global loopBool. The commented line specifying whether the Thread should be deamonized should affect this behaviour (by which I mean that it would seem logical to me), but I didn't see any effect.
I have searched numerous sites, used plots, subplots, some basic animation, and other roundabout ways, but the figure will not close despite using close(), clf(), etc.
I have something like this:
import numpy
from pylab import *
import time
fig = Figure()
counter1 = 0
counter2 = 0
while counter1<5:
counter1 = counter1+1
while counter2<10:
scatter(x_list[counter2], y_list[counter2], hold = 'on') ### x_list and y_list are just lists of random numbers
counter2 = counter2 + 1
show()
sleep(0.5)
close()
I am looking for any solution, as seen above. Plots, subplots, animation...
Two side issues to start: first, are you sure that this is the code you're actually running? sleep isn't a function in my version of pylab, so your import time doesn't seem to match your call, it should be time.sleep(0.5).. Second, I don't understand your loops at all. It looks like you're plotting the same thing 5 times, because counter1 has no effect and you add each point to the scatterplot before you pause. Are you trying to plot x_list/y_list point by point?
If you use draw() instead of show() I think it should work; the show() is what's holding the close(). Is the following something like what you want?
import time
from pylab import *
ion()
# test data
x = arange(0, 10, 0.5)
y = 10*x+exp(x)*abs(cos(x))
for j in range(len(x)):
if j > 0: scatter(x[:j], y[:j])
# assuming we don't want the limits to change
xlim(0, 10)
ylim(0, 1000)
draw()
time.sleep(2)
#close()
Note that I've commented out the close() because this way it produces a nice animation. If you leave it in, it'll keep closing and reopening the window, which could be what you want, but doesn't look very useful to my eyes. YMMV, of course.