I'm writing a code which plots raw data from an experiment and I'm trying to manipulate a regression in that plot using matplotlib.widget
with the Slider
widget. The widget changes a fitting parameter of the regression and automatically re-calculates the regression as you change the parameter. Once I've found the ideal parameter (by visual inspection of the regression) I then want to accept that parameter and move on with my code. The problem I'm having is that the entire code is run, regardless of me pressing the accept button. It seems to me that this is blocking issue, and trying to follow a very similar question has not proved successful:
Continue code after closing matplotlib figure
I've read that it may be a graphics backend issue but I've tried everything in my IDE's (Spyder 4.1.3 running Python 3.7.6) drop-down list:
- Inline
- Automatic
- Qt5
- Qt4
- Tkinter
I don't know if it's a graphics backend issue (I'm running Qt5
currently) or an error in my code. It's very possible that it's an error in the code since this is my first go at python but I'm not getting any errors and even a simple pseudo-code like the following isn't doing what's expected:
from matplotlib.pyplot import plot,show
plot([1,2,3])
show(block=True)
show()
print ('continue computation after plot closes')
I've also tried the answers, or I guess the reverse of, in these links to no avail:
Plotting in a non-blocking way with Matplotlib
Is there a way to detach matplotlib plots so that the computation can continue?
Any ideas how I can continue my code after a plot closes?
Thanks for your help.
p.s. My actual code is the following, although the pseuo-code above should be sufficient (note that the 95% confidence interval doesn't reset
as I'd like but that's a battle for another day):
import numpy as np
import matplotlib.pyplot as plt
import csv
import math
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C
from matplotlib.widgets import Slider, Button
def kernreg(xx,yy,zz,aa):
#Define the kernel regression function.
#xx=Input x-data
#yy=Input y-data
#zz=Predicted x-values
#aa=Passed value of alpha
#Function outputs is the predicted y-values and mean squared error
#Instantiate Gaussian Process model with a product-kernel consisting of a
#radial-basis function kernel and a constant kernel
kernel=C(1.0, (1e-3, 1e3))*RBF(10, (1e-2, 1e2))
gp=GaussianProcessRegressor(kernel=kernel,alpha=aa,
optimizer='fmin_l_bfgs_b',
n_restarts_optimizer=25)
#Fit to data using Maximum Likelihood Estimation of the parameters
gp.fit(xx,yy)
#Make the prediction on the meshed x-axis (ask for MSE as well)
y_pred,sig=gp.predict(zz,return_std=True)
return y_pred,sig
in2ft=1/12 #Convert inches to ft
fps2mph=1/1.4667 #Convert feet per second to miles per hour
fpsps2g=1/32.174049 #Convert feet per second squared to g
z_conf_int=1.96 #Z-score for a 95% confidence interval
#Import raw data; x [in], y[in], z[in]
with open('XYZ_data_20fps.csv','r') as datafile:
readCSV=csv.reader(datafile, delimiter=',')
x=[]
y=[]
z=[]
for row in readCSV:
x_i=float(row[0])
y_i=float(row[1])
z_i=float(row[2])
x.append(x_i)
y.append(y_i)
z.append(z_i)
#Calculate the raw distance travelled
d=[] #Distance travelled [ft]
for i in range(len(x)-1):
d_i=math.sqrt((x[i+1]-x[i])**2+(y[i+1]-y[i])**2+(z[i+1]-z[i])**2)*in2ft
d.append(d_i)
#Sample frame queries
frm_pred=np.atleast_2d(np.arange(1,len(x)+0.25,0.25)).T
#Frames analyzed (1=first frame)
frm=np.arange(1,len(x)+1)
frm_data=np.atleast_2d(frm[1:]).T
#Calling the kernel regression function for the initial guess
alpha_0=0.5
d_pred_0,sigma_0=kernreg(frm_data,d,frm_pred,alpha_0)
fig=plt.figure()
ax=fig.add_subplot(111)
fig.subplots_adjust(bottom=0.25)
#Draw the initial plot
plt.plot(frm_data,d,'o',color='black',label='Data')
[line]=plt.plot(frm_pred,np.array(kernreg(frm_data,d,frm_pred,alpha_0) [0:1]).T,'b-',label='Prediction')
plt.xlabel("Frame")
plt.ylabel("Distance Travelled [ft]")
plt.legend(loc='best')
#Define an axes area and draw a slider in it
axis_color = 'lightgoldenrodyellow'
alph_slider_ax = fig.add_axes([0.175, 0.1, 0.65, 0.03], facecolor=axis_color)
alph_slider = Slider(alph_slider_ax, chr(945), 0.01, 1.0, valinit=alpha_0)
#Define an action for modifying the line when any slider's value changes
def sliders_on_changed(val):
line.set_ydata(kernreg(frm_data,d,frm_pred,alph_slider.val)[0:1])
fig.canvas.draw_idle()
alph_slider.on_changed(sliders_on_changed)
#Add a button for resetting the parameters
reset_button_ax = fig.add_axes([0.175, 0.05, 0.1, 0.04])
reset_button = Button(reset_button_ax, 'Reset',color=axis_color,hovercolor='0.975')
def reset_button_on_clicked(mouse_event):
alph_slider.reset()
reset_button.on_clicked(reset_button_on_clicked)
#Add a button for calculating the 95% confidence interval
conf_button_ax=fig.add_axes([0.175, 0.005, 0.35, 0.04])
conf_button=Button(conf_button_ax, 'Calculate Confidence Interval', color=axis_color,hovercolor='0.975')
def conf_button_on_click(mouse_event):
ax.fill(np.concatenate([frm_pred,frm_pred[::-1]]),
np.concatenate(
[np.array(kernreg(frm_data,d,frm_pred,alph_slider.val)[0:1]).T- z_conf_int*np.array(kernreg(frm_data,d,frm_pred,alph_slider.val)[1:2]).T,
(np.array(kernreg(frm_data,d,frm_pred,alph_slider.val)[0:1]).T+z_conf_int*np.array(kernreg(frm_data,d,frm_pred,alph_slider.val)[1:2]).T)[::-1]]),
alpha=.5, fc='b', ec='None', label='95% Confidence Interval')
ax.legend(loc='best') #Plot new legend with the confidence interval
conf_button.on_clicked(conf_button_on_click)
#Add a button to accept the chosen value of alpha
accept_button_ax=fig.add_axes([0.28, 0.05, 0.15, 0.04])
accept_button=Button(accept_button_ax,'Accept Value',color=axis_color,hovercolor='0.975')
mutable_object={}
def accept_button_on_click(mouse_event):
alpha=alph_slider.val
mutable_object['mouse_event']=alpha
alpha=mutable_object['mouse_event']
print(alpha)
plt.close()
accept_button.on_clicked(accept_button_on_click)
print('THIS SHOULD RUN ONLY AFTER I PRESS ACCEPT')
print('REST OF COMPUTATIONS')