如何提高 2d interpolating/smoothing 行使用 scipy 时的性能?
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,其中 none 有效。然后我查看了官方scipy参考资料:scipy.interpolate.spline这里。他们说 spline
在 v 0.19 中已弃用,但我没有使用新版本。如果 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
SciPy 中的大多数插值方法都是函数生成的,即它们 return 函数,然后您可以在 x 数据上执行。例如,使用 CubicSpline
方法,用 pointwise cubic 样条连接所有点将是
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)
既然你有这么多的数据,它可能一定很嘈杂。我建议使用更大的样条阶数,这与用于插值的结数有关。
在这两种情况下,您的结,即 x 和 y 都应该排序。这些是一维插值(因为您仅使用 x_smooth
作为输入)。您可以使用 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()
这里有几个问题。首先,您尝试使用的样条曲线拟合是全局的。这意味着您在构建时正在求解一个大小为 20000 的线性方程组(尽管评估对数据集大小不太敏感)。这解释了为什么样条构造缓慢。
scipy.interpolate.spline
,此外,用全矩阵做线性代数——因此内存消耗。这正是它从 scipy 0.19.0 开始被弃用的原因。
在 scipy 0.19.0 中可用的推荐替代品是 BSpline
/ make_interp_spline
组合:
>>> 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
组合。
不过,你可能要想想你是否真的需要两次连续可微的函数。如果只有一次可微函数对于您的目的足够平滑,那么您可以使用局部样条插值,例如Akima1DInterpolator
或 PchipInterpolator
:
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
这里 splmake
是 spline
在幕后使用的,它也被弃用了。
我的问题可以概括为如何在数据点随机化时平滑地绘制二维图形。由于您只处理两列数据,如果您按自变量对数据进行排序,至少您的数据点将按顺序连接,这就是 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
值,而这些值没有对应的 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 #
这就是我解决问题的方法。
我有一个中等大小的数据集,即两列矩阵中的 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,其中 none 有效。然后我查看了官方scipy参考资料:scipy.interpolate.spline这里。他们说 spline
在 v 0.19 中已弃用,但我没有使用新版本。如果 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
SciPy 中的大多数插值方法都是函数生成的,即它们 return 函数,然后您可以在 x 数据上执行。例如,使用 CubicSpline
方法,用 pointwise cubic 样条连接所有点将是
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)
既然你有这么多的数据,它可能一定很嘈杂。我建议使用更大的样条阶数,这与用于插值的结数有关。
在这两种情况下,您的结,即 x 和 y 都应该排序。这些是一维插值(因为您仅使用 x_smooth
作为输入)。您可以使用 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()
这里有几个问题。首先,您尝试使用的样条曲线拟合是全局的。这意味着您在构建时正在求解一个大小为 20000 的线性方程组(尽管评估对数据集大小不太敏感)。这解释了为什么样条构造缓慢。
scipy.interpolate.spline
,此外,用全矩阵做线性代数——因此内存消耗。这正是它从 scipy 0.19.0 开始被弃用的原因。
在 scipy 0.19.0 中可用的推荐替代品是 BSpline
/ make_interp_spline
组合:
>>> 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
组合。
不过,你可能要想想你是否真的需要两次连续可微的函数。如果只有一次可微函数对于您的目的足够平滑,那么您可以使用局部样条插值,例如Akima1DInterpolator
或 PchipInterpolator
:
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
这里 splmake
是 spline
在幕后使用的,它也被弃用了。
我的问题可以概括为如何在数据点随机化时平滑地绘制二维图形。由于您只处理两列数据,如果您按自变量对数据进行排序,至少您的数据点将按顺序连接,这就是 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
值,而这些值没有对应的 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 #
这就是我解决问题的方法。