seaborn distplot loop lazy evaluation - python

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

Related

matplotlib funcAnimation won't repeat

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.

Stop Returning SNS plot when a function is called

Here is the Python code. I am using a function here which should return a float value. However, it is also returning the graph as sns.distplot is called. Is there any way to only return the numeric value?
data = []
for idx, var in enumerate(df):
myPlot = sns.distplot(df['Acceleration'])
lines2D = [obj for obj in myPlot.findobj() if str(type(obj)) == "<class 'matplotlib.lines.Line2D'>"]
x, y = lines2D[idx].get_data()[0], lines2D[idx].get_data()[1]
data.append(pd.DataFrame({'x':x, 'y':y}))
limit=pd.DataFrame(data[0]).loc[data[0]['y']>=0.01]['x']
limit=limit.values.tolist()
off= df[(df.Acceleration< limit[0]) | (df.Acceleration>limit[-1])]
return len(off)/len(df)
In case you're using jupyter:
I don't think your graph is returned by the function, it is simply displayed while "working" your function. Try inserting plt.close() at the end of your for loop block and I'm sure your plot won't show anymore. I've tried it and it works, the sns plot display will be suppressed. Make sure to put import matplotlib.pyplot as plt at the top of your file.

Way to wait until user deletes Matplotlib figure before adding more?

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.

pystan .plot() plots trace summary twice

This is probably a stupid question, but whenever I'm using the .plot() function it plots the summary twice. Anyone knows, why it does that and how I can stop it?
As you can see I'm using jupyter notebooks if that matters.
It happens with any stan model (and on two separate installations)
This code would produce the problem for me
import pystan
import numpy as np
model_string = """
data {
int<lower=0> N;
int y[N];
}
parameters {
real<lower=0, upper=1> theta;
}
model {
theta ~ beta(1,1);
y ~ bernoulli(theta);
}
"""
N = 50
z = 10
y = np.append(np.repeat(1, z), np.repeat(0, N - z))
dat = {'y':y,
'N':N}
fit = pystan.stan(model_code=model_string, data=dat, iter=1000, warmup=200, thin=1, chains = 3)
fit.plot()
This is caused by the %matplotlib inline statement drawing more than you want it to. The StanFit4Model.plot method calls matplotlib.pyplot.subplot, and that call itself will draw a plot when your notebook has %matplotlib inline. Then the method returns the Figure object. If you don't capture it, your notebook decides to show it to you as an image instead of printing the type, and you get the double plot.
You can output a single plot by capturing the output Figure. Change your code from
fit.plot()
to instead be
fig = fit.plot()
Putting a semicolon after the `.plot()' also does the trick.
Learned it from https://github.com/stan-dev/pystan/issues/230

How does one close a figure or replace a figure without having to manually close each figure in Python/pylab?

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.

Categories