【Python数据插值】

简介: 【Python数据插值】

2.png6.png5.png5.png1. 数据插值

插值是一种从离散数据点构建函数的数学方法。插值函数或者插值方法应该与给定的数据点完全一致。插值可能的应用场景:


根据给定的数据集绘制平滑的曲线

对计算量很大的复杂函数进行近似求值

插值和前面介绍过的最小二乘拟合有些类似。在最小二乘拟合中,我们感兴趣的是使用数据点和超定方程组,将函数拟合到数组点,使得误差平方和最小。在插值中,我们需要一个方程能够与已有的数据点完全重合,仅使用与插值函数自由参数个数相同的数据点。因此,最小二乘法适合将大量数据点拟合到模型函数,插值是根据少量数据点创建函数。


外插(extrapolation)是与插值(interpolation)相关的一个概念。外插是在采样范围之外计算函数的估计值。我们这里只介绍插值。


2. 导入模块

本部分我们将使用NumPy中的polynomial模块和SciPy的interpolation模块。

import numpy as np
from scipy import linalg
import matplotlib.pyplot as plt
from numpy import polynomial as P
from scipy import interpolate
%reload_ext version_information
%version_information numpy, matplotlib, scipy


3. 插值函数

为了简洁起见,我们这里只考虑一维插值问题。对于给定的数据点的集合 {(xi,yi)}i=1n,找到插值函数f(xi)=yi插值函数的选择并不是唯一的,事实上有无数函数满足插值标准。通常可以把插值函数写为一组基函数ϕ ( x )的线性组合,即f(x)=j=1ncjϕj(x)其中c j是未知系数。将给定的数据点代入线性组合,可以得到未知系数的线性方程组:

3.0.png

这里基函数的数量与数据点的数量想用,可以使用求解方程组的标准方法得到系数向c cc的唯一解。如果基函数的数量小于数据点的数量,该方程组是超定的,可能需要使用最小二乘拟合进行近似插值。


3.1 多项式

NumPy库包含的polynominal模块提供了处理多项式的函数和类。要创建Polynominal类的实例,可以将系数数组传给该类的构造函数。


p1 = P.Polynomial([1,2,3])
p1

3.1.1.png

也可以使用Polynomial.fromroots方法,通过指定多项式的根来初始化多项式。

p2 = P.Polynomial.fromroots([-1, 1])
p2


我们可以通过实例的方法访问其特性:


p1.roots() # 根

3.1.3.png

p1.coef # 系数

3.1.4.png


Polynomial实例包括domain和windows两个参数,可以用于把多项式的输入域映射到另外一个区间。

p1.domain

3.1.5.png


p1.window

3.1.6.png


多项式在用Polynomial实例表示后可以很容易计算任何x xx的多项式值。

p1(np.array([1.5, 2.5, 3.5]))

3.1.7.png


Polynomial实例支持标准的算术运算符。

p1 + p2

3.1.8.png


分解因式

p3 = P.Polynomial([1,1])  # 等价于 P.Polynomial.fromroots(-1)
p2 // p3

3.1.9.png

P.Polynomial.fromroots(-1)

3.1.1.png

除了标准幂基多项式的Polynomial类,polynomial模块还提供了切比雪夫多项式、勒让德多项式、拉盖尔多项式、埃尔米特多项式的类。

l1 = P.Legendre([1,2,3])  # 勒让德多项式
l1

3.1.2.png

l1.roots()

3.1.3.png

3.2 多项式插值

求解多项式插值问题,首先需要根据提供的基函数计算矩阵ϕ ( x ) \phi(x)ϕ(x),然后求解得到的线性方程组。polynomial模块中的每个多项式类都提供了方便的函数来计算相应基的范德蒙矩阵。


例如,假设存在数据点(1, 1)、(2, 3)、(3, 5)、(4, 4)。

x = np.array([1, 2, 3, 4])
y = np.array([1, 3, 5, 4])
deg = len(x) - 1
A = P.polynomial.polyvander(x, deg)  # 范德蒙矩阵
A

3.2..1.png

c = linalg.solve(A, y)
c

3.2.2.png

找到的插值多项式是f ( x ) =23.5x+3x20.5x3



f1 = P.Polynomial(c)
f1(2.5)

3.2.3.png

下面,我们使用切比雪夫多项式重新对数据进行插值。

A = P.chebyshev.chebvander(x, deg)
A

3.2.4.png

c = linalg.solve(A, y)
c

3.2.5.png

f2 = P.Chebyshev(c)
f2(2.5)

3.2.6.png

将函数f 1 和f 2进行对比,验证不同基函数进行插值,得到了一致的插值函数。

xx = np.linspace(x.min(), x.max(), 100)
fig, ax = plt.subplots(1, 1, figsize=(8, 4))
ax.plot(xx, f1(xx), 'b', lw=2, label='Power basis interp.')
ax.plot(xx, f2(xx), 'r--', lw=2, label='Chebyshev basis interp.')
ax.scatter(x, y, label='data points')
ax.legend(loc=4)
ax.set_xticks(x)
ax.set_ylabel(r"$y$", fontsize=18)
ax.set_xlabel(r"$x$", fontsize=18)

3.2.7.png

polynomial模块还提供了更快捷的方法计算插值多项式。每个多项式类都有fit方法用于计算插值,deg参数用于控制多项式的次数。如果多项式的次数少于数据点的数目减1,fit方法会使用最小二乘拟合而不是精确插值。

f1b = P.Polynomial.fit(x, y, deg)
f1b

3.2.8.png

f2b = P.Chebyshev.fit(x, y, deg)
f2b

3.2.9.png

注意在使用fit方法时,实例中的domain属性会自动设置为数据点中相应的x值,上述示例中为[1, 4],并相应调整系数。将插值数据映射到某个基函数最合适的范围,可以明显提高插值的数值稳定性,减小范德蒙矩阵的条件数。

np.linalg.cond(P.chebyshev.chebvander(x, deg))

1.png

np.linalg.cond(P.chebyshev.chebvander((2*x-5)/3.0, deg))

2.png

当数据点的数量增加时,需要使用次数更多的多项式才能得到精确的插值。这会带来多方面问题:


次数更多的多项式插值计算和插值函数的确定,计算量很大。

高次多项式插值可能会在插值点之间带来不可预料的行为,插值函数在数据点之间可能变化很大。

下面我们将使用龙格函数演示这种行为:

def runge(x):
    return 1/(1 + 25 * x**2)
def runge_interpolate(n):
    x = np.linspace(-1, 1, n+1)
    p = P.Polynomial.fit(x, runge(x), deg=n)
    return x, p
xx = np.linspace(-1, 1, 250)
fig, ax = plt.subplots(1, 1, figsize=(8, 4))
ax.plot(xx, runge(xx), 'k', lw=2, label="Runge's function")
n = 13
x, p = runge_interpolate(n)
ax.plot(x, runge(x), 'ro')
ax.plot(xx, p(xx), 'r', label='interp. order %d' % n)
n = 14
x, p = runge_interpolate(n)
ax.plot(x, runge(x), 'go')
ax.plot(xx, p(xx), 'g', label='interp. order %d' % n)
ax.legend(loc=8)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1, 2)
ax.set_xticks([-1, -0.5, 0, 0.5, 1])
ax.set_ylabel(r"$y$", fontsize=18)
ax.set_xlabel(r"$x$", fontsize=18)

3.png

可以看到,高阶插值函数在远端采样点之间急剧震荡。这种不良特性违背了插值的初衷。一个可行的解决方法是,当面对大量数据点时,使用分段低次多项式进行插值。

3.3 样条插值

对于包含n nn个数据点的集合,在整个数据区间上有n − 1 n-1n−1个子区间。连接两个子区间的数据点在分段多项式插值中被称为节点。在每个子区间上使用k kk次多项式对n nn个数据点进行插值,需要确定( k + 1 ) ( n − 1 ) (k+1)(n-1)(k+1)(n−1)个参数。所有节点的值可以给出2 ( n − 1 ) 2(n-1)2(n−1)个方程。为了保证分段多项式的平滑,节点处导数和高阶导数的连续性也会给出相应方程。


样条是一种特殊类型的分段式插值函数。最常用的是三次样条,k = 3 k=3k=3,需要4 ( n − 1 ) 4(n-1)4(n−1)个参数。在n − 2 n-2n−2个节点处,一阶和二阶导数的连续性可以给出2 ( n − 1 ) 2(n-1)2(n−1)个方程,总方程的数目为4 ( n − 1 ) − 2 4(n-1)-24(n−1)−2。此时还剩下两个未确定的参数。一种常见的方法是要求端点处的二阶导数为0,这被称为自然样条。


SciPy的interpolate模块提供了用于样条插值的多个函数和类。下面我们将再次使用龙格函数,演示使用interpolate.interp1d函数。这里设置kind=3来计算三次样条。

x = np.linspace(-1, 1, 11)
y = runge(x)
f = interpolate.interp1d(x, y, kind=3)
f(0.05)

4.png

xx = np.linspace(-1, 1, 100)
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(xx, runge(xx), 'k', lw=1, label="Runge's function")
ax.plot(x, y, 'ro', label='sample points')
ax.plot(xx, f(xx), 'r--', lw=2, label='spline order 3')
ax.legend()
ax.set_ylim(0, 1.1)
ax.set_xticks([-1, -0.5, 0, 0.5, 1])
ax.set_ylabel(r"$y$", fontsize=18)
ax.set_xlabel(r"$x$", fontsize=18)

5.png


这里使用了11个数据点和三次样条,可以看到插值很好地与原始函数吻合。

为了说明阶数对样条插值的影响,我们下面准备了8个数据点使用不同阶数的样条进行插值。

x = np.array([0, 1, 2, 3, 4, 5, 6, 7])
y = np.array([3, 4, 3.5, 2, 1, 1.5, 1.25, 0.9])
xx = np.linspace(x.min(), x.max(), 100)
fig, ax = plt.subplots(figsize=(8, 4))
ax.scatter(x, y)
for n in [1, 2, 3, 5]:
    f = interpolate.interp1d(x, y, kind=n)
    ax.plot(xx, f(xx), label='order %d' % n)
ax.legend()
ax.set_ylabel(r"$y$", fontsize=18)
ax.set_xlabel(r"$x$", fontsize=18)


6.png

可以看到,二阶或三阶样条已经能够提供较好的插值。高阶样条会出现高阶多项式插值中类似的数值震荡问题。


3.4 多变量插值

多项式插值和样条插值都可以直接推广到多变量情况。SciPy为多变量插值提供了多个函数和类。我们将介绍两个最常用的双变量插值函数:interpolate.interp2d和interpolate.griddata。


3.4.1 均匀网格

interpolate.interp2d函数是interpolate.interp1d函数的推广,要求数据点位于x和y坐标组成的规则且均匀的网格中。


为了演示函数用法,我们在已知函数

1.png

中添加随机噪声来模拟测量噪声。为了构造插值问题,我们在x和y坐标的[-2, 2]区间上采样10个点。

def f(x, y):
    return np.exp(-(x + .5)**2 - 2*(y + .5)**2) - np.exp(-(x - .5)**2 - 2*(y - .5)**2)
x = y = np.linspace(-2, 2, 10)
X, Y = np.meshgrid(x, y)
# simulate noisy data at fixed grid points X, Y
Z = f(X, Y) + 0.05  * np.random.randn(*X.shape)
f_interp = interpolate.interp2d(x, y, Z, kind='cubic')  # 三次样条插值
xx = yy = np.linspace(x.min(), x.max(), 100)
ZZ_interp = f_interp(xx, yy)
XX, YY = np.meshgrid(xx, yy)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
c = axes[0].contourf(XX, YY, f(XX, YY), 15, cmap=plt.cm.RdBu)
axes[0].set_xlabel(r"$x$", fontsize=20)
axes[0].set_ylabel(r"$y$", fontsize=20)
axes[0].set_title("exact / high sampling")
cb = fig.colorbar(c, ax=axes[0])
cb.set_label(r"$z$", fontsize=20)
c = axes[1].contourf(XX, YY, ZZ_interp, 15, cmap=plt.cm.RdBu)
axes[1].set_ylim(-2.1, 2.1)
axes[1].set_xlim(-2.1, 2.1)
axes[1].set_xlabel(r"$x$", fontsize=20)
axes[1].set_ylabel(r"$y$", fontsize=20)
axes[1].scatter(X, Y, marker='x', color='k')
axes[1].set_title("interpolation of noisy data / low sampling")
cb = fig.colorbar(c, ax=axes[1])
cb.set_label(r"$z$", fontsize=20)


对于更高维的问题,可以使用interpolate.interpnd函数,它是对N维问题的推广。


3.4.2 不均匀网格

另一种多变量插值的常见场景是从不规则的坐标网络中采样数据,例如实验室数据等。为了能够使用现有的工具对数据进行可视化分析,可能需要将它们插值到规则的坐标网格中。


在SciPy中,可以使用interpolate.griddata函数来完成这项工作。该函数的第一个参数是坐标向量构成的元组,第二个参数是对应的数据点的值,第三个参数是待插值的新数据点的坐标数组或坐标矩阵。

我们考虑函数f(x,y)=exp(x2y2)在x和y坐标区间[-1, 1]上随机选择的采样点,并对采样数据进行插值。

def f(x, y):
    return np.exp(-x**2 - y**2) * np.cos(4*x) * np.sin(6*y)
x = y = np.linspace(-1, 1, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
np.random.seed(0)
N = 500
xdata = np.random.uniform(-1, 1, N)
ydata = np.random.uniform(-1, 1, N)
zdata = f(xdata, ydata)


我们画出函数 𝑓(𝑥,𝑦) 的等高线图,以及500个采样点的位置。




fig, ax = plt.subplots(figsize=(8, 6))
c = ax.contourf(X, Y, Z, 15, cmap=plt.cm.RdBu);
ax.scatter(xdata, ydata, marker='.')
ax.set_ylim(-1,1)
ax.set_xlim(-1,1)
ax.set_xlabel(r"$x$", fontsize=20)
ax.set_ylabel(r"$y$", fontsize=20)
cb = fig.colorbar(c, ax=ax)
cb.set_label(r"$z$", fontsize=20)

3.png

随后,我们使用X和Y坐标数组定义更细小间隔(超采样)的网格中对数据进行插值。我们将比较最近邻点插值(0阶插值)、线性插值(1阶插值)和三次样条插值在不用数据点数目下的效果区别。

def z_interpolate(xdata, ydata, zdata):
    Zi_0 = interpolate.griddata((xdata, ydata), zdata, (X, Y), method='nearest')
    Zi_1 = interpolate.griddata((xdata, ydata), zdata, (X, Y), method='linear')
    Zi_3 = interpolate.griddata((xdata, ydata), zdata, (X, Y), method='cubic')
    return Zi_0, Zi_1, Zi_3
fig, axes = plt.subplots(3, 3, figsize=(12, 12), sharex=True, sharey=True)
n_vec = [50, 150, 500]
for idx, n in enumerate(n_vec):
    Zi_0, Zi_1, Zi_3 = z_interpolate(xdata[:n], ydata[:n], zdata[:n])
    axes[idx, 0].contourf(X, Y, Zi_0, 15, cmap=plt.cm.RdBu)
    axes[idx, 0].set_ylabel("%d data points\ny" % n, fontsize=16)
    axes[idx, 0].set_title("nearest", fontsize=16)
    axes[idx, 1].contourf(X, Y, Zi_1, 15, cmap=plt.cm.RdBu)
    axes[idx, 1].set_title("linear", fontsize=16)
    axes[idx, 2].contourf(X, Y, Zi_3, 15, cmap=plt.cm.RdBu)
    axes[idx, 2].set_title("cubic", fontsize=16)
for m in range(len(n_vec)):
    axes[idx, m].set_xlabel("x", fontsize=16)

4.png


只要采样点能够有效覆盖我们感兴趣的区域,就可以通过非结构化的样本数据进行插值来重建函数。上面例子中,三次样条插值的效果要远优于最近邻点插值和线性插值。

目录
相关文章
|
6天前
|
存储 JSON JavaScript
【chat-gpt问答记录】python将数据存为json格式和yaml格式
【chat-gpt问答记录】python将数据存为json格式和yaml格式
22 1
|
7天前
|
存储 索引 Python
Python基础第五篇(Python数据容器)
Python基础第五篇(Python数据容器)
|
5天前
|
数据采集 Web App开发 数据挖掘
使用Python和BeautifulSoup轻松抓取表格数据
使用Python和BeautifulSoup,结合代理IP,可以从网页抓取表格数据,如中国气象局的天气信息。通过requests库发送HTTP请求,BeautifulSoup解析HTML提取表格。安装必要库后,设置代理IP,发送请求,解析HTML找到表格,提取数据并存储。通过Pandas进行数据分析,如计算平均气温。这种方法让数据抓取和分析变得更加便捷。
使用Python和BeautifulSoup轻松抓取表格数据
|
6天前
|
数据采集 Web App开发 数据处理
一步步教你用Python Selenium抓取动态网页任意行数据
使用Python Selenium爬取动态网页,结合代理IP提升抓取效率。安装Selenium,配置代理(如亿牛云),设置User-Agent和Cookies以模拟用户行为。示例代码展示如何使用XPath提取表格数据,处理异常,并通过隐式等待确保页面加载完成。代理、模拟浏览器行为和正确配置增强爬虫性能和成功率。
一步步教你用Python Selenium抓取动态网页任意行数据
|
1天前
|
存储 数据挖掘 Python
使用Python集合高效统计Excel数据
使用Python集合高效统计Excel数据
13 7
|
1天前
|
数据可视化 Python
Python中的数据可视化:在数据点上添加标签
Python中的数据可视化:在数据点上添加标签
14 3
|
6天前
|
Python
Python列表推导式是一种简洁的创建新列表的方式,它允许你在一行代码中完成对数据的操作和转换
【6月更文挑战第19天】Python列表推导式是创建新列表的简洁语法,它在一行内处理数据。表达式如`[expr for item in iterable if cond]`,其中`expr`是对元素的操作,`item`来自`iterable`,`if cond`是可选过滤条件。例如,将数字列表平方:`[x**2 for x in numbers]`。嵌套列表推导处理复杂结构,如合并二维数组:`[[a+b for a,b in zip(row1, row2)] for row1, row2 in zip(matrix1, matrix2)]`。简洁但勿过度复杂化。
13 5
|
6天前
|
存储 数据安全/隐私保护 计算机视觉
Python教程:一文了解从Bytes到Bits的数据转换
在Python编程中,处理数据时经常需要在字节(bytes)和位(bits)之间进行转换。这种转换在网络通信、数据加密、图像处理等领域尤为常见。本文将详细介绍如何在Python中进行字节与位之间的转换,并提供一个实用的功能:如何在指定的位位置替换位数据。
18 4
|
6天前
|
Python
Python+Jinja2实现接口数据批量生成工具
在做接口测试的时候,我们经常会遇到一种情况就是要对接口的参数进行各种可能的校验,手动修改很麻烦,尤其是那些接口参数有几十个甚至更多的,有没有一种方法可以批量的对指定参数做生成处理呢。
16 3
|
6天前
|
Python
【代码】Python实现Excel数据合并
【代码】Python实现Excel数据合并
11 0