Following the best practices, I protected my code with tests (I preferred Doctest for this example). In this way, we can now transform well.

Could you please help me to improve the code quality? You can easily share all your feedback or suggest some improvements.

Many Thanks,

Ivan

**code**: CubicSplineStruct.py

```
"" "
Natural cubic spline interpolation
Reference: https://en.wikipedia.org/wiki/Spline_interpolation
To run doc tests: python -m doctest CubicSplineStruct.py
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 4
>>> cubicSplineStruct.m_xvalues = [0.0, 10./3., 20./3., 10.]
>>> cubicSplineStruct.computeYtoKMatrix ()
>>> cubicSplineStruct.m_yvalues = [128., -64., 128., -64.]
>>> cubicSplineStruct.computeKCoeffs ()
>>> print (cubicSplineStruct.interpolate (10./3.))
-64.0
>>> print (cubicSplineStruct.interpolate (5.))
32.0
>>> print (cubicSplineStruct.interpolate (20./3.))
128.0
"" "
import numpy as np
from bisect import bisect_left
Class CubicSplineStruct:
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 3
>>> cubicSplineStruct.m_xvalues = [0., 10., 42.]
>>> cubicSplineStruct.computeYtoKMatrix ()
>>> cubicSplineStruct.m_yvalues = [100., 75., 95.]
>>> cubicSplineStruct.computeKCoeffs ()
>>> y = cubicSplineStruct.interpolate (30.)
"" "
def __init __ (self):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> print (cubicSplineStruct.m_n)
0
>>> print (cubicSplineStruct.m_xvalues)
[]
>>> print (cubicSplineStruct.m_yvalues)
[]
>>> print (cubicSplineStruct.m_kMatrix)
[]
>>> print (cubicSplineStruct.m_yMatrix)
[]
>>> print (cubicSplineStruct.m_ytoKMatrix)
[]
>>> print (cubicSplineStruct.m_kCoeffs)
[]
"" "
self.m_n = 0
self.m_xvalues = []
self.m_yvalues = []
self.m_kMatrix = np.matrix (np.zeros (shape = (0,0)))
self.m_yMatrix = np.matrix (np.zeros (shape = (0,0)))
self.m_ytoKMatrix = np.matrix (np.zeros (shape = (0,0)))
self.m_kCoeffs = []
consist
def pushFirstEquationToKMatrix (self, x0, x1):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_kMatrix = np.matrix (np.zeros (shape = (1,5)))
>>> cubicSplineStruct.pushFirstEquationToKMatrix (1.0, 1.5)
>>> print (cubicSplineStruct.m_kMatrix.)[0, 0]) # 2./(x1 - x0)
4.0
>>> print (cubicSplineStruct.m_kMatrix.)[0, 1]) # 1./(x1 - x0)
2.0
"" "
self.m_kMatrix[0, 0] = 2./(x1 - x0)
self.m_kMatrix[0, 1] = 1./(x1 - x0)
def pushLastEquationToKMatrix (self, xnm1, xn):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_kMatrix = np.matrix (np.zeros (shape = (5,5)))
>>> cubicSplineStruct.pushLastEquationToKMatrix (1.0, 1.5)
>>> print (cubicSplineStruct.m_kMatrix.)[-1, -1]) # 2./(xn - xnm1)
4.0
>>> print (cubicSplineStruct.m_kMatrix.)[-1, -2]) # 1./(xn - xnm1)
2.0
"" "
self.m_kMatrix[-1, -1] = 2./(xn - xnm1)
self.m_kMatrix[-1, -2] = 1./(xn - xnm1)
def pushMiddleEquationToKMatrix (self, i, xim1, xi, xip1):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_kMatrix = np.matrix (np.zeros (shape = (4,5)))
>>> cubicSplineStruct.pushMiddleEquationToKMatrix (3, 1.0, 1.5, 1.75)
>>> print (cubicSplineStruct.m_kMatrix.)[3, 2]) # 1./(xi - xim1)
2.0
>>> print (cubicSplineStruct.m_kMatrix.)[3, 3]) # 2./(xi - xim1) + 2./(xip1 - xi)
12.0
>>> print (cubicSplineStruct.m_kMatrix.)[3, 4]) # 1./(xip1 - xi)
4.0
"" "
self.m_kMatrix[i, i-1] = 1./(xi - xim1)
self.m_kMatrix[i, i] = 2./(xi - xim1) + 2./(xip1 - xi)
self.m_kMatrix[i, i + 1] = 1./(xip1 - xi)
def computeKMatrix (self):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 4
>>> cubicSplineStruct.m_xvalues = [1.0, 1.5, 1.75, 2.25]
>>> cubicSplineStruct.computeKMatrix ()
>>> print (cubicSplineStruct.m_kMatrix)
[[ 4. 2. 0. 0.]
[ 2. 12. 4. 0.]
[ 0. 4. 12. 2.]
[ 0. 0. 2. 4.]]"" "
self.m_kMatrix = np.matrix (np.zeros (shape = (self.m_n, self.m_n)))
self.pushFirstEquationToKMatrix (self.m_xvalues[0]self.m_xvalues[1])
for i in the range (1, self.m_n-1):
self.pushMiddleEquationToKMatrix (i, self.m_xvalues[i-1]self.m_xvalues[i]self.m_xvalues[i+1])
self.pushLastEquationToKMatrix (self.m_xvalues[-2]self.m_xvalues[-1])
def pushFirstEquationToYMatrix (self, x0, x1):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_yMatrix = np.matrix (np.zeros (shape = (1,5)))
>>> cubicSplineStruct.pushFirstEquationToYMatrix (1.0, 1.5)
>>> print (cubicSplineStruct.m_yMatrix.)[0, 0]) # -3./(x1 - x0) ** 2
-12.0
>>> print (cubicSplineStruct.m_yMatrix.)[0, 1]) # 3./(x1 - x0) ** 2
12.0
"" "
self.m_yMatrix[0, 0] = -3./(x1 - x0) ** 2
self.m_yMatrix[0, 1] = 3. / (x1 - x0) ** 2
def pushLastEquationToYMatrix (self, xnm1, xn):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_yMatrix = np.matrix (np.zeros (shape = (5,5)))
>>> cubicSplineStruct.pushLastEquationToYMatrix (1.0, 1.5)
>>> print (cubicSplineStruct.m_yMatrix.)[-1, -1]) # 3./(xn - xnm1) ** 2
12.0
>>> print (cubicSplineStruct.m_yMatrix.)[-1, -2]) # -3./(xn - xnm1) ** 2
-12.0
"" "
self.m_yMatrix[-1, -1] = 3./(xn - xnm1) ** 2
self.m_yMatrix[-1, -2] = -3./(xn - xnm1) ** 2
def pushMiddleEquationToYMatrix (self, i, xim1, xi, xip1):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_yMatrix = np.matrix (np.zeros (shape = (4,5)))
>>> cubicSplineStruct.pushMiddleEquationToYMatrix (3, 1.0, 1.5, 1.75)
>>> print (cubicSplineStruct.m_yMatrix.)[3, 2]) # -3./(xi - xim1) ** 2
-12.0
>>> print (cubicSplineStruct.m_yMatrix.)[3, 3]) # 3./(xi - xim1) ** 2 - 3./(xip1 - xi) ** 2
-36.0
>>> print (cubicSplineStruct.m_yMatrix.)[3, 4]) # 3./(xip1 - xi) ** 2
48.0
"" "
self.m_yMatrix[i, i-1] = -3./(xi - xim1) ** 2
self.m_yMatrix[i, i] = 3./(xi - xim1) ** 2 - 3./(xip1 - xi) ** 2
self.m_yMatrix[i, i + 1] = 3./(xip1 - xi) ** 2
def computeYMatrix (Self):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 4
>>> cubicSplineStruct.m_xvalues = [1.0, 1.5, 1.75, 2.25]
>>> cubicSplineStruct.computeYMatrix ()
>>> print (cubicSplineStruct.m_yMatrix)
[[-12. 12. 0. 0.]
[-12. -36. 48. 0.]
[ 0. -48. 36. 12.]
[ 0. 0. -12. 12.]]"" "
self.m_yMatrix = np.matrix (np.zeros (shape = (self.m_n, self.m_n)))
self.pushFirstEquationToYMatrix (self.m_xvalues[0]self.m_xvalues[1])
for i in the range (1, self.m_n-1):
self.pushMiddleEquationToYMatrix (i, self.m_xvalues[i-1]self.m_xvalues[i]self.m_xvalues[i+1])
self.pushLastEquationToYMatrix (self.m_xvalues[-2]self.m_xvalues[-1])
def computeYtoKMatrix (self):
"" "
Should be called when x-node values are updated
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 4
>>> cubicSplineStruct.m_xvalues = [0.0, 10./3., 20./3., 10.]
>>> cubicSplineStruct.computeYtoKMatrix ()
>>> print (cubicSplineStruct.m_ytoKMatrix)
[[-0.38 0.48 -0.12 0.02]
[-0.14 -0.06 0.24 -0.04]
[ 0.04 -0.24 0.06 0.14]
[-0.02 0.12 -0.48 0.38]]"" "
self.computeKMatrix ()
self.computeYMatrix ()
self.m_ytoKMatrix = self.m_kMatrix.I * self.m_yMatrix
def computeKCoeffs (self):
"" "
Should be called when y node values are updated
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_n = 2
>>> cubicSplineStruct.m_ytoKMatrix = np.mat (& # 39; 1 2; 4 5 & # 39;)
>>> cubicSplineStruct.m_yvalues =[3., 4.]
>>> cubicSplineStruct.computeKCoeffs ()
>>> print (cubicSplineStruct.m_kCoeffs)
[11.0, 32.0]
"" "
kCoeffs = np.array (self.m_yvalues) * self.m_ytoKMatrix.T
self.m_kCoeffs = [kCoeffs[0, i] within reach of i (self.m_n)]def interpolateOnInterval (self, IntervalIndex, x):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_xvalues = [None, 10., 42.]
>>> cubicSplineStruct.m_yvalues = [None, 128., -64.]
>>> cubicSplineStruct.m_kCoeffs = [None, 2., 6.]
>>> print (cubicSplineStruct.interpolateOnInterval (1, 10.))
128.0
>>> print (cubicSplineStruct.interpolateOnInterval (1, 18.))
98.0
>>> print (cubicSplineStruct.interpolateOnInterval (1, 34.))
-58.0
>>> print (cubicSplineStruct.interpolateOnInterval (1, 42.))
-64.0
"" "
x1 = self.m_xvalues[intervalIndex]
x2 = self.m_xvalues[intervalIndex+1]
y1 = self.m_yvalues[intervalIndex]
y2 = self.m_yvalues[intervalIndex+1]
t = (x - x1) / (x2 - x1)
a = computeACoeff (x1, x2, y1, y2, self.m_kCoeffs)[intervalIndex])
b = computeBCoeff (x1, x2, y1, y2, self.m_kCoeffs)[intervalIndex+1])
Return (1-t) * y1 + t * y2 + t * (1-t) * (a * (1-t) + b * t)
interpolate def (self, x):
"" "
>>> cubicSplineStruct = CubicSplineStruct ()
>>> cubicSplineStruct.m_xvalues = [None, 10., 42.]
>>> cubicSplineStruct.m_yvalues = [None, 128., -64.]
>>> cubicSplineStruct.m_kCoeffs = [None, 2., 6.]
>>> cubicSplineStruct.interpolate (18.)
98.0
"" "
if len (self.m_xvalues) == 0:
Return 0
IntervalLowerBound = FindLowerBound (self.m_xvalues, x)
Return value self.interpolateOnInterval (IntervalLowerBound, x)
def computeACoeff (x1, x2, y1, y2, k):
"" "
>>> print (computeACoeff (10., 42., 128., -64., 2.))
256.0
"" "
Return k * (x2 - x1) - (y2 -y1)
def computeBCoeff (x1, x2, y1, y2, k):
"" "
>>> print (computeBCoeff (10., 42., 128., -64., 6.))
-384.0
"" "
Return -k * (x2 - x1) + (y2 -y1)
def findLowerBound (xvalues, x):
"" "
>>> findLowerBound ([10., 30.]9.)
-1
>>> findLowerBound ([10., 30.]10.)
0
>>> findLowerBound ([10., 30.], fifteen.)
0
>>> findLowerBound ([10., 30.], 30.)
0
>>> findLowerBound ([10., 30.], 31.)
1
>>> findLowerBound ([10., 30., 40.]9.)
-1
>>> findLowerBound ([10., 30., 40.]10.)
0
>>> findLowerBound ([10., 30., 40.], fifteen.)
0
>>> findLowerBound ([10., 30., 40.], 30.)
0
>>> findLowerBound ([10., 30., 40.]40.)
1
>>> findLowerBound ([10., 30., 40.], 41.)
2
"" "
if xvalues[-1] == x:
return len (xvalues) - 2
left = bisect_left (xvalues, x)
if left> = len (xvalues):
return len (xvalues) - 1
if (xvalues[left]== x):
Return 0 if left == 0 otherwise left - 1
back to the left - 1
```