繁体   English   中英

使用scipy对2d线进行插值/平滑处理时,如何提高性能?

[英]How to improve the performance when 2d interpolating/smoothing lines using scipy?

我有一个中等大小的数据集,即两列矩阵中的20000 x 2浮点数。 第一列是x列,代表沿轨迹到原始点的距离,另一列是y列,代表对对象完成的工作。 该数据集是从实验室操作中获得的,因此相当随意。 我已经把这个结构变成了numpy数组。 我想在具有平滑曲线的图中绘制y vs x。 所以我希望以下代码能对我有所帮助:

x_smooth = np.linspace(x.min(),x.max(), 20000)
y_smooth = spline(x, y, x_smooth)
plt.plot(x_smooth, y_smooth)
plt.show()

但是,当我的程序执行y_smooth = spline(x,y,x_smooth) ,它要花很长时间,例如10分钟,甚至有时它会炸毁我的内存,我必须重新启动机器。 我试图将块数减少到200和2000,但它们都不起作用。 然后,我检查了官方的scipy参考: scipy.interpolate.spline 他们说在0.19版中已弃用spline线,但我没有使用新版本。 如果不建议使用spline线很多时间,现在如何使用等效的Bspline 如果spline线仍然起作用,那么是什么原因导致性能下降

我的数据的一部分可能看起来像这样:

13.202      0.0
13.234738      -0.051354643759
12.999116      0.144464320836
12.86252      0.07396528119
13.1157      0.10019738758
13.357109      -0.30288563381
13.234004      -0.045792536285
12.836279      0.0362257166275
12.851597      0.0542649286915
13.110691      0.105297378401
13.220619      -0.0182963209185
13.092143      0.116647353635
12.545676      -0.641112204849
12.728248      -0.147460703493
12.874176      0.0755861585235
12.746764      -0.111583725833
13.024995      0.148079528382
13.106033      0.119481137144
13.327233      -0.197666132456
13.142423      0.0901867159545

这里有几个问题。 首先,您要使用的样条拟合是全局的。 这意味着您在构建时正在求解大小为20000的线性方程组(尽管评估对数据集的大小非常敏感)。 这解释了为什么样条构建缓慢。

此外, scipy.interpolate.spline做具有完整矩阵的线性代数---因此占用了内存。 这就是为什么从scipy 0.19.0开始不推荐使用的原因。

推荐的替代品是BSpline / make_interp_spline组合,可在scipy 0.19.0中使用:

>>> spl = make_interp_spline(x, y, k=3)    # returns a BSpline object
>>> y_new = spl(x_new)                     # evaluate 

注意它不是 BSpline(x, y, k) :BSpline对象对数据,拟合或插值一无所知。

如果您使用的是较旧的scipy版本,则可以选择:

  • 三次样条CubicSpline(x, y)
  • splrep(x, y, s=0) / splev组合。

但是,您可能需要考虑是否真的需要两次连续微分的功能。 如果只有一次可微函数足以满足您的目的,那么您可以使用局部样条插值,例如Akima1DInterpolatorPchipInterpolator

In [1]: import numpy as np

In [2]: from scipy.interpolate import pchip, splmake

In [3]: x = np.arange(1000)

In [4]: y = x**2

In [5]: %timeit pchip(x, y)
10 loops, best of 3: 58.9 ms per loop

In [6]: %timeit splmake(x, y)    
1 loop, best of 3: 5.01 s per loop

splmakesplinesplmake使用的东西,它也已弃用。

SciPy中的大多数插值方法都是函数生成的,即它们返回可以在x数据上执行的函数 例如,使用CubicSpline方法,该方法将所有点与逐点三次样条曲线相连

from scipy.interpolate import CubicSpline

spline = CubicSpline(x, y)
y_smooth = spline(x_smooth)

根据您的描述,我认为您正确地希望使用BSpline。 为此,请遵循上面的模式,即

from scipy.interpolate import BSpline

order = 2 # smoothness order
spline = BSpline(x, y, order)
y_smooth = spline(x_smooth)

由于您拥有如此大量的数据,因此它可能必须非常嘈杂。 我建议使用更大的样条顺序,这与用于插值的结数有关。

在这两种情况下,您的结,即xy ,都应进行排序。 这些是一维插值(因为仅使用x_smooth作为输入)。 您可以使用np.argsort对它们进行np.argsort 简而言之:

from scipy.interpolate import BSpline

sort_idx = np.argsort(x)
x_sorted = x[sort_idx]
y_sorted = y[sort_idx]

order = 20 # smoothness order
spline = BSpline(x_sorted, y_sorted, order)
y_smooth = spline(x_smooth)

plt.plot(x_sorted, y_sorted, '.')
plt.plot(x_smooth, y_smooth, '-')
plt.show()

我的问题可以概括为当数据点随机化时如何平滑绘制2d图形。 由于您只处理两列数据,因此,如果按自变量对数据进行排序,则至少将按顺序连接数据点,这就是matplotlib连接数据点的方式。

@Dawid Laszuk提供了一种解决方案,可以按自变量对数据进行排序,我将在这里显示我的方法:

plotting_columns = []
    for i in range(len(x)):
        plotting_columns.append(np.array([x[i],y[i]]))
    plotting_columns.sort(key=lambda pair : pair[0])
    plotting_columns = np.array(plotting_columns)

传统的按过滤条件的sort()在这里也可以有效地完成排序工作。

但这只是您的第一步。 以下步骤也不难,为使图形平滑,您还希望将自变量以相同的步长间隔保持线性升序,因此

x_smooth = np.linspace(x.min(), x.max(), num_steps)

足以胜任这项工作。 通常,如果您有大量数据点,例如,超过10000个点(正确性和准确性是无法人工验证的),则只想绘制重要点以显示趋势,则仅平滑x就足够了。 因此,您可以简单地plt.plot(x_smooth,y)

您会注意到x_smooth将生成许多x值,而这些x值将没有对应的y值。 如果要保持正确性,则需要使用线拟合功能。 正如@ ev-br在他的回答中所表明的那样, spline函数的目的是昂贵的。 因此,您可能想做一些简单的技巧。 我在不使用这些函数的情况下平滑了图表。 您只需执行一些简单的步骤。

首先,四舍五入您的值,以使您的数据在很小的间隔内不会有太大变化。 (您可以跳过此步骤)将plotting_columns构造为以下内容时,可以更改一行:

plotting_columns.append(np.around(np.array(x[i],y[i]), decimal=4))

完成此操作后,可以通过选择接近x_smooth值的点来过滤掉不想绘制的点:

new_plots = []
for i in range(len(x_smooth)):
    if plotting_columns[:,0][i] >= x_smooth[i] - error and plotting_columns[:,0][i]< x_smooth[i] + error:
        new_plots.append(plotting_columns[i])
    else:
        # Remove all points between the interval #

这就是我解决问题的方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM