In [1]:
%pylab inline

import csv
import pandas as pd

from scipy.interpolate import SmoothBivariateSpline
from scipy.optimize import minimize
from mpl_toolkits.mplot3d import Axes3D


Populating the interactive namespace from numpy and matplotlib

In [2]:
def single_angle_gap(xTest,yTest,xData,yData,xScale,yScale):
    
    xi = xScale * (xData - xData.min()) / xData.ptp()
    yi = yScale * (yData - yData.min()) / yData.ptp()
    
    xVal = xScale * (xTest - xData.min()) / xData.ptp()
    yVal = yScale * (yTest - yData.min()) / yData.ptp()
    
    dx = xi - xVal
    dy = yi - yVal
    
    if any((dx == 0) & (dy == 0)):
        gap = 0
        return gap
        
    theta = np.arctan(dy/dx)
    theta[dx<0] = theta[dx<0] + np.pi
    theta[(dx>0) & (dy<0)] = theta[(dx>0) & (dy<0)] + 2*np.pi
    theta[(dx==0) & (dy>0)] = np.pi/2
    theta[(dx==0) & (dy<0)] = 3*np.pi/2

    test = np.sort(theta)
    test = np.append(test,test[0] + 2*np.pi)
    gap = np.max(np.diff(test))*180/np.pi

    return gap


def angle_gap(xTest,yTest,xData,yData,xScale,yScale):
    
    dim = np.core.fromnumeric.shape(xTest)  
    
    if np.size(dim) == 0:
        gap = single_angle_gap(xTest,yTest,xData,yData,xScale,yScale)
        
        return gap
    
    
    gap = np.zeros(dim)
    
    
    if np.size(dim) == 1:
        for i in range(dim[0]):
            gap[i] = single_angle_gap(xTest[i],yTest[i],xData,yData,xScale,yScale)
            
        return gap
    
    
    for i in range(dim[0]):
        for j in range (dim[1]):
            gap[i,j] = single_angle_gap(xTest[i,j],yTest[i,j],xData,yData,xScale,yScale)

    return gap

In [3]:
def single_fit_give(xTest,yTest,xData,yData,zData,s=None):
    
    initialFitReturn = SmoothBivariateSpline(xData,yData,zData,kx=2,ky=2,s=s).ev(xTest,yTest)
    
    adjXData = np.append(xData,xTest)
    adjYData = np.append(yData,yTest)
    
    posAdjZData = np.append(zData,initialFitReturn + 1)
    negAdjZData = np.append(zData,initialFitReturn - 1)
    
    posFitReturn = SmoothBivariateSpline(adjXData,adjYData,posAdjZData,kx=2,ky=2,s=s).ev(xTest,yTest)
    negFitReturn = SmoothBivariateSpline(adjXData,adjYData,negAdjZData,kx=2,ky=2,s=s).ev(xTest,yTest)
    
    posGive = posFitReturn - initialFitReturn
    negGive = initialFitReturn - negFitReturn
    
    give = np.mean([posGive, negGive])
    
    return give


def fit_give(xTest,yTest,xData,yData,zData,s=None):
    
    dim = np.core.fromnumeric.shape(xTest)
    
    if np.size(dim) == 0:
        give = single_fit_give(xTest,yTest,xData,yData,zData,s=s)
        
        return give
    
    
    give = np.zeros(dim)
    
    
    if np.size(dim) == 1:
        for i in range(dim[0]):
            give[i] = single_fit_give(xTest[i],yTest[i],xData,yData,zData,s=s)
            
        return give
    
    
    for i in range(dim[0]):
        for j in range (dim[1]):
            give[i,j] = single_fit_give(xTest[i,j],yTest[i,j],xData,yData,zData,s=s)

    return give

In [4]:
pylab.rcParams['savefig.dpi'] = 130

In [5]:
# Define thresholds
giveThreshold = 0.5
gapThreshold = 180

In [6]:
with open('cutout_factors','r') as f:
    
    loaded_factor = eval(f.read())
    
dictArray = array(list(loaded_factor.items()),float)
cutoutRef = dictArray[:,0].astype(int)
ref = argsort(cutoutRef)

factor = dictArray[:,1]

cutoutRef = cutoutRef[ref]
factor = factor[ref]


with open('circle_cutout_factors','r') as f:
    
    loaded_circle_factor = eval(f.read())
    
circleDictArray = array(list(loaded_circle_factor.items()),float)
circleDiameter = circleDictArray[:,0]
circleFactor = circleDictArray[:,1]

factor = append(factor,circleFactor)


with open('custom_cutout_factors','r') as f:
    
    loaded_custom_factor = eval(f.read())

customDimensionsDict = {'5x13':[5,13],
                   '5x10':[5,10],
                   '5x8':[5,8],
                   '4x13':[4,13],
                   '4x10':[4,10],
                   '4x8':[4,8],
                   '4x6.5':[4,6.5],
                   '3x13':[3,13],
                   '3x9':[3,9],
                   '3x6.5':[3,6.5],
                   '3x5':[3,5]}

customDataLabelList = list(customDimensionsDict.keys())

custom_factors = [loaded_custom_factor[x] for x in customDataLabelList]
custom_widths = [customDimensionsDict[x][0] for x in customDataLabelList]
custom_lengths = [customDimensionsDict[x][1] for x in customDataLabelList]


factor = append(factor,custom_factors)

In [7]:
# loaded_factor.items()

In [8]:
cutoutRef


Out[8]:
array([  3,   6,  14,  16,  18,  19,  20,  22,  32,  33,  34,  38,  41,
        43,  57,  58,  70,  73,  82,  83, 104, 106, 109, 112])

In [8]:


In [9]:
cutout_dimensions = pd.DataFrame.from_csv('new_cutoutdata.csv',index_col =None)

ref = argsort(cutout_dimensions['cutoutRef'].values.astype(int))

width = cutout_dimensions['new_width'].values[ref]
length = cutout_dimensions['new_length'].values[ref]

weight = concatenate((ones(shape(width)), 1*ones(shape(circleDiameter)), ones(shape(custom_widths))))


width = append(width,circleDiameter)
length = append(length,circleDiameter)

nanFill = empty(shape(circleDiameter))
nanFill.fill(nan)

cutoutRef = append(cutoutRef,nanFill)


width = append(width,custom_widths)
length = append(length,custom_lengths)

nanFill = empty(shape(custom_widths))
nanFill.fill(nan)

cutoutRef = append(cutoutRef,nanFill)



# editRef = cutoutRef == 14
# width[editRef] = 4.3
# length[editRef] = 4.6

ratio = width / length

# PonA = 2 * (ratio + 1) / width
R = ratio

PonA = 2*( 3*(R+1) - sqrt( (3*R+1)*(R+3) ) ) / width

In [10]:
circleDiameter


Out[10]:
array([ 5.,  6.,  8.,  3.,  9.,  4.,  7.])

In [11]:
scatter(circleDiameter,circleFactor)


Out[11]:
<matplotlib.collections.PathCollection at 0x8ffa208>

In [12]:
scatter(width,PonA)
title('Data so far')
xlabel('Width (cm)')
ylabel('PonA')


Out[12]:
<matplotlib.text.Text at 0x9885710>

In [13]:
PonA


Out[13]:
array([ 0.77798816,  0.47844919,  0.83573069,  0.52677861,  0.46382288,
        0.54841504,  0.85915659,  0.59701373,  0.4420151 ,  0.66084598,
        0.49367704,  0.7789461 ,  0.49290812,  0.58844874,  0.99653702,
        0.61051311,  0.50761466,  0.49461768,  0.57188045,  0.60173369,
        0.59898442,  0.54161672,  0.57429839,  1.04880758,  0.8       ,
        0.66666667,  0.5       ,  1.33333333,  0.44444444,  1.        ,
        0.57142857,  0.90269906,  0.58154284,  1.00770775,  0.70049259,
        0.73252057,  0.61678404,  1.08339895,  0.77098005,  0.81918004,
        0.65868284,  0.94534074])

In [14]:
# Spline fitting

splineFit = SmoothBivariateSpline(width,PonA,factor,kx=2,ky=2)

In [15]:
# Create surface mesh
xVec2 = linspace(width.min(),width.max(),100)
yVec2 = linspace(PonA.min(),PonA.max(),100)
xMesh2, yMesh2 = meshgrid(xVec2, yVec2)

gapMesh2 = angle_gap(xMesh2,yMesh2,width,PonA,1,1)
giveMesh2 = fit_give(xMesh2,yMesh2,width,PonA,factor)

zMesh2 = splineFit.ev(xMesh2, yMesh2)

ref2 = (gapMesh2>gapThreshold) | (giveMesh2>giveThreshold)
zMesh2[ref2] = nan

In [16]:
# Plot the fit
fig = plt.figure()

ax = fig.add_subplot(111, projection='3d')

bot = np.amin(factor) - 0.04 * np.ptp(factor)
top = np.amax(factor) + 0.04 * np.ptp(factor)

ax.contour(xMesh2,yMesh2,gapMesh2, offset=bot, levels=[gapThreshold], colors='r',alpha=0.3)
ax.contour(xMesh2,yMesh2,giveMesh2, offset=bot, levels=[giveThreshold], colors='g',alpha=0.3)

proxyGap, = ax.plot([width.min(),width.min()],[PonA.min(),PonA.min()],[bot,bot],'r-',alpha=0.3)
proxyGive, = ax.plot([width.min(),width.min()],[PonA.min(),PonA.min()],[bot,bot],'g-',alpha=0.3)

ax.plot_surface(xMesh2,yMesh2,zMesh2,alpha=0.3,rstride=4,cstride=4)
sc = ax.scatter(width,PonA,factor)

ax.set_title('Fit displayed against width and aspect ratio')
ax.set_xlabel('Width')
ax.set_ylabel('PonA')
ax.set_zlabel('Factor')
legend([proxyGap,proxyGive],['Gap threshold', 'Give threshold'],loc=7,prop={'size':8})

# right = ratio.min()-0.01*ratio.ptp()
# ax.set_ylim(right,1)
ax.set_zlim(bot,top)
ax.view_init(elev=10, azim=-80)



In [17]:
# Remove data and predict
predictedFactor = zeros(shape(width))
predictionAngleGap = zeros(shape(width))
predictionFitGive = zeros(shape(width))

for i in range(len(width)):
    
    x = width[i]
    y = PonA[i]

    adjWidth = np.delete(width,i)
    adjPonA = np.delete(PonA,i)
    adjFactor = np.delete(factor,i)
   
    adjSplineFit = SmoothBivariateSpline(adjWidth,adjPonA,adjFactor,kx=2,ky=2)
    
    predictedFactor[i] = adjSplineFit.ev(x,y)
    predictionAngleGap[i] = angle_gap(x,y,adjWidth,adjPonA,1,1)
    predictionFitGive[i] = fit_give(x,y,adjWidth,adjPonA,adjFactor)
    
difference = factor - predictedFactor
withinThresholds = ((predictionAngleGap<gapThreshold) & 
                    (predictionFitGive<giveThreshold)
                    )

In [18]:
# Display table
df = pd.DataFrame(transpose([cutoutRef,width, length, factor, predictedFactor, difference,
                             predictionAngleGap, predictionFitGive, withinThresholds]))
df.columns = ['Reference','width', 'length', 'factor', 'Predicted Factor', 'Difference',
              'Angle Gap', 'Fit Give', 'Within thresholds']

# df.to_csv(path_or_buf = "cutoutdata.csv", index=False)

df


Out[18]:
Reference width length factor Predicted Factor Difference Angle Gap Fit Give Within thresholds
0 3 4.498726 6.077114 1.032704 1.031868 0.000837 89.337139 0.120782 1
1 6 6.996235 10.669437 1.001302 0.995010 0.006292 180.334476 0.156412 0
2 14 4.388701 5.288022 1.043027 1.036181 0.006846 130.845095 0.117291 1
3 16 6.107799 10.515871 0.999567 1.001264 -0.001696 178.294180 0.096888 1
4 18 7.602645 10.075798 0.998054 0.994204 0.003851 136.706355 0.155734 1
5 19 5.799849 10.375006 1.001089 1.004184 -0.003095 170.266596 0.084669 1
6 20 4.289047 5.112375 1.037967 1.039559 -0.001593 136.208882 0.121616 1
7 22 5.178076 10.260920 1.011713 1.010640 0.001072 121.928496 0.095411 1
8 32 7.715225 11.165712 0.993330 0.995860 -0.002530 199.614474 0.419822 0
9 33 5.905678 6.209543 1.006789 1.012798 -0.006009 100.136552 0.178697 1
10 34 7.044718 9.674088 0.993330 0.996887 -0.003556 87.898580 0.096103 1
11 38 4.677451 5.724189 1.027190 1.031141 -0.003951 90.482884 0.122288 1
12 41 6.919052 10.011766 0.994829 0.997069 -0.002240 144.111877 0.106147 1
13 43 6.100202 7.735733 1.007783 1.006012 0.001770 108.031390 0.076423 1
14 57 3.574667 4.618824 1.059704 1.052976 0.006728 92.694656 0.222113 1
15 58 5.880419 7.454923 1.008999 1.009117 -0.000118 85.669819 0.081305 1
16 70 7.461006 8.363232 0.997190 0.994541 0.002649 134.181981 0.149547 1
17 73 7.294143 9.137754 0.995472 0.995776 -0.000303 119.868940 0.094773 1
18 82 6.268781 7.974677 1.000867 1.004299 -0.003432 89.641420 0.076190 1
19 83 5.782263 7.931109 1.010552 1.008924 0.001628 82.948379 0.075028 1
20 104 5.658933 8.328343 1.006601 1.009658 -0.003057 102.384349 0.072107 1
21 106 6.087204 9.708158 1.000871 1.002675 -0.001805 110.699146 0.072353 1
22 109 6.306754 7.827168 1.006979 1.003676 0.003303 117.855350 0.079863 1
23 112 3.538729 4.149551 1.053904 1.058880 -0.004976 141.171370 0.286529 1
24 NaN 5.000000 5.000000 1.030396 1.027093 0.003303 150.015849 0.371316 1
25 NaN 6.000000 6.000000 1.014436 1.009547 0.004889 150.264831 0.272449 1
26 NaN 8.000000 8.000000 0.993360 0.990951 0.002409 152.215887 0.357113 1
27 NaN 3.000000 3.000000 1.075771 1.065699 0.010072 324.187410 0.939597 0
28 NaN 9.000000 9.000000 0.991661 0.996926 -0.005265 319.663593 0.789578 0
29 NaN 4.000000 4.000000 1.045826 1.051876 -0.006050 150.815444 0.493406 1
30 NaN 7.000000 7.000000 0.996776 0.998637 -0.001860 151.092816 0.270991 1
31 NaN 3.000000 13.000000 1.049271 1.048352 0.000918 216.303243 0.384269 0
32 NaN 5.000000 13.000000 1.013374 1.007594 0.005780 199.515172 0.215430 0
33 NaN 3.000000 6.500000 1.054423 1.061675 -0.007253 180.000000 0.269647 0
34 NaN 4.000000 13.000000 1.024896 1.024736 0.000160 195.011720 0.268158 0
35 NaN 4.000000 10.000000 1.029933 1.028795 0.001138 138.881484 0.151693 1
36 NaN 5.000000 10.000000 1.012277 1.013611 -0.001334 119.400940 0.100009 1
37 NaN 3.000000 5.000000 1.069714 1.063917 0.005797 180.000000 0.339473 0
38 NaN 4.000000 8.000000 1.030055 1.033938 -0.003883 131.562762 0.115729 1
39 NaN 4.000000 6.500000 1.040668 1.038031 0.002637 119.345427 0.128031 1
40 NaN 5.000000 8.000000 1.017153 1.018364 -0.001211 105.719076 0.099628 1
41 NaN 3.000000 9.000000 1.053516 1.053525 -0.000010 180.000000 0.275392 0

In [19]:
print("There are", sum(withinThresholds), "points within give and gap thresholds."+
      "\nThe standard deviation of the prediction differences for these is", std(difference[withinThresholds]))


There are 32 points within give and gap thresholds.
The standard deviation of the prediction differences for these is 0.00340871780594

In [20]:
# Histogram
fig = plt.figure()
ax = fig.add_subplot(111)

n, bins, patches = hist(difference[withinThresholds])

setp(patches, 'facecolor', 'g', 'alpha', 0.5)
ax.set_xlabel('Prediction differences')
ax.set_ylabel('Frequency')
ax.set_title('Histogram of prediction differences')


Out[20]:
<matplotlib.text.Text at 0x9a86978>

In [21]:
# Visual representation of differences
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.plot_surface(xMesh2,yMesh2,zMesh2,alpha=0.3,rstride=4,cstride=4)

diffColour = copy(difference)
diffColour[~withinThresholds] = nan

colourRange = nanmax(abs(diffColour))

sc = ax.scatter(width,PonA,factor,c=diffColour, vmin=-colourRange, vmax=colourRange,s=50)
colorbar(sc)

ax.set_title('Differences')
ax.set_xlabel('Width')
ax.set_ylabel('PonA')
ax.set_zlabel('Factor')

# ax.set_ylim(right,1)
ax.set_zlim(bot,top)

ax.view_init(elev=10, azim=-80)



In [22]:
diffTest = abs(difference)
diffTest[~withinThresholds] = 0

testRef = argmax(diffTest)
cutoutRef[testRef]


Out[22]:
14.0

In [23]:
factor[testRef]


Out[23]:
1.0430265009664661

In [24]:
scatter(width,PonA)
scatter(width[testRef],PonA[testRef],c='red')


Out[24]:
<matplotlib.collections.PathCollection at 0x97d0b00>

In [25]:
def to_minimise(cut_increase,ref):
    
    test_width = width[ref] + cut_increase
    test_PonA = PonA[ref] + cut_increase
    
    return (factor[ref] - splineFit.ev(test_width,test_PonA))

In [26]:
output = minimize(to_minimise,0,args=(testRef,))
output


Out[26]:
   status: 0
      jac: array([ -6.82473183e-06])
        x: array([ 0.09493757])
  success: True
 hess_inv: array([[ 1.75041976]])
     nfev: 12
     njev: 4
      fun: 0.0034832858880236017
  message: 'Optimization terminated successfully.'

In [26]: