Python科学计算(第2版)
上QQ阅读APP看书,第一时间看更新

3.7.2 多维插值

使用interp2d( )可以进行二维插值运算。它的调用形式如下:

    interp2d(x, y, z, kind='linear', ...)

其中x、y、z都是一维数组,如果传入的是多维数组,则先将其转为一维数组。kind参数指定插值运算的阶数,可以为'linear'、'cubic'、'quintic'。

下面的例子对某个函数曲面上的网格点进行二维插值,效果如图3-43所示。其中下左图显示插值之前的数据,而下右图显示插值运算后得到的结果。

图3-43 使用interp2d类进行二维插值

    def func(x, y): ❶
        return (x+y)*
np.exp(-5.0*
(x**
2 + y**
2))
    
    # X-Y轴分为15*
15的网格
    y, x = np.mgrid[-1:1:15j, -1:1:15j] ❷
    fvals = func(x, y) # 计算每个网格点上的函数值
    
    # 二维插值
    newfunc = interpolate.interp2d(x, y, fvals, kind='cubic') ❸
    
    # 计算100*
100的网格上的插值
    xnew = np.linspace(-1,1,100)
    ynew = np.linspace(-1,1,100)
    fnew = newfunc(xnew, ynew) ❹

❶func是计算曲面上各点高度的函数。❷计算X、Y轴在-1到1范围之内,大小为15×15的等间距网格上各点的高度。注意所得到的二维数组fvals的第0轴与Y轴对应,第一轴与X轴对应。❸使用网格上各点的X、Y和Z轴的坐标创建interp2d对象,这里我们使用二阶插值曲面。❹interp2d对象可以像函数一样调用,我们用它计算插值曲面在一个更密网格中的高度值。注意这里的参数是两个一维数组,分别指定网格的X-Y轴坐标,而不需要通过mgrid创建网格坐标数组。

1.griddata

interp2d类只能对网格形状的取样值进行插值运算,如果需要对随机散列的取样点进行插值,则可以使用griddata( )。其调用形式如下:

    griddata(points, values, xi, method='linear', fill_value=nan)

其中points表示K维空间中的坐标,它可以是形状为(N, k)的数组,也可以是一个有k个数组的序列,N为数据的点数。values是points中每个点对应的值。xi是需要进行插值运算的坐标,其形状为(M, k)。method有三个选项——'nearest'、'linear'、'cubic',分别对应0阶、1阶以及3阶插值。

下面是griddata( )的演示程序,其输出如图3-44所示。下左图与'nearest'算法对应,平面上每个点都被填充为与它最近的采样点的数据,因此图中由许多相同颜色的色块构成。'linear'和'cubic'算法只对采样点构成的凸包区域进行插值,区域之外采用fill_value进行填充。中图和下右图中的白色区域为插值的凸包区域之外。

图3-44 使用gridata进行二维插值

griddata( )使用欧几里得距离计算插值。如果K维空间中每个维度的取值范围相差较大,则应先将数据正规化,然后使用griddata( )进行插值运算。

    # 计算随机N个点的坐标,以及这些点对应的函数值
    N = 200
    np.random.seed(42)
    x = np.random.uniform(-1, 1, N)
    y = np.random.uniform(-1, 1, N)
    z = func(x, y)
    
    yg, xg = np.mgrid[-1:1:100j, -1:1:100j]
    xi = np.c_[xg.ravel( ), yg.ravel( )]
    
    methods = 'nearest', 'linear', 'cubic'
    
    zgs = [interpolate.griddata((x, y), z, xi, method=method).reshape(100, 100) 
        for method in methods]

2.径向基函数插值

径向基函数(radial basis function)插值算法也可以用于高维随机散布点的插值。所谓径向基函数,是指函数值只与某特定点的距离相关的一类函数,其中xi是某个给定取样点的坐标。使用这些ϕ函数,可以近似表示N维空间中的函数:

为了方便读者理解RBF,下面先看一个一维插值的例子,结果如图3-45所示。图中显示了三种ϕ函数对应的插值曲线:multiquadric、gaussian和linear。

图3-45 一维RBF插值

    from scipy.interpolate import Rbf
    
    x1 = np.array([-1, 0, 2.0, 1.0])
    y1 = np.array([1.0, 0.3, -0.5, 0.8])
    
    funcs = ['multiquadric', 'gaussian', 'linear']
    nx = np.linspace(-3, 4, 100)
    rbfs = [Rbf(x1, y1, function=fname) for fname in funcs] ❶
    rbf_ys = [rbf(nx) for rbf in rbfs] ❷

❶使用表示取样点的x和y数组创建一个rbf对象,并通过function参数指定所使用的径向基函数。❷rbf对象可以像函数一样被调用,我们用它计算以nx为横坐标对应的插值曲线的值。

rbf对象的nodes属性保存wi系数:

    for fname, rbf in zip(funcs, rbfs):
        print fname, rbf.nodes
    multiquadric [ -3.79570791   9.82703701   5.08190777 -11.13103777]
    gaussian [ 1.78016841 -1.83986382 -1.69565607  2.5266374 ]
    linear [-0.26666667  0.6         0.73333333 -0.9       ]

下面的程序演示二维径向基函数插值,效果如图3-46所示。

图3-46 二维径向基函数插值

    rbfs = [Rbf(x, y, z, function=fname) for fname in funcs]
    rbf_zg = [rbf(xg, yg).reshape(xg.shape) for rbf in rbfs]

某些径向基函数可以通过epsilon参数指定其作用范围,该值越大每个插值点的作用范围越广,所得到的曲面也就越平滑。下面的代码演示gaussian径向基函数的epsilon参数与插值结果的关系,效果如图3-47所示。

    epsilons = 0.1, 0.15, 0.3
    rbfs = [Rbf(x, y, z, function="gaussian", epsilon=eps) for eps in epsilons]
    zgs = [rbf(xg, yg).reshape(xg.shape) for rbf in rbfs]