|
import numpy as np |
|
from scipy.linalg import lstsq |
|
import matplotlib.pyplot as plt |
|
|
|
def generateData(n = 30): |
|
# similar to peaks() function in MATLAB |
|
g = np.linspace(-3.0, 3.0, n) |
|
X, Y = np.meshgrid(g, g) |
|
X, Y = X.reshape(-1,1), Y.reshape(-1,1) |
|
Z = 3 * (1 - X)**2 * np.exp(- X**2 - (Y+1)**2) \ |
|
- 10 * (X/5 - X**3 - Y**5) * np.exp(- X**2 - Y**2) \ |
|
- 1/3 * np.exp(- (X+1)**2 - Y**2) |
|
return X, Y, Z |
|
|
|
def exp2model(e): |
|
# C[i] * X^n * Y^m |
|
return ' + '.join([ |
|
f'C[{i}]' + |
|
('*' if x>0 or y>0 else '') + |
|
(f'X^{x}' if x>1 else 'X' if x==1 else '') + |
|
('*' if x>0 and y>0 else '') + |
|
(f'Y^{y}' if y>1 else 'Y' if y==1 else '') |
|
for i,(x,y) in enumerate(e) |
|
]) |
|
|
|
# generate some random 3-dim points |
|
X, Y, Z = generateData() |
|
|
|
# 1=linear, 2=quadratic, 3=cubic, ..., nth degree |
|
order = 11 |
|
|
|
# calculate exponents of design matrix |
|
#e = [(x,y) for x in range(0,order+1) for y in range(0,order-x+1)] |
|
e = [(x,y) for n in range(0,order+1) for y in range(0,n+1) for x in range(0,n+1) if x+y==n] |
|
eX = np.asarray([[x] for x,_ in e]).T |
|
eY = np.asarray([[y] for _,y in e]).T |
|
|
|
# best-fit polynomial surface |
|
A = (X ** eX) * (Y ** eY) |
|
C,resid,_,_ = lstsq(A, Z) # coefficients |
|
|
|
# calculate R-squared from residual error |
|
r2 = 1 - resid[0] / (Z.size * Z.var()) |
|
|
|
# print summary |
|
print(f'data = {Z.size}x3') |
|
print(f'model = {exp2model(e)}') |
|
print(f'coefficients =\n{C}') |
|
print(f'R2 = {r2}') |
|
|
|
# uniform grid covering the domain of the data |
|
XX,YY = np.meshgrid(np.linspace(X.min(), X.max(), 20), np.linspace(Y.min(), Y.max(), 20)) |
|
|
|
# evaluate model on grid |
|
A = (XX.reshape(-1,1) ** eX) * (YY.reshape(-1,1) ** eY) |
|
ZZ = np.dot(A, C).reshape(XX.shape) |
|
|
|
# plot points and fitted surface |
|
ax = plt.figure().add_subplot(projection='3d') |
|
ax.scatter(X, Y, Z, c='r', s=2) |
|
ax.plot_surface(XX, YY, ZZ, rstride=1, cstride=1, alpha=0.2, linewidth=0.5, edgecolor='b') |
|
ax.axis('tight') |
|
ax.view_init(azim=-60.0, elev=30.0) |
|
ax.set_xlabel('X') |
|
ax.set_ylabel('Y') |
|
ax.set_zlabel('Z') |
|
plt.show() |
Here is a cubic version