python的scipy.interpolate模块有一维插值函数interp1d,二维插值函数interp2d,多维插值函数interpnd.
interp1d的基本调用格式为
from scipy.interpolate import interp1d
f=interp1d(x, y, kind='linear')
其中kind的取值是字符串,指明插值方法,kind的取值可以为:‘linear’, ‘nearest’, ‘zero’, ‘slinear’, ‘quadratic’, ‘cubic’等,这里的’zero’, ‘slinear’, ‘quadratic’ and 'cubic’分别指的是0阶、1阶、2阶和3阶样条插值.
例 在一天24小时内,从零点开始每间隔2小时测得的环境温度(摄氏度)如表所示。分别进行分段线性插值和三次样条插值,并画出插值曲线。
时间 | 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
温度 | 12 | 9 | 9 | 10 | 18 | 24 | 28 | 27 | 25 | 20 | 18 | 15 | 13 |
python求解的代码如下
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
x=np.arange(0,25,2)
y=np.array([12, 9, 9, 10, 18, 24, 28, 27, 25, 20, 18, 15, 13])
xnew=np.linspace(0, 24, 500) #插值点
f1=interp1d(x, y) #分段线性插值
y1=f1(xnew) #预测
f2=interp1d(x, y,'cubic') #三次样条插值
y2=f2(xnew)
plt.rc('font',size=16); plt.rc('font',family='SimHei') #绘图中文文字输出
plt.subplot(121); plt.plot(xnew, y1); plt.xlabel("分段线性插值")
plt.subplot(122); plt.plot(xnew, y2); plt.xlabel("三次样条插值")
plt.show()
例 已知平面区域 0 ⩽ x ⩽ 1400 0 \leqslant x \leqslant 1400 0⩽x⩽1400 , 0 ⩽ y ⩽ 1200 0 \leqslant y \leqslant 1200 0⩽y⩽1200 的高程数据见data1_1.txt(单位:m)。求该区域地表面积的近似值,并用插值数据画出该区域的等高线图和三维表面图。
分析: 原始数据给出的 100 × 100 100 \times 100 100×100 网格节点上的高程数据,为了提高计算精度,我们利用双三次样条插值,得到给定区域上 10 × 10 10 \times 10 10×10 网格节点上的高程。
利用分点 x i = 10 i ( i = 0 , 1 , ⋯ , 140 ) {x_i} = 10i(i = 0,1, \cdots ,140) xi=10i(i=0,1,⋯,140) 把 0 ⩽ x ⩽ 1400 0 \leqslant x \leqslant 1400 0⩽x⩽1400 剖分成140个小区间,利用分点 y j = 10 j {y_j} = 10j yj=10j ( j = 0 , 1 , ⋯ , 120 ) (j = 0,1, \cdots ,120) (j=0,1,⋯,120) 把 0 ⩽ y ⩽ 1200 0 \leqslant y \leqslant 1200 0⩽y⩽1200 剖分成120个小区间,把平面区域 0 ⩽ x ⩽ 1400 0 \leqslant x \leqslant 1400 0⩽x⩽1400 , 0 ⩽ y ⩽ 1200 0 \leqslant y \leqslant 1200 0⩽y⩽1200 剖分成 140 × 120 140 \times 120 140×120 个小矩形,对应地把所计算的三维曲面剖分成 140 × 120 140 \times 120 140×120 个小曲面进行计算,每个小曲面的面积用对应的三维空间中4个点所构成的两个小三角形面积的和作为近似值。
计算三角形面积时,使用海伦公式,即设 Δ A B C \Delta ABC ΔABC 的边长分别为 a , b , c a,b,c a,b,c , p = ( a + b + c ) / 2 p = (a + b + c)/2 p=(a+b+c)/2 ,则 Δ A B C \Delta ABC ΔABC 的面积 s = p ( p − a ) ( p − b ) ( p − c ) s = \sqrt {p(p - a)(p - b)(p - c)} s=p(p−a)(p−b)(p−c) 。
求解的python程序如下
import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import norm # np.linalg.norm 求范数
from scipy.interpolate import interp2d
z=np.loadtxt("data1_1.txt") #导入数据
x=np.arange(0,1500,100)
y=np.arange(1200,-100,-100) #网格节点数据
f=interp2d(x, y, z, 'cubic') #双三次样条插值
xn=np.linspace(0,1400,141)
yn=np.linspace(0,1200,121) #插值节点
zn=f(xn, yn) #预测
#分块计算面积
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]]) #网格四个顶点的 xyz 坐标向量
#分别计算 $\Delta p1p2p3$ 与 $\Delta p1p4p3$ 的面积
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)) # $\Delta p1p2p3$ 的面积
L2=(p13+p14+p34)/2; s2=np.sqrt(L2*(L2-p13)*(L2-p14)*(L2-p34)) # $\Delta p1p4p3$ 的面积
s=s+s1+s2; #求和
print("区域的面积为:", s)
plt.rc('font',size=16); plt.rc('text',usetex=True) # usetex=True
plt.subplot(121); contr=plt.contour(xn,yn,zn); plt.clabel(contr) #画等高线图
plt.xlabel('$x$'); plt.ylabel('$y$',rotation=90)
#画三维表面图
ax=plt.subplot(122,projection='3d');
X,Y=np.meshgrid(xn,yn) #构造网格节点
ax.plot_surface(X, Y, zn,cmap='viridis')
ax.set_xlabel('$x$'); ax.set_ylabel('$y$'); ax.set_zlabel('$z$')
plt.show()
利用Python求得的地表面积的近似值为 4.7827 × 1 0 6 4.7827 \times {10^6} 4.7827×106 m 2 m^2 m2
网格点插值可以使用 scipy.interpolate.griddata, 使用方式如下
import scipy
f=scipy.interpolate.griddata(points, values, xi, method='linear', fill_value=nan, rescale=False)
#参数:
# points: 数据点坐标: (数据量为 n , 维数为 D )
# 具有形状 (n, D) 的浮点数的二维 ndarray,或具有形状 (n, ) 的长度 D 元组的一维 ndarray
# values: 数据值: 浮点数或复数的ndarray,形状 (n, )
# xi: 插入数据的点:
# 具有形状 (m, D) 的二维 ndarray 或长度为 D 元组可广播到相同形状的 ndarray
# method: 插值方法: 可选 {‘linear’, ‘nearest’, ‘cubic’} 之一
# ‘linear’: 分段线性, ‘nearest’: 最近邻点, ‘cubic’: 三次样条
# fill_value: 浮点数,可选,
# 用于填充输入点凸包之外的请求点的值。如果未提供,则默认值为 NaN , 此选项对‘nearest’ 方法无效
# rescale: 布尔型,可选
# 在执行插值之前将点重新缩放到单位立方体 (如果某些输入维度具有不可比较的单位并且相差许多数量级时可以使用)
#返回: ndarray 插值数组
例 在某海域测得一些点( x , y x,y x,y )处的水深 z z z 由表给出,画出海底区域的地形和等高线图。
x x x | 129 | 140 | 103.5 | 88 | 185.5 | 195 | 105 | 157.5 | 107.5 | 77 | 81 | 162 | 162 | 117.5 |
y y y | 7.5 | 141.5 | 23 | 147 | 22.5 | 137.5 | 85.5 | -6.5 | -81 | 3 | 56.5 | -66.5 | 84 | -33.5 |
z z z | 4 | 8 | 6 | 8 | 6 | 8 | 8 | 9 | 9 | 8 | 8 | 9 | 4 | 9 |
求解的 python 程序如下
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
x=np.array([129,140,103.5,88,185.5,195,105,157.5,107.5,77,81,162,162,117.5])
y=np.array([7.5,141.5,23,147,22.5,137.5,85.5,-6.5,-81,3,56.5,-66.5,84,-33.5])
z=-np.array([4,8,6,8,6,8,8,9,9,8,8,9,4,9])
xy=np.vstack([x,y]).T
xn=np.linspace(x.min(), x.max(), 100)
yn=np.linspace(y.min(), y.max(), 100) #生成插值点
xng, yng = np.meshgrid(xn,yn) #构造网格节点
zn=griddata(xy, z, (xng, yng), method='nearest') #最近邻点插值
plt.rc('font',size=16); plt.rc('text',usetex=True)
ax=plt.subplot(121,projection='3d')
ax.plot_surface(xng, yng, zn,cmap='viridis') #画地形图
ax.set_xlabel('$x$'); ax.set_ylabel('$y$'); ax.set_zlabel('$z$')
plt.subplot(122); c=plt.contour(xn,yn,zn,8); plt.clabel(c) #画等高线图
plt.show()