python 插值求包络线

您所在的位置:网站首页 包络线的概念 python 插值求包络线

python 插值求包络线

2023-08-18 11:08| 来源: 网络整理| 查看: 265

在这里插入图片描述 SciPy 是 Python 里处理科学计算 (scientific computing) 的包,使用它遇到问题可访问它的官网 (https://www.scipy.org/). 去找答案。 在使用 scipy 之前,需要引进它,语法如下:

import scipy

这样你就可以用 scipy 里面所有的内置方法 (build-in methods) 了,比如插值、积分和优化。

numpy.interpolatenumpy.integratenumpy.optimize

但是每次写 scipy 字数有点多,通常我们给 scipy 起个别名 sp,用以下语法,这样所有出现 scipy 的地方都可以用 sp 替代。

import scipy as sp

SciPy 是建立 NumPy 基础上的,很多关于线性代数的矩阵运算在 NumPy 都能做,因此就不重复在这里讲了

插值

给定一组 (xi, yi),其中 i = 1, 2, …, n,而且 xi 是有序的,称为「标准点」。插值就是对于任何新点 xnew,计算出对应的 ynew。换句话来说,插值就是在标准点之间建立分段函数 (piecewise function),把它们连起来。这样给定任意连续 x 值,带入函数就能计算出任意连续 y 值。 在 SciPy 中有个专门的函数 scipy.interpolate 是用来插值的,首先引进它并记为 spi。

import scipy.interpolate as spi 简单例子

用 scipy.interpolate 来插值函数 sin(x) + 0.5x。

基本概念

首先定义 x 和函数 f(x):

x = np.linspace(-2 * np.pi, 2 * np.pi, 11) f = lambda x: np.sin(x) + 0.5 * x f(x) array([-3.14159265, -1.56221761, -1.29717034, -1.84442231, -1.57937505, 0. , 1.57937505, 1.84442231, 1.29717034, 1.56221761, 3.14159265])

接下来介绍 scipy.interpolate 里面两大杀器:splrep 和 splev。两个函数名称都是以 spl 开头,全称 spline (样条),可以理解这两个函数都和样条有关。不同的是,两个函数名称以 rep 和 ev 结尾,它们的意思分别是:

rep:representation 的缩写,那么 splrep 其实生成的是一个「表示样条的对象」ev:evaluation 的缩写,那么 splev 其实用于「在样条上估值」 splrep 和 splev 像是组合拳 (one two punch)前者将 x, y 和插值方式转换成「样条对象」tck后者利用它在 xnew 上生成 ynew

一图胜千言: 在这里插入图片描述 接下来仔细分析一下 tck。

tck = spi.splrep( x, f(x), k=1 ) tck

在这里插入图片描述 tck 就是样条对象,以元组形式返回,tck 这名字看起来很奇怪,实际指的是元组 (t, c, k) 里的三元素:

t - vector of knots (节点)c - spline cofficients (系数)k - degree of spline (阶数) 对照上图,tck 确实一个元组,包含两个 array 和一个标量 1,其中 第一个 array 是节点,即标准点,注意到一开始 x 只有 11 个,但现在是 13 个,首尾都往外补了一个和首尾一样的 x第二个 array 是系数,注意它就是 y 在尾部补了两个 0标量 1 是阶数,因为在调用 splrep 时就把 k 设成 1 注:前两个 array 我只是发现这个规律,但解释不清楚为什么这样。它和 matlab 里面的 spline() 的产出不太一样,希望懂的读者可以留言区解释一下。 虽然解释不清楚前两个 array,那就把 tck 当成是个黑匣子 (black-box) 直接用了。比如可用 PPoly.from_spline 来查看每个分段函数的系数。 pp = spi.PPoly.from_spline(tck) pp.c.T array([[ 1.25682673, -3.14159265], [ 1.25682673, -3.14159265], [ 0.21091791, -1.56221761], [-0.43548928, -1.29717034], [ 0.21091791, -1.84442231], [ 1.25682673, -1.57937505], [ 1.25682673, 0. ], [ 0.21091791, 1.57937505], [-0.43548928, 1.84442231], [ 0.21091791, 1.29717034], [ 1.25682673, 1.56221761], [ 1.25682673, 3.14159265]])

tck 的系数数组里有 13 个元素,而上面数组大小是 (12, 2),12 表示 12 段,2 表示每段线性函数由 2 个系数确定。 把 x 和 tck 丢进 splev 函数,我们可以插出在 x 点对应的值 iy。

iy = spi.splev( x, tck ) iy

用 Matplotlib 来可视化插值的 iy 和原函数 f(x) 发现 iy 都在 f(x) 上。Matplotlib 是之后的课题,现在读者可忽略其细节。 在这里插入图片描述 在这里插入图片描述 除了可视化,我们还可以用具体的数值结果来评估插值的效果:

np.allclose(f(x), iy) np.sum((f(x) - iy) ** 2) / len(x) True 0.0

第一行 allclose 的结果都是 True 证明插值和原函数值完全吻合,第二行就是均方误差 (mean square error, MSE),0.0 也说明同样结果。 上面其实做的是在「标准点 x」上插值,那得到的结果当然等于「标准点 y」了。这种插值确实意义不大,但举这个例子只想让大家 4. 明晰 splrep 和 splev 是怎么运作的 5. 如何可视化插出来的值和原函数的值 6. 如何用 allclose 来衡量插值和原函数值之间的差异

正规例子

上面在「标准点 x」上插值有点作弊,现在我们在 50 个「非标准点 xd」上线性插值得到 iyd。

xd = np.linspace( 1.0, 3.0, 50 ) iyd = spi.splev( xd, tck ) print( xd, '\n\n', iyd )

在这里插入图片描述 密密麻麻的数字啥都看不出来,可视化一下把。 在这里插入图片描述 在这里插入图片描述 这插得是个什么鬼?红色插值点在第二段和深青色原函数差别也太远了吧 (MSE 也显示有差异)。

np.sum((f(xd) - iyd) ** 2) / len(xd) 0.011206417290260647

问题出在哪儿呢?当「标准点 x」不密集时 (只有 11 个),分段线性函数来拟合 sin(x) + 0.5x 函数当然不会太好啦。那我们试试分段三次样条函数 (k = 3)。

tck = spi.splrep( x, f(x), k=3 )iyd = spi.splev( xd, tck )

可视化一下并计算 MSE 看看 在这里插入图片描述

np.sum((f(xd) - iyd) ** 2) / len(xd) 1.6073247177220542e-05

视觉效果好多了!误差小多了!

定义信号

import numpy as np t = np.arange(0, 0.3, 1/20000.0) x = np.cos(2*np.pi*1000*t) * (np.cos(2*np.pi*20*t) + np.cos(2*np.pi*8*t) + 3.0)

可视化 在这里插入图片描述 用极大值极小值方法计算信号的包络

import scipy as sp import scipy.interpolate as spi peak_id_up, peak_property_up = signal.find_peaks(x) print(peak_id) print(x[peak_id]) print(peak_property) peak_id_down, peak_property_down = signal.find_peaks(-x) tck_up = spi.splrep(n[peak_id_up], x[peak_id_up],k=3) iy_up = spi.splev(n,tck_up) tck_down = spi.splrep(n[peak_id_down],x[peak_id_down], k=3) iy_down = spi.splev(n,tck_down) plt.plot(x ,'k') plt.plot(nt[peak_id_up], x[peak_id_up], 'ro') plt.plot(nt[peak_id_down],x[peak_id_down],'go') plt.plot(nt, iy_up,'r-',label = 'up') plt.plot(nt,iy_down,'g-',label ='down') plt.legend() # plt.plot(y_new)

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3