我正在尝试为绘图目的插入一些数据。例如,给定N个数据点,我希望能够生成一个“平滑”图,由10 * N左右的内插数据点组成。
我的方法是生成N×10 * N矩阵并计算原始向量和我生成的矩阵的内积,得到1乘10 * N向量。我已经计算出我想用于插值的数学运算,但我的代码非常慢。我对Python很陌生,所以我希望这里的一些专家可以给我一些关于如何加速我的代码的想法。
我认为部分问题是生成矩阵需要对以下函数进行10 * N ^ 2次调用:
def sinc(x):
import math
try:
return math.sin(math.pi * x) / (math.pi * x)
except ZeroDivisionError:
return 1.0
(这comes from sampling theory。基本上,我试图从其样本中重新创建一个信号,并将其上采样到更高的频率。)
矩阵由以下内容生成:
def resampleMatrix(Tso, Tsf, o, f):
from numpy import array as npar
retval = []
for i in range(f):
retval.append([sinc((Tsf*i - Tso*j)/Tso) for j in range(o)])
return npar(retval)
我正在考虑将任务分解成更小的部分,因为我不喜欢N ^ 2矩阵坐在内存中的想法。我可能会将'resampleMatrix'变成一个生成器函数并逐行执行内部产品,但我不认为这会加速我的代码,直到我开始在内存中分页内容。
提前感谢您的建议!
答案 0 :(得分:9)
这是上采样。有关示例解决方案,请参阅Help with resampling/upsampling。
执行此操作的快速方法(对于离线数据,如绘图应用程序)是使用FFT。这就是SciPy的原生resample()
function所做的。然而,它假设一个周期性信号so it's not exactly the same。见this reference:
这是关于时域实信号插值的第二个问题,确实很重要。只有当原始x(n)序列在其全时间间隔内是周期性时,这种精确的插值算法才能提供正确的结果。
您的函数假设信号的样本在定义的范围之外都是0,因此这两种方法将偏离中心点。如果先用大量的零填充信号,它将产生非常接近的结果。在这里没有显示的情节边缘还有几个零:
对于重采样目的,立方插值不正确。这个例子是一个极端情况(接近采样频率),但正如你所看到的,三次插值甚至不是很接近。对于较低的频率,它应该非常准确。
答案 1 :(得分:3)
如果要以非常通用且快速的方式插入数据,样条线或多项式非常有用。 Scipy有scipy.interpolate模块,非常有用。您可以在官方页面中找到many examples。
答案 2 :(得分:1)
你的问题并不完全清楚;你试图优化你发布的代码,对吗?
重写这样的sinc应该会大大加快速度。此实现避免检查是否在每次调用时导入数学模块,不执行三次属性访问,并使用条件表达式替换异常处理:
from math import sin, pi
def sinc(x):
return (sin(pi * x) / (pi * x)) if x != 0 else 1.0
你也可以尝试通过直接创建一个numpy.array(而不是列表列表)来避免创建矩阵两次(并在内存中并行保存两次):
def resampleMatrix(Tso, Tsf, o, f):
retval = numpy.zeros((f, o))
for i in xrange(f):
for j in xrange(o):
retval[i][j] = sinc((Tsf*i - Tso*j)/Tso)
return retval
(在Python 3.0及更高版本上用x替换xrange)
最后,您可以使用numpy.arange创建行,也可以在每行甚至整个矩阵上调用numpy.sinc:
def resampleMatrix(Tso, Tsf, o, f):
retval = numpy.zeros((f, o))
for i in xrange(f):
retval[i] = numpy.arange(Tsf*i / Tso, Tsf*i / Tso - o, -1.0)
return numpy.sinc(retval)
这应该比原始实现快得多。尝试不同的这些想法的组合,并测试他们的表现,看看哪个是最好的!
答案 3 :(得分:1)
这是一个用scipy进行1d插值的最小例子 - 不像重新发明那么有趣,但是。
情节看起来像sinc
,这不是巧合:
尝试谷歌样条重新取样“近似sinc”
(可能更少的本地/更多的水龙头⇒更好的近似,
但我不知道当地的UnivariateSplines是怎样的。)
""" interpolate with scipy.interpolate.UnivariateSpline """
from __future__ import division
import numpy as np
from scipy.interpolate import UnivariateSpline
import pylab as pl
N = 10
H = 8
x = np.arange(N+1)
xup = np.arange( 0, N, 1/H )
y = np.zeros(N+1); y[N//2] = 100
interpolator = UnivariateSpline( x, y, k=3, s=0 ) # s=0 interpolates
yup = interpolator( xup )
np.set_printoptions( 1, threshold=100, suppress=True ) # .1f
print "yup:", yup
pl.plot( x, y, "green", xup, yup, "blue" )
pl.show()
添加了2010年2月的费用:另见basic-spline-interpolation-in-a-few-lines-of-numpy
答案 4 :(得分:1)
我不太确定你要做什么,但是你可以做一些加速来创建矩阵。 Braincore's suggestion使用numpy.sinc
是第一步,但第二步是要意识到numpy函数想要在numpy数组上工作,它们可以在C speen上执行循环,并且可以比单个数组更快地执行元件。
def resampleMatrix(Tso, Tsf, o, f):
retval = numpy.sinc((Tsi*numpy.arange(i)[:,numpy.newaxis]
-Tso*numpy.arange(j)[numpy.newaxis,:])/Tso)
return retval
技巧是通过使用numpy.newaxis索引aranges,numpy将具有形状i的数组转换为形状为i x 1的数组,将形状为j的数组转换为形状1 x j。在减法步骤中,numpy将“广播”每个输入以充当i x j形阵列并进行减法。 (“广播”是numpy的术语,反映了没有额外的副本将i x 1拉伸到i x j的事实。)
现在numpy.sinc可以迭代编译代码中的所有元素,比你可以编写的任何for循环快得多。
(如果你在减法之前进行除法,还有一个额外的加速,特别是因为在后者中除法取消了乘法。)
唯一的缺点是你现在需要支付额外的Nx10 * N阵列以保持差异。如果N很大并且内存是个问题,这可能是一个破坏者。
否则,您应该能够使用numpy.convolve
来写这个。从我刚刚学到的关于sinc-interpolation的一点点开始,我会说你想要numpy.convolve(orig,numpy.sinc(numpy.arange(j)),mode="same")
之类的东西。但我对这些细节可能不对。
答案 5 :(得分:1)
如果您唯一的兴趣是“生成”平滑的“情节”,我会选择简单的多项式样条曲线:
对于任何两个相邻的数据点,可以从这些数据点的坐标和左右两个附加点(忽略边界点)计算三次多项式函数的系数。这将产生一个很好的点平滑的曲线与连续的第一个dirivitive。将4个坐标转换为4个多项式系数有一个直接的公式,但我不想剥夺你查找它的乐趣; o)。
答案 6 :(得分:0)
小改进。使用内置的numpy.sinc(x)函数,该函数在已编译的C代码中运行。
可能有更大的改进:你可以动态进行插值(当绘图时)吗?或者你是否只接受一个只接受矩阵的绘图库?
答案 7 :(得分:0)
我建议您检查算法,因为这是一个非常重要的问题。具体来说,我建议您访问Hu和Pavlidis(1991)的文章“使用圆锥花键的功能绘图”(IEEE计算机图形和应用程序)。它们的算法实现允许对函数进行自适应采样,使得渲染时间小于规则间隔的方法。
摘要如下:
提出了一种方法,给出了一个 a的数学描述 函数,近似的圆锥样条 产生了函数的图。 圆锥曲线被选为 原始曲线因为有 简单的增量绘图算法 对于已包含在某些中的圆锥曲线 设备驱动程序,有简单 用于局部近似的算法 圆锥曲线。拆分合并算法 自适应地选择结, 根据形状分析 基于它的原始功能 一阶导数,是 引入的。