完全矢量化numpy polyfit

时间:2015-01-30 13:55:21

标签: python numpy

概述

我使用polyfit遇到性能问题,因为它似乎不能接受广播数组。我知道from this post如果您使用y,则相关数据numpy.polynomial.polynomial.polyfit可以是多维的。但是,x维度不能是多维的。反正有吗?

动机

我需要计算一些数据的变化率。为了与实验匹配,我想使用以下方法:获取数据yx,对于适合多项式的短数据部分,然后使用拟合系数作为变化率的估计。

插图

import numpy as np
import matplotlib.pyplot as plt

n = 100
x = np.linspace(0, 10, n)
y = np.sin(x)

window_length = 10
ydot = [np.polyfit(x[j:j+window_length], y[j:j+window_length], 1)[0] 
                                  for j in range(n - window_length)]
x_mids = [x[j+window_length/2] for j in range(n - window_length)]

plt.plot(x, y)
plt.plot(x_mids, ydot)

plt.show()

enter image description here

蓝线是原始数据(正弦曲线),而绿色是第一个差分(余弦曲线)。

问题

为了实现这一点,我做了以下工作:

window_length = 10
vert_idx_list = np.arange(0, len(x) - window_length, 1)
hori_idx_list = np.arange(window_length)
A, B = np.meshgrid(hori_idx_list, vert_idx_list)
idx_array = A + B 

x_array = x[idx_array]
y_array = y[idx_array]

这将两个1D向量广播到形状为(n-window_length, window_length)的2D向量。现在我希望polyfit会有一个axis参数,所以我可以将计算并行化,但没有这样的运气。

有没有人对如何做到这一点有任何建议?我愿意接受

3 个答案:

答案 0 :(得分:3)

很抱歉回答我自己的问题,但是还有20分钟的时间试图解决这个问题我有以下解决方案:

ydot = np.polynomial.polynomial.polyfit(x_array[0], y_array.T, 1)[-1]

一个令人困惑的部分是np.polyfit返回功率最高第一的系数。在np.polynomial.polynomial.polyfit中,最高权力最后(因此-1代替0索引。

另一个困惑是我们只使用xx_array[0])的第一个切片。我认为这是可以的,因为它不是所使用的独立向量x的绝对值,而是它们之间的差异。或者,它就像更改引用x值。

如果有更好的方法,我仍然很高兴听到它!

答案 1 :(得分:3)

polyfit的工作方式是解决形式的最小二乘问题:

y = [X].a

其中y是您的相关坐标,[X]是相应独立坐标的Vandermonde matrixa是拟合系数的矢量。

在你的情况下,你总是计算一次多项式近似,并且实际上只对第一度项的系数感兴趣。这有well known closed form solution你可以在任何统计书中找到,或者通过创建一个2x2线性方程组来产生你的自我,通过[X]的转置预乘上述方程的两边。这一切都加起来要计算的值为:

>>> n = 10
>>> x = np.random.random(n)
>>> y = np.random.random(n)
>>> np.polyfit(x, y, 1)[0]
-0.29207474654700277
>>> (n*(x*y).sum() - x.sum()*y.sum()) / (n*(x*x).sum() - x.sum()*x.sum())
-0.29207474654700216

除此之外,您还有一个在您的数据上运行的滑动窗口,因此您可以使用类似于1D summed area table的内容,如下所示:

def sliding_fitted_slope(x, y, win):
    x = np.concatenate(([0], x))
    y = np.concatenate(([0], y))
    Sx = np.cumsum(x)
    Sy = np.cumsum(y)
    Sx2 = np.cumsum(x*x)
    Sxy = np.cumsum(x*y)

    Sx = Sx[win:] - Sx[:-win]
    Sy = Sy[win:] - Sy[:-win]
    Sx2 = Sx2[win:] - Sx2[:-win]
    Sxy = Sxy[win:] - Sxy[:-win]

    return (win*Sxy - Sx*Sy) / (win*Sx2 - Sx*Sx)

使用此代码,您可以轻松检查(通知我将范围扩展了1):

>>> np.allclose(sliding_fitted_slope(x, y, window_length),
                [np.polyfit(x[j:j+window_length], y[j:j+window_length], 1)[0]
                 for j in range(n - window_length + 1)])
True

%timeit sliding_fitted_slope(x, y, window_length)
10000 loops, best of 3: 34.5 us per loop

%%timeit
[np.polyfit(x[j:j+window_length], y[j:j+window_length], 1)[0]
 for j in range(n - window_length + 1)]
100 loops, best of 3: 10.1 ms per loop

因此,样本数据的速度提高了约300倍。

答案 2 :(得分:1)

使用替代方法计算变化率可能是速度和准确度增加的解决方案。

n = 1000
x = np.linspace(0, 10, n)
y = np.sin(x)


def timingPolyfit(x,y):
    window_length = 10
    vert_idx_list = np.arange(0, len(x) - window_length, 1)
    hori_idx_list = np.arange(window_length)
    A, B = np.meshgrid(hori_idx_list, vert_idx_list)
    idx_array = A + B 

    x_array = x[idx_array]
    y_array = y[idx_array]

    ydot = np.polynomial.polynomial.polyfit(x_array[0], y_array.T, 1)[-1]
    x_mids = [x[j+window_length/2] for j in range(n - window_length)]

    return ydot, x_mids

def timingSimple(x,y):
    dy = (y[2:] - y[:-2])/2
    dx =  x[1] - x[0]
    dydx = dy/dx
    return dydx, x[1:-1]

y1, x1 = timingPolyfit(x,y)
y2, x2 = timingSimple(x,y)

polyfitError = np.abs(y1 - np.cos(x1))
simpleError = np.abs(y2 - np.cos(x2))

print("polyfit average error: {:.2e}".format(np.average(polyfitError)))
print("simple average error: {:.2e}".format(np.average(simpleError)))

result = %timeit -o timingPolyfit(x,y)
result2 = %timeit -o timingSimple(x,y)

print("simple is {0} times faster".format(result.best / result2.best))

polyfit average error: 3.09e-03 
simple average error: 1.09e-05 
100 loops, best of 3: 3.2 ms per loop 
100000 loops, best of 3: 9.46 µs per loop 
simple is 337.995634151131 times faster 

相对错误: Relative error

<强>结果: Results-closeup