bbtools
Box-Behnken Tools (bbtools)
tablewidget
Tabular in/out for Box-Behnken widgets Author: Audun Skau Hansen, 2022
as_numpy_array(self)
Returns the table (excluding headers) as a numpy array
Source code in btjenesten/bbtools.py
def as_numpy_array(self):
"""
Returns the table (excluding headers) as a numpy array
"""
ret = np.zeros(self.tab[1:,1:].shape, dtype = float)
for i in range(self.tab.shape[0]-1):
for j in range(self.tab.shape[1]-1):
ret[i,j] = float(self.tab[i+1,j+1].value)
return ret
set_from_array(self, input_array)
Set the table (excluding headers) from an input array
Source code in btjenesten/bbtools.py
def set_from_array(self, input_array):
"""
Set the table (excluding headers) from an input array
"""
for i in range(self.tab.shape[0]-1):
for j in range(self.tab.shape[1]-1):
self.tab[i+1,j+1].value = str(input_array[i,j])
bbdesign(n_center=3, randomize=True, sheet=None)
Returns a Box-Benhken experimental design for 3 variables Author: Audun Skau Hansen, Department of Chemistry, UiO
Keyword arguments:
Argument | Description |
---|---|
n_center | number of samples in the center |
randomize | whether or not to randomize the ordering (bool) |
sheet | heet containing the min/max values of variables |
Source code in btjenesten/bbtools.py
def bbdesign(n_center = 3, randomize = True, sheet = None):
"""
Returns a Box-Benhken experimental design for 3 variables
Author: Audun Skau Hansen, Department of Chemistry, UiO
## Keyword arguments:
| Argument | Description |
| ----------- | ----------- |
| n_center | number of samples in the center |
| randomize | whether or not to randomize the ordering (bool) |
| sheet | heet containing the min/max values of variables |
"""
a = np.arange(-1,2)
A = np.array(np.meshgrid(a,a,a)).reshape(3,-1).T
A = np.concatenate([A[np.sum(A**2, axis = 1)==2, :], np.zeros((n_center,3))])
ai = np.arange(len(A))
if randomize == True:
# randomize run order
np.random.shuffle(ai)
if sheet is not None:
# Transform coordinates
tm = sheet.as_numpy_array()
for i in range(3):
A[:,i] = interp1d(np.linspace(-1,1,2),tm[i] )(A[:,i])
#A = A.dot(tm)
return A[ai, :], ai
bbsetup()
Returns an interactive sheet (ipysheet) for setting up a Box-Benkhen design session
Author: Audun Skau Hansen, Department of Chemistry, UiO
Source code in btjenesten/bbtools.py
def bbsetup():
"""
Returns an interactive sheet (ipysheet)
for setting up a Box-Benkhen design session
**Author**: Audun Skau Hansen, Department of Chemistry, UiO
"""
global sheet
arr = np.zeros((4,3), dtype = object)
arr[0,0] = ""
arr[1,0] = "Variable A"
arr[2,0] = "Variable B"
arr[3,0] = "Variable C"
arr[0,1] = "Minimum"
arr[0,2] = "Maximum"
arr[1:,1] = -1
arr[1:,2] = 1
sheet = from_array(arr)
sheet.column_headers = False
sheet.row_headers = False
return sheet
bbsheet(sheet)
Returns a Box-Behnken sheet for gathering experimental results Author: Audun Skau Hansen, Department of Chemistry, UiO
Keyword arguments
sheet = setup from bbsetup
Source code in btjenesten/bbtools.py
def bbsheet(sheet):
"""
Returns a Box-Behnken sheet for gathering experimental results
Author: Audun Skau Hansen, Department of Chemistry, UiO
## Keyword arguments
sheet = setup from bbsetup
"""
bd, ai = bbdesign(sheet = sheet)
#global bb_sheet
#sh = sheet.as_numpy_array()
#arr = np.zeros(sh.shape + np.array([1,2]), dtype = object)
#print("sh")
#print(sh)
#print(bd)
column_headers = ["Run",
sheet.row_headers[0],
sheet.row_headers[1],
sheet.row_headers[2],
"Result"
]
row_headers = ["" for i in range(bd.shape[0])]
bb_widget = tablewidget(column_headers,row_headers)
arr = np.zeros( (len(row_headers), len(column_headers)), dtype = float)
arr[:,0] = np.arange(len((row_headers)))+1
arr[:,0] = ai+1
arr[:,1:4] = bd
bb_widget.set_from_array(arr)
return bb_widget
minitable(titles, values, sheet)
Generate a mini-table for displaying inter-variable dependencies as indicated by the model Author: Audun Skau Hansen, Department of Chemistry, UiO
Keyword arguments:
Argument | Description |
---|---|
titles | default variables |
values | coefficients |
sheet | the Box-Behnken sheet |
Source code in btjenesten/bbtools.py
def minitable(titles, values, sheet):
"""
Generate a mini-table for displaying inter-variable
dependencies as indicated by the model
Author: Audun Skau Hansen, Department of Chemistry, UiO
## Keyword arguments:
| Argument | Description |
| ----------- | ----------- |
| titles | default variables |
| values | coefficients |
| sheet | the Box-Behnken sheet |
"""
arr = np.zeros((len(titles),2), dtype = object)
arr[:,0] =relabel_defaults(titles, sheet)
arr[:,1] = values
return from_array(arr)
relabel_defaults(titles, new_names)
Rename default variable ("x0", "x1", "x2") to variable names from sheet[1:,0] Author: Audun Skau Hansen, Department of Chemistry, UiO
Source code in btjenesten/bbtools.py
def relabel_defaults(titles, new_names):
"""
Rename default variable ("x0", "x1", "x2") to
variable names from sheet[1:,0]
**Author**: Audun Skau Hansen, Department of Chemistry, UiO
"""
#new_names = to_array(sheet)[1:4,0]
new_titles = []
for i in titles:
new_titles.append( i.replace("x0", new_names[0]).replace("x1", new_names[1]).replace("x2", new_names[2]) )
return new_titles
visualize_surfaces(bbwidget, Nx=30)
Visualize response surfaces the regressor model Author: Audun Skau Hansen, Department of Chemistry, UiO (2022)
Keyword arguments:
Argument | Description |
---|---|
sheet | Box-Benhken data sheet |
regressor | sklearn LinearRegression instance |
Nx | mesh resolution along each axis |
Source code in btjenesten/bbtools.py
def visualize_surfaces(bbwidget, Nx = 30):
"""
Visualize response surfaces the regressor model
**Author**: Audun Skau Hansen, Department of Chemistry, UiO (2022)
## Keyword arguments:
| Argument | Description |
| ----------- | ----------- |
| sheet | Box-Benhken data sheet |
| regressor | sklearn LinearRegression instance |
| Nx | mesh resolution along each axis |
"""
data = bbwidget.as_numpy_array()
bounds = np.zeros((3,2), dtype = float)
bounds[:,0] = np.min(data[:,1:4], axis = 0)
bounds[:,1] = np.max(data[:,1:4], axis = 0)
#if regressor is None:
# crop data from the sheet above
X_train = data[:,1:4]
y_train = data[:, 4]
#print(X_train, y_train)
# perform a second order polynomial fit
# (linear in the matrix elements)
degree=2 # second order
poly= PolynomialFeatures(degree) # these are the matrix elements
regressor=make_pipeline(poly,LinearRegression()) #set up the regressor
regressor.fit(X_train,y_train) # fit the model
# first, we extract all relevant information
coefficients = regressor.steps[1][1].coef_
names = poly.get_feature_names()
predicted = regressor.predict(X_train)
measured = y_train
score = regressor.score(X_train, y_train)
# we then compute and tabulate various statistics
squared_error = (predicted - measured)**2
#print(predicted, measured)
mean_squared_error = np.mean(squared_error)
variance_error = np.var(squared_error)
std_error = np.std(squared_error)
# find max and min inside bounds using scipy.optimize
from scipy.optimize import minimize
mx = minimize(lambda x : -1*regressor.predict(np.array([x])), X_train[0], bounds = bounds)
max_point = mx.x
max_fun = -1*mx.fun
mn = minimize(lambda x : regressor.predict(np.array([x])), X_train[0], bounds = bounds)
min_point = mn.x
min_fun = mn.fun
#mse = mean_squared_error(predicted, measured) # mean squared error
#mse = np.sum((predicted - measured)**2)/len(predicted) #alternative calculation
print("Mean squared error :", mean_squared_error)
print("Variance of error :", variance_error)
print("Standard dev. error:", std_error)
print("Fitting score. :", score)
print("Maximum coords :", max_point)
print("Maximum value. :", max_fun[0])
print("Minimum coords :", min_point)
print("Minimum value. :", min_fun[0])
xa = np.linspace(bounds[0,0], bounds[0,1],Nx)
xb = np.linspace(bounds[1,0], bounds[1,1],Nx)
xc = np.linspace(bounds[2,0], bounds[2,1],Nx)
va, vb, vc = bbwidget.column_headers[1], bbwidget.column_headers[2], bbwidget.column_headers[3]
# displaying the fitting parameters
fnames = relabel_defaults(poly.get_feature_names(), [va,vb,vc])
ax, fig = plt.subplots(figsize=(9,5))
plt.plot(regressor.steps[1][1].coef_, "s")
#fig.set_xticklabels(poly.get_feature_names())
for i in range(len(regressor.steps[1][1].coef_)):
plt.text(i+.1,regressor.steps[1][1].coef_[i], fnames[i], ha = "left" , va = "center")
plt.axhline(0)
plt.title("Fitting parameters")
plt.show()
# print a table of the fitting parameters
print(np.array([fnames, regressor.steps[1][1].coef_]).T)
print("Intercept:", regressor.steps[1][1].intercept_)
"""
plt.figure(figsize=(9.5,8))
plt.title(va + " vs " + vb)
plt.contourf(xa,xb,yab)
plt.xlabel(va)
plt.ylabel(vb)
plt.colorbar()
plt.show()
"""
"""
plt.figure(figsize=(9.5,8))
plt.title(va + " vs " + vc)
plt.contourf(xa,xc,yac)
plt.xlabel(va)
plt.ylabel(vc)
plt.colorbar()
plt.show()
"""
"""
plt.figure(figsize=(9.5,8))
plt.title(vb + " vs " + vc)
plt.contourf(xb,xc,ybc)
plt.xlabel(vb)
plt.ylabel(vc)
plt.colorbar()
plt.show()
"""
#fig = plt.figure(figsize=(9,3))
fig, (ax1, ax2, ax3) = plt.subplots(1,3, figsize = (9,4), sharey = True)
#fig.
#ax = fig.add_subplot(1, 3, 1)
Xa = np.zeros((Nx,3), dtype = float)
Xa[:,0] = xa
Xa[:,1:] = min_point[1:]
ax1.plot(xa, regressor.predict(Xa))
ax1.set_xlabel(va)
#ax = fig.add_subplot(1, 3, 2)
Xb = np.zeros((Nx,3), dtype = float)
Xb[:,1] = xb
Xb[:,0] = min_point[0]
Xb[:,2] = min_point[2]
ax2.plot(xb, regressor.predict(Xb))
ax2.set_xlabel(vb)
ax2.set_title("Fitted means")
#ax = fig.add_subplot(1, 3, 3)
Xc = np.zeros((Nx,3), dtype = float)
Xc[:,2] = xc
Xc[:,0] = min_point[0]
Xc[:,1] = min_point[1]
ax3.plot(xc, regressor.predict(Xc))
ax3.set_xlabel(vc)
#ax.show()
#plt.show()
xab3 = np.vstack((np.array(np.meshgrid(xa, xb)).reshape(2,-1), np.zeros(Nx**2))).T
yab = regressor.predict(xab3).reshape((Nx,Nx))
#fig, ax = plt.subplots() #subplot_kw={"projection": "3d"})
fig = plt.figure(figsize=(9,3))
ax = fig.add_subplot(1, 3, 1, projection='3d')
X,Y = np.meshgrid(xa, xb)
surf = ax.plot_surface(X,Y,yab, linewidth=0, antialiased=False, cmap=cm.coolwarm)
#fig.colorbar(surf, shrink=0.3, aspect=5)
ax.contour(X, Y, yab, zdir='z', offset=yab.min(), cmap=cm.coolwarm)
plt.xlabel(va)
plt.ylabel(vb)
#plt.show()
xac3 = np.vstack((np.array(np.meshgrid(xa, xc)).reshape(2,-1), np.zeros(Nx**2))).T
xac3[:,[1,2]] = xac3[:, [2,1]]
yac = regressor.predict(xac3).reshape((Nx,Nx))
ax = fig.add_subplot(1, 3, 2, projection='3d')
X,Y = np.meshgrid(xa, xc)
surf = ax.plot_surface(X,Y,yac, linewidth=0, antialiased=False, cmap=cm.coolwarm)
#fig.colorbar(surf, shrink=0.3, aspect=5)
ax.contour(X, Y, yac, zdir='z', offset=yac.min(), cmap=cm.coolwarm)
plt.xlabel(va)
plt.ylabel(vc)
#plt.show()
xbc3 = np.vstack((np.array(np.meshgrid(xb, xc)).reshape(2,-1), np.zeros(Nx**2))).T
xbc3[:,[0,1,2]] = xac3[:, [1,0,2]]
ybc = regressor.predict(xbc3).reshape((Nx,Nx))
ax = fig.add_subplot(1, 3, 3, projection='3d')
X,Y = np.meshgrid(xb, xc)
surf = ax.plot_surface(X,Y,ybc, linewidth=0, antialiased=False, cmap=cm.coolwarm)
#fig.colorbar(surf, shrink=0.3, aspect=5)
ax.contour(X, Y, ybc, zdir='z', offset=ybc.min(), cmap=cm.coolwarm)
plt.xlabel(vb)
plt.ylabel(vc)
plt.show()