作者:欧新宇(Xinyu OU)
当前版本:Release v1.0
开发平台:Python3.11
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce GTX 1080 Ti
本教案为非完整版教案,请结合课程PPT使用。
最后更新:2024年3月19日
已知未知函数y=(fx)的6个观测点|的值如表7.1所示,试求插值函数,并求处函数的估计值。
# Lec0301: 例7.1 待定系数法插值
import numpy as np
import pylab as plt
# 1. 计算多项式的系数
x0 = np.arange(1|7)
y0 = np.array([16|18|21|17|15|12])
A = np.vander(x0) # 计算方程范德蒙德行列式(系数矩阵)
# coef = np.linalg.inv(A) @ y0 # 求插值多项式的系数,inv(A)矩阵的逆,@矩阵乘法
coef = np.linalg.solve(A|y0) # solve(A|y0)直接使用线性方程的求解器求解
print('方程系数:'|np.round(coef, 4)) # 输出系数矩阵
# 2. 根据插值函数计算给定值的估计值
xh = [1.5, 2.6]
yh = np.polyval(coef, xh) # 计算给定值x的函数值
plt.plot(xh, yh, 'ok') # 根据插值数据点和函数值绘制插值数据点
print('预测值为:', np.round(yh, 4))
# 3. 根据给定的观察数据绘制插值函数
plt.plot(x0, y0, 'o') # 画出已知数据点的离散值
xt = np.linspace(1, 6, 1000) # 在插值空间中生成100个插值数据点
yt = np.polyval(coef, xt) # 使用多项式计算插值数据点的函数值
plt.plot(xt, yt) # 根据插值数据点和函数值绘制插值曲线
plt.show()
方程系数: [ -0.2417 4.3333 -28.9583 87.6667 -115.8 69. ]
预测值为: [14.918 20.8846]
# Lec0302: 例7.2 拉格朗日插值
import numpy as np
from scipy.interpolate import lagrange
import pylab as plt
# 1. 计算多项式的系数
x0 = np.arange(1, 7)
y0 = np.array([16, 18, 21, 17, 15, 12])
coef = lagrange(x0, y0) # 求拉格朗日插值多项式的系数
print('方程系数:', np.round(coef, 4)) # 输出系数矩阵
# 2. 根据插值函数计算给定值的估计值
xh = [1.5, 2.6]
yh = np.polyval(coef, xh) # 计算给定值x的函数值
plt.plot(xh, yh, 'ok') # 根据插值数据点和函数值绘制插值数据点
print('预测值为:', np.round(yh, 4))
# 3. 根据给定的观察数据绘制插值函数
plt.plot(x0, y0, 'o') # 画出已知数据点的离散值
xt = np.linspace(1, 6, 100) # 在插值空间中生成100个插值数据点
yt = np.polyval(coef, xt) # 使用多项式计算插值数据点的函数值
plt.plot(xt, yt) # 根据插值数据点和函数值绘制插值曲线
plt.show()
方程系数: [ -0.2417 4.3333 -28.9583 87.6667 -115.8 69. ]
预测值为: [14.918 20.8846]
牛顿插值并不直接用于计算方程系数,它主要用于通过给定的数据点来估计函数在任意点的值。然而,如果你想要使用牛顿插值多项式来近似一个函数,并获取这个多项式的系数,你可以从差商表中提取这些系数。但注意的是这里求出来的系数并不是通用的多项式系数,而是针对给定值 的系数。
在python的scipy.interpolate库中没有牛顿插值方法,这里我们按照牛顿法的基本思想自行实现。从结果看,其预测的值和拟合的函数与待定系数法和拉格朗日插值法是相同的。
# Lec0303: 例7.2-扩展 牛顿插值
import numpy as np
import pylab as plt
# 0. 定义牛顿插值函数
def divided_diff(x, y):
"""计算差商表"""
n = len(x)
coefs = np.zeros((n, n))
coefs[:, 0] = y
for j in range(1, n):
for i in range(n - j):
coefs[i][j] = (coefs[i+1][j-1] - coefs[i][j-1]) / (x[i+j] - x[i])
return coefs
def newton_interpolation(x, y, xi):
"""牛顿插值"""
coefs = divided_diff(x, y)
n = len(x)
result = 0
for i in range(n):
term = coefs[0][i]
for j in range(1, i+1):
term *= (xi - x[j-1])
result += term
return result
# 1. 设置已知数据点
x0 = np.arange(1, 7)
y0 = np.array([16, 18, 21, 17, 15, 12])
# 2. 根据插值函数计算给定值的估计值
xh = [1.5, 2.6]
yh = newton_interpolation(x0, y0, xh) # 计算给定值x的函数值
plt.plot(xh, yh, 'ok') # 根据插值数据点和函数值绘制插值数据点
print('预测值为:', np.round(yh, 4))
# 3. 根据给定的观察数据绘制插值函数
plt.plot(x0, y0, 'o') # 画出已知数据点的离散值
xt = np.linspace(1, 6, 100) # 在插值空间中生成100个插值数据点
yt = newton_interpolation(x0, y0, xt) # 使用多项式计算插值数据点的函数值
plt.plot(xt, yt) # 根据插值数据点和函数值绘制插值曲线
plt.show()
预测值为: [14.918 20.8846]
在区间 [-5, 5] 上,用 n+1 个等距节点作多项式 ,使得它在节点处的值与函数 在对应节点处的值相等,考察 时,多项式的次数与逼近误差的关系。
# Lec0304: 7.3-续例7.2 龙格振荡
import numpy as np
import pylab as plt
from scipy.interpolate import lagrange
f = lambda x: 1/(1+x**2) # 定义插值函数
def fun(n): # 定义拉格朗日插值函数生成系数
x = np.linspace(-5, 5, n+1)
coef = lagrange(x, f(x)) # n次插值多项式
return coef
x0 = np.linspace(-5, 5, 100) # 初始化给定结点x
plt.rc('font', size=12);
N = [6, 8, 10]
s = ['--*b', '-.', '-p']
for k in range(len(N)): # 绘制出等距结点分别为[6,8,10]的三条曲线
coef = fun(N[k])
y0 = np.polyval(coef, x0)
plt.plot(x0, y0, s[k])
plt.plot(x0, f(x0));
plt.legend(['n=6', 'n=8', 'n=10', '1/(1+x^2)'])
plt.show()
下面我们将分别给出分段线性插值、二次插值、最近邻插值以及三次样条插值的代码,以供读者比较。
# Lec0305: 分段插值
import numpy as np
from scipy.interpolate import interp1d
import pylab as plt
# 1. 计算多项式的系数
x0 = np.arange(1, 7)
y0 = np.array([16, 18, 21, 17, 15, 12])
# 2. 根据插值函数计算给定值的估计值
kind = ['linear', 'quadratic', 'nearest', 'cubic'] # 定义插值类型
xt = np.linspace(1, 6, 100) # 在插值空间中生成100个插值数据点
plt.figure(figsize=(12,7))
for i in range(len(kind)):
f = interp1d(x0, y0, kind=kind[i]) # 生成插值函数
plt.subplot(2,2,i+1)
plt.plot(xt, f(xt))
plt.plot(x0, y0, 'o') # 画出已知数据点的离散值
plt.title(kind[i])
plt.show()
待定系数法:
拉格朗日插值:
牛顿插值:
分段线性插值:
三次样条插值:
二次插值:
最近邻插值:
# Lec0306:例7.4 机床加工
import numpy as np
from scipy.interpolate import interp1d, lagrange
import pylab as plt
plt.rc('font', family='SimHei') #用来正常显示中文标签
plt.rc('axes', unicode_minus=False) #用来正常显示负号
# 1. 读取数据,并生成区间为 [0,15],间隔为0.1的待插值数据
a = np.loadtxt('../Data/Lec03/data7_4.txt')
x0 = a[0]
y0 = a[1]
x = np.linspace(0, 15, 151)
# 2. 使用已知数据构造分段线性插值函数、拉格朗日插值函数和三次样条插值函数
f1 = interp1d(x0, y0, 'linear') # 2.1.1 构造分段线性插值
y1 = f1(x) # 2.1.2 根据函数f1,计算插值点的函数值
coef = lagrange(x0, y0) # 2.2.1 计算Lagange插值函数的多项式系数
y2 = np.polyval(coef, x) # 2.2.2 根据函数f2,计算插值点的函数值
f3 = interp1d(x0, y0, 'cubic') # 2.3.1 构造三次样条插值函数
y3 = f3(x) # 2.3.2 根据函数f3,计算插值点的函数值
fun_name = ['分段线性插值', '拉格朗日插值', '三次样条插值']
# 3. 绘制三种插值函数的曲线图
plt.figure(figsize=(12, 4))
plt.subplots_adjust(wspace = 0.5) #调整子图水平间距
plt.subplot(131); plt.plot(x, y1)
plt.title(fun_name[0])
plt.subplot(132); plt.plot(x, y2)
plt.title(fun_name[1])
plt.subplot(133); plt.plot(x, y3)
plt.title(fun_name[2])
plt.show()
# 4. 计算 x=0 处的曲线斜率
dx = np.diff(x)
fun = ['y1', 'y2', 'y3']
for i in range(len(fun)):
dy = eval('np.diff('+ fun[i]+ ')')
dyx = dy / dx
dyx0 = dyx[0]
xt = x[130:]
yt = eval(fun[i]+'[130:]')
ymin = min(yt)
xmin = [xt[ind] for ind, v in enumerate(yt) if v==ymin]
print('x=0 处的斜率({}): {:.2f}'.format(fun_name[i], dyx0))
print('y 的极值点(x, y): ({:.2f}, {:.2f})'.format(xmin[0], ymin))
x=0 处的斜率(分段线性插值): 0.40
y 的极值点(x, y): (14.00, 1.00)
x=0 处的斜率(拉格朗日插值): -49.44
y 的极值点(x, y): (13.70, 0.94)
x=0 处的斜率(三次样条插值): 0.50
y 的极值点(x, y): (13.80, 0.98)
可以看出,拉格朗日插值的结果根本不能应用,分段线性插值的光滑性较差(特别是在x=14附近弯曲处),建议选用三次样条插值的结果。
已知速度曲线上的4个数据点如表7.3所示。用三次样条函数进行插值,求位移。
表7.3 速度的4个观测值
0.15 | 0.16 | 0.17 | 0.18 |
---|---|---|---|
3.5 | 1.5 | 2.5 | 2.8 |
# Lec0307:例7.5 使用速度曲线求位移
from scipy.interpolate import UnivariateSpline, interp1d
import numpy as np
t0 = np.array([0.15, 0.16, 0.17, 0.18])
v0 = np.array([3.5, 1.5, 2.5, 2.8])
f1 = UnivariateSpline(t0, v0, k=3) # 使用平滑曲线函数(B样条插值)拟合插值函数,k=3即三次样条
# print(f1.get_coeffs()) # 获取B-spline曲线的系数
res1 = f1.integral(0.15, 0.18) # 求位移,即速度的积分
print("B-spline曲线的积分值: ", res1)
f2 = interp1d(t0, v0, 'cubic') # 定义三次样条插值函数
xt = np.linspace(0.15, 0.18, 200)
yt = f2(xt) # 计算给定值的函数值
res2 = np.trapz(yt, xt) # 根据函数值计算定积分
print("cubic-spline曲线的积分值: ", res2)
B-spline曲线的积分值: 0.06862499999999995
cubic-spline曲线的积分值: 0.06862565339259107
已知欧洲一个国家的地图,为了算出它的国土面积和边界长度,首先对地图作如下测量:以由西向东方向为 x 轴,由南向北方向为 y 轴,选择方便的原点,并将从最西边界点到最东边界点在 x 轴上的区间适当地分为若干段,在每个分点的 y 方向测出南边界点 y 和北边界点的坐标 和 ,这样就得到了表7.4的数据(单位:mm)。
表7.4 某国国土地图边界测量值(单位:mm)
7.0 | 10.5 | 13.0 | 17.5 | 34.0 | 40.5 | 44.5 | 48.0 | 56.0 | |
44 | 45 | 47 | 50 | 50 | 38 | 30 | 30 | 34 | |
44 | 59 | 70 | 72 | 93 | 100 | 110 | 110 | 110 | |
61.0 | 68.5 | 76.5 | 80.5 | 91.0 | 96.0 | 101.0 | 104.0 | 106.5 | |
36 | 34 | 41 | 45 | 46 | 43 | 37 | 33 | 28 | |
117 | 118 | 116 | 118 | 118 | 121 | 124 | 121 | 121 | |
111.5 | 118.0 | 123.5 | 136.5 | 142.0 | 146.0 | 150.0 | 157.0 | 158.0 | |
32 | 65 | 55 | 54 | 52 | 50 | 66 | 66 | 68 | |
121 | 122 | 116 | 83 | 81 | 82 | 86 | 85 | 68 |
根据地图的比例我们知道18mm相当于40km,试由测量数据计算该国国土的近似面积和边界的近似长度,并与国土面积的精确值 比较。
# Lec0308:例7.6 使用插值计算周长和面积
from scipy.interpolate import UnivariateSpline
import pylab as plt
import numpy as np
# 1. 数据准备:根据已有数据绘制出边界结点图,从数据表不难发现大体分为上半区和下半区
a = np.loadtxt('../Data/Lec03/data7_6.txt')
x0 = a[::3].flatten() # 提出点的横坐标,从头开始,步长为3
y1 = a[1::3].flatten() # 提出下边界的纵坐标,从第1行开始,步长为3
y2 = a[2::3].flatten() # 提出上边界的纵坐标,从第2行开始,步长为3
plt.plot(x0, y1,'*-')
plt.plot(x0, y2,'.-')
plt.show()
# 2. 计算周长:分别对上、下半区的曲线进行积分并求和
f1 = UnivariateSpline(x0, y1, k=3) #计算三次样条函数
f2 = UnivariateSpline(x0, y2, k=3)
d1 = f1.derivative(1) # 求样条函数的导数
d2 = f2.derivative(1)
x = np.linspace(x0[0], x0[-1], 1000)
L = np.trapz(np.sqrt(1+d1(x)**2)+np.sqrt(1+d2(x)**2),x) # 使用积分计算周长
L = L/18*40 # 单位换算
print('周长 L = ',round(L,4))
# 3. 面积的计算
# (1) 将不规则区域看作是上、下边界围起来的曲边四边形。
# (2) 然后使用(上边界y值-下边界y值)对x做积分即可求得面积
S = np.trapz(f2(x)-f1(x), x) # 使用积分计算面积
S = S/(18**2)*(40**2) # 单位换算
print('面积 S = ', round(S,4))
delta = (S-41288)/41288 # 计算差值
print('相对误差 = {:.2f}%'.format(delta*100))
周长 L = 1151.2715
面积 S = 41714.8096
相对误差 = 1.03%
# Lec0309:例7.6 使用观测值直接计算周长和面积
import pylab as plt
import numpy as np
# 1. 数据准备:根据已有数据绘制出边界结点图,从数据表不难发现大体分为上半区和下半区
a = np.loadtxt('../Data/Lec03/data7_6.txt')
x0 = a[::3].flatten() # 提出点的横坐标,从头开始,步长为3
y1 = a[1::3].flatten() # 提出下边界的纵坐标,从第1行开始,步长为3
y2 = a[2::3].flatten() # 提出上边界的纵坐标,从第2行开始,步长为3
# 2. 计算周长:分别对上、下半区的曲线进行积分并求和
L = np.trapz(np.sqrt(1+np.gradient(y1, x0)**2) + np.sqrt(1+np.gradient(y2, x0)**2), x0) # 使用积分计算周长
L = L/18*40 # 单位换算
print('周长 L = ',round(L,4))
# 3. 面积的计算
S = np.trapz(y2-y1, x0) # 使用积分计算面积
S = S/(18**2)*(40**2) # 单位换算
print('面积 S = ', round(S,4))
delta = (S-41288)/41288 # 计算差值
print('相对误差 = {:.2f}%'.format(delta*100))
周长 L = 1107.3141
面积 S = 42413.5802
相对误差 = 2.73%
【结果分析】 不难发现,直接进行计算比使用三次样条插值得到的面积准确率更高,说明在本题所给的样本中,三次样条可能存在局部过拟合,从而使得结果的准确率有所降低。有兴趣的同学可以尝试使用二次插值,其相对误差只有 。
已知平面区域 ,的高程数据见表7.5(单位:m) data7_7.txt
。求该区域地表面积的近似值,并用插值数据画出该区域的等高线图和三维表面图。
# Lec0310:例7.7 计算三维区域的面积
import pylab as plt
import numpy as np
from numpy.linalg import norm
from scipy.interpolate import interp2d, RegularGridInterpolator
# 1. 载入观察数据,并创建插值函数
# 1.1 载入观察数据
z = np.loadtxt("../Data/Lec03/data7_7.txt").T # 加载高程数据
x = np.arange(0, 1500, 100) # 创建高程数据的x坐标
y = np.arange(1200, -100, -100) # 创建高程数据的y坐标
# 1.2 将网格划分成140×120的小区间
xn = np.linspace(0, 1400, 141)
yn = np.linspace(0, 1200, 121)
# 1.3 使用双线性插值对给定位置进行插值
interpolator = RegularGridInterpolator((x, y), z, method='linear', bounds_error=False, fill_value=None)
xn_grid, yn_grid = np.meshgrid(xn, yn, indexing='ij') # 构造网格数据
zn = interpolator((xn_grid, yn_grid)).T # 通过插值算法计算高程
# 2. 计算网格的面积
m = len(xn)
n = len(yn)
S = 0
for i in np.arange(m-1):
for j in np.arange(n-1):
# 获取每个网格四个角落的坐标和高程
p1 = np.array([xn[i], yn[j], zn[j,i]]) # 左上
p2 = np.array([xn[i+1], yn[j], zn[j,i+1]]) # 右上
p3 = np.array([xn[i+1], yn[j+1], zn[j+1,i+1]]) # 右下
p4 = np.array([xn[i], yn[j+1], zn[j+1,i]]) # 左下
# 构造并计算两个右上和左下两个小三角形区域
p12 = norm(p1-p2); p23 = norm(p3-p2); p13 = norm(p3-p1)
p14 = norm(p4-p1); p34 = norm(p4-p3);
# 使用海伦公式计算两个小三角区域的面积
L1 = (p12+p23+p13)/2
s1 = np.sqrt(L1*(L1-p12)*(L1-p23)*(L1-p13))
L2 = (p13+p14+p34)/2
s2 = np.sqrt(L2*(L2-p13)*(L2-p14)*(L2-p34))
# 将小三角区域的面积合并成网格内的面积,并逐网格叠加
S = S + s1 + s2;
# 3. 输出计算结果并进行可视化
print("区域的面积为:", S)
plt.figure(figsize=(12, 4))
plt.subplot(121);
contr = plt.contour(xn, yn, zn) # 创建等高线
plt.clabel(contr) # 为等高线添加标签
plt.xlabel('x')
plt.ylabel('y', rotation=0)
ax = plt.subplot(122, projection='3d');
ax.plot_surface(xn_grid, yn_grid, zn.T, cmap='viridis') # 在三维空间中绘制表面图形
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
区域的面积为: 4264891.35391983
在一丘陵地带测量高程,x和y方向每隔100米测一个点,得高程如表7.6,试插值一曲面,确定合适的模型,并由此找出最高点和该点的高程。
y\x | 100 | 200 | 300 | 400 | 500 |
---|---|---|---|---|---|
100 | 636 | 697 | 624 | 478 | 450 |
200 | 698 | 712 | 630 | 800 | 420 |
300 | 680 | 674 | 598 | 412 | 400 |
400 | 662 | 626 | 552 | 334 | 310 |
# Lec0311:例7.8 计算丘陵区域的最高点和高程
import numpy as np
from scipy.interpolate import RegularGridInterpolator
# 1. 载入观察数据,并创建双三次样条插值函数
z0 = np.loadtxt("../Data/Lec03/data7_8.txt").T # 加载高程数据
x0 = np.linspace(100, 500, 5) # 创建高程数据的x坐标
y0 = np.linspace(100, 400, 4) # 创建高程数据的y坐标
interpolator = RegularGridInterpolator((x0, y0), z0, method='cubic', bounds_error=False, fill_value=None)
# 2. 使用插值函数对给定位置进行插值
xn = np.linspace(100, 500, 41)
yn = np.linspace(100, 400, 31)
xn_grid, yn_grid = np.meshgrid(xn, yn, indexing='ij') # 构造网格数据
zn = interpolator((xn_grid, yn_grid)).T # 通过插值算法计算高程
# 3. 计算最大高程及该高程的坐标位置
zmax = zn.max() # 求矩阵元素的最大值
iy, ix = np.where(zn == zmax) # 查找极值的坐标位置
print('最高点的坐标 (x, y): ({}, {})'.format(xn[ix][0], yn[iy][0]))
print('最大高程为: ', round(zmax, 4))
最高点的坐标 (x, y): (410.0, 180.0)
最大高程为: 831.0493
在某海域测得一些点 处的水深由表7.7给出,画出海底区域的地形和等高线图。
表7.7 海底水深数据
129 | 140 | 103.5 | 88 | 185.5 | 195 | 105 | 157.5 | 107.5 | 77 | 81 | 162 | 162 | 117.5 | |
7.5 | 141.5 | 23 | 147 | 22.5 | 137.5 | 85.5 | -6.5 | -81 | 3 | 56.5 | -66.5 | 84 | -33.5 | |
4 | 8 | 6 | 8 | 6 | 8 | 8 | 9 | 9 | 8 | 8 | 9 | 4 | 9 |
# Lec0312:例7.9 海域地形
import pylab as plt
import numpy as np
from scipy.interpolate import griddata
import matplotlib as mpl
mpl.rcParams.update(mpl.rcParamsDefault)
# 1. 载入观察数据,并创建双三次样条插值函数
a = np.loadtxt('../Data/Lec03/data7_9.txt') # 加载高程数据
x = a[0]
y = a[1]
z = -a[2]
xy = np.vstack([x,y]).T
# 2. 使用三次样条作为基本插值,并用最近邻插值替换nan值
xn = np.linspace(x.min(), x.max(), 100) # 插值点x坐标
yn = np.linspace(y.min(), y.max(), 200) # 插值点y坐标
xn_grid, yn_grid = np.meshgrid(xn, yn) # 构造网格节点
zn1 = griddata(xy, z, (xn_grid, yn_grid), method='cubic') # 三次样条插值
zn2 = griddata(xy, z, (xn_grid, yn_grid), method='nearest') # 最近邻插值
zn1[np.isnan(zn1)] = zn2[np.isnan(zn1)] # 使用最近邻插值将三次样条中的nan值替换掉
# 3. 可视化海底区域地形和等高线图
plt.figure(figsize=(12, 4))
plt.rc('font', size=12)
ax = plt.subplot(121)
contr = plt.contour(xn, yn, zn1*1000) # 创建等高线
plt.clabel(contr) # 为等高线添加标签
ax = plt.subplot(122, projection='3d')
ax.plot_surface(xn_grid, yn_grid, zn1,cmap='viridis') # 在三维空间中绘制表面图形
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
为了测量刀具的磨损速度,我们做这样的实验:经过一定时间(如每隔一小时),测量一次刀具的厚度,得到一组实验数据 如表7.8所示。试根据实验数据建立 与 之间的经验公式 。
表7.8 实验数据观测值
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
27.0 | 26.8 | 26.5 | 26.3 | 26.1 | 25.7 | 25.3 | 24.8 |
# Lec0313:例7.10 刀具磨损速度研究
import numpy as np
import sympy as sp
# 1. 数据准备
t = np.arange(8)
y = np.array([27.0, 26.8, 26.5, 26.3, 26.1, 25.7, 25.3, 24.8])
# 2. 生成拟合函数(求解拟合函数系数)
#### 2.1 方法一:标准数学求解(先化简,再代入)
tb = t.mean()
yb = y.mean()
a1 = sum((t - tb) * (y - yb))/sum((t - tb) ** 2)
b1 = yb - a1 * tb
print('方法一:[{:.6f}, {:.6f}]'.format(a1, b1)) # 输出第一种方法的解
#### 2.1 方法二:标准数学求解(数值法)
# 在实际应用中,当处理线性回归问题时(例如使用最小二乘法来求解拟合函数的参数),
# 通常直接进行数值代入法进行求解,先对函数进行简化(符号求解)再进行数值代入,对于大型数据集通常不是最有效率的。
# 下面我们将直接使用数值代入进行求解
# 2.1.1 定义变量
a, b = sp.symbols('a b')
# 2.1.2 构造最小二乘代价函数:delta函数
delta = sum((a * t_i + b - y_i) ** 2 for t_i, y_i in zip(t, y))
# 2.1.3 构造方程组(令其=0),计算变量a和b的偏导数
delta_a_prime = sp.diff(delta, a)
delta_b_prime = sp.diff(delta, b)
eq1 = sp.Eq(delta_a_prime, 0) # 方程1
eq2 = sp.Eq(delta_b_prime, 0) # 方程2
# 1.3 使用solve解方程组
coef2 = sp.solve((eq1, eq2), (a, b))
print('方法二:', coef2) # 输出第二种方法的解
#### 2.2 方法三:使用逆矩阵方法求解方程组Ax=y的系数
A = np.vstack([t, np.ones(len(t))]).T # 构造方程组,第1列为a的系数(t),第2列为b的系数(=1)
coef3 = np.linalg.pinv(A) @ y # 基于SVD奇异值的方法构造伪逆矩阵求解系数
print('方法三:', coef3) # 输出第三种方法的解
#### 2.3 方法四:直接使用Python自带的拟合函数(默认为线性最小二乘)进行求解
coef4 = np.polyfit(t, y, 1) # 输出第四种方法的解
print('方法四:', coef4) # 输出第四种方法的解
方法一:[-0.303571, 27.125000]
方法二: {a: -0.303571428571429, b: 27.1250000000000}
方法三: [-0.30357143 27.125 ]
方法四: [-0.30357143 27.125 ]
# Lec0314:例7.11 行星运行轨道
import numpy as np
import pylab as plt
# 1. 数据准备
# x0 = np.array([5.764, 6.286, 6.759, 7.168, 7.408])
# y0 = np.array([0.648, 1.202, 1.823, 2.526, 3.36 ])
a = np.loadtxt('../Data/Lec03/data7_11.txt');
x0 = a[0]
y0 = a[1]
# 2. 构造方程组
A = np.vstack([x0**2, x0*y0, y0**2, x0, y0]).T # 构建系数A
b = -np.ones(5) # 构建系数b
coef = np.linalg.pinv(A)@b # 拟合方程的系数
print('拟合的系数为:', coef)
# 3. 可视化椭圆方程
f = lambda x, y: coef[0]*x**2 + coef[1]*x*y + coef[2]*y**2 + coef[3]*x + coef[4]*y
x = np.linspace(3, 8, 100)
y = np.linspace(-1, 5, 100)
x, y = np.meshgrid(x,y)
z = f(x, y)
plt.contour(x, y, z, [-1]) # 画高度为-1的等高线
plt.plot(x0, y0, 'o') # 绘制初始数据点
plt.xlabel('x')
plt.ylabel('y', rotation=0)
plt.show()
拟合的系数为: [ 0.05076623 -0.07021888 0.03812646 -0.45309109 0.26425697]
已知 的观测值见表7.10。用给定数据拟合函数 ,且满足 。
# Lec0315: 例7.12 约束线性最小二乘的拟合
import numpy as np
import cvxpy as cp
# 1. 数据准备
# x0 = np.array([3, 5, 6, 7, 4, 8, 5, 9])
# y0 = np.array([4, 9, 5, 3, 8, 5, 8, 5])
a = np.loadtxt('../Data/Lec03/data7_12.txt');
x0 = a[0]
y0 = a[1]
# 2. 创建约束线性方程组并进行求解
t = cp.Variable(2, pos=True) # 拟合的参数:t[0]=a, t[1]=b
c = np.vstack([np.exp(x0), np.log(x0)]).T # 定义约束函数的系数,第一项为a的系数,第二项为b的系数
obj = cp.Minimize(cp.sum_squares(c@t - y0)) # 定义目标函数
con = [sum(t)<=1] # 定义约束
prob = cp.Problem(obj, con) # 定义问题
prob.solve(solver='CVXOPT') # 解方程
print("最优解为:\n", t.value)
最优解为:
[4.77938762e-04 9.99522183e-01]
在研究某单分子化学反应速度时,得到数据 如表7.11所示。其中 表示从实验开始算起的时间, 表示时刻 反应物的量。试根据上述数据定出经验公式 ,其中 是待定常数。
表7.11反应物的观测值数据
3 | 6 | 9 | 2 | 15 | 18 | 21 | 24 | |
---|---|---|---|---|---|---|---|---|
57.6 | 41.9 | 31.0 | 22.7 | 16.6 | 12.2 | 8.9 | 6.5 |
# Lec0316: 例7.13 单分子化学反应速度
import numpy as np
# 1. 数据准备
t0 = np.array([3, 6, 9, 12, 15, 18, 21, 24])
y0 = np.array([57.6, 41.9, 31.0, 22.7, 16.6, 12.2, 8.9, 6.5])
y = np.log(y0) # 使用log(y) 表示y,以确保多项式转换为线性方程
# 2. 使用ployfit库拟合多项式系数
coef = np.polyfit(t0, y, deg=1) # 拟合1次多项式
print("多项式的系数 [m,b] = ", coef)
print('多项式的系数 k = ', np.exp(coef[1]))
多项式的系数 [m,b] = [-0.10368584 4.36399041]
多项式的系数 k = 78.57003612766904
用curve_fit函数拟合例7.13中的参数 ,并求 时, 的预测值。
# Lec0317: 例7.13 单分子化学反应速度(非线性拟合)
import numpy as np
from scipy.optimize import curve_fit
# 1. 数据准备
t0 = np.array([3, 6, 9, 12, 15, 18, 21, 24])
y0 = np.array([57.6, 41.9, 31.0, 22.7, 16.6, 12.2, 8.9, 6.5])
# 2. 拟合目标函数
y = lambda t, k, m: k*np.exp(m*t) # 定义匿名函数,未知数[t, k, m],函数值y
coef, cov = curve_fit(y, t0, y0)
print('拟合的参数为:', np.round(coef, 4))
# 3. 对特定点进行预测
th = np.array([5, 8])
yh = y(th, *coef) # *coef表示可变长参数,这里代表 [k, m]
print('预测值为:', np.round(yh, 4))
拟合的参数为: [78.45 -0.1036]
预测值为: [46.745 34.2626]
用表7.12的数据拟合函数 。
图7.12 的观测值
6 | 2 | 6 | 7 | 4 | 2 | 5 | 9 | |
---|---|---|---|---|---|---|---|---|
4 | 9 | 5 | 3 | 8 | 5 | 8 | 2 | |
5 | 2 | 1 | 9 | 7 | 4 | 3 | 3 |
# Lec0318: 例7.15 拟合函数
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
x_data = np.array([[6, 2, 6, 7, 4, 2, 5, 9],
[4, 9, 5, 3, 8, 5, 8, 2]])
y_data = np.array([5, 2, 1, 9, 7, 4, 3, 3])
z = lambda x_data, a, b, c: a*np.exp(b*x_data[0]) + c*x_data[1]**2
coef, pcov = curve_fit(z, x_data, y_data)
print('a, b, c的拟合值为: ', np.round(coef, 4))
a, b, c的拟合值为: [ 5.0891e+00 -2.6000e-03 -2.1500e-02]
利用模拟数据拟合曲面 ,并画出拟合曲面的图形,要求分别使用函数 curve_fit
, least_squares
和 leastsq
进行拟合,并做简单对比。
# Lec0319: 例7.16 拟合曲面数据
import numpy as np
from scipy.optimize import curve_fit, least_squares, leastsq
import pylab as plt
# 1. 数据预处理
np.random.seed(1) # 为确保生成的伪随机序列相同,手动设置随机数种子
m, n = (200, 300) # 定义插值分辨率
x = np.linspace(-6, 6, m)
y = np.linspace(-8, 8, n)
x2, y2 = np.meshgrid(x, y)
x3 = x2.flatten()
y3 = y2.flatten()
xy = np.vstack([x3, y3])
# 2. 拟合函数
zxy = lambda t, m1, m2, var: np.exp(-((t[0]-m1)**2+(t[1]-m2)**2)/(2*var**2))
z = zxy(xy, 1, 4, 6) # 无噪声函数值
zr = z+0.2*np.random.normal(size=z.shape) # 噪声数据
coef = curve_fit(zxy, xy, zr)[0] # 拟合参数
print("coef1:", coef)
zn= zxy(xy, *p1) # 计算拟合函数的值
zn2 = np.reshape(zn, x2.shape)
ax = plt.axes(projection='3d') #创建一个三维坐标轴对象
ax.plot_surface(x2, y2, zn2,cmap='gist_rainbow')
coef0 = np.random.randn(3) #拟合参数的初值
fun = lambda t, x, y: np.exp(-((x-t[0])**2 +(y-t[1])**2)/(2*t[2]**2))
err = lambda t, x, y, z: fun(t,x,y)- z #定义误差向量
coef2 = least_squares(err, coef0, args=(x3, y3, zr))
print('coef2:', coef2)
coef3 = leastsq(err, coef0, args=(x3,y3,zr))
print('coef3:', coef3)
plt.show()
coef1: [0.9962754 4.00604227 6.01024171]
coef2: message: `ftol` termination condition is satisfied.
success: True
status: 2
fun: [-3.243e-01 1.229e-01 ... -9.811e-03 1.269e-01]
x: [ 9.963e-01 4.006e+00 6.010e+00]
cost: 1203.3215859180655
jac: [[-1.338e-02 -2.295e-02 6.143e-02]
[-1.342e-02 -2.322e-02 6.187e-02]
...
[ 7.824e-02 6.322e-02 1.064e-01]
[ 7.854e-02 6.269e-02 1.071e-01]]
grad: [-2.463e-07 5.357e-08 -9.862e-07]
optimality: 9.862281413752783e-07
active_mask: [ 0.000e+00 0.000e+00 0.000e+00]
nfev: 8
njev: 8
coef3: (array([0.9962754 , 4.00604227, 6.01024171]), 1)