【问题标题】:Fully vectorise numpy polyfit完全矢量化 numpy polyfit
【发布时间】:2015-03-30 00:28:11
【问题描述】:

概述

我在使用 polyfit 时遇到了性能问题,因为它似乎无法接受广播数组。我知道from this post 如果您使用numpy.polynomial.polynomial.polyfit,则依赖数据y 可以是多维的。但是,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()

蓝线是原始数据(正弦曲线),而绿线是一阶微分(余弦曲线)。

问题

为了矢量化,我做了以下操作:

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 参数,这样我就可以并行计算,但没有这样的运气。

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

【问题讨论】:

  • 计算一阶数值导数而不是使用 polyfit 应该更快更准确
  • @M4rtini 你是对的,但我这样做是为了与实验者使用的方法保持一致。它在问题中,但我很感激有太多的文字让任何人都被打扰阅读。
  • 哦,错过了问题的那一部分。我想我的答案完全无关紧要

标签: python numpy


【解决方案1】:

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

y = [X].a

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

在您的情况下,您总是在计算 1 次多项式近似值,并且实际上只对 1 次项的系数感兴趣。这有一个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】:

    使用另一种计算变化率的方法可能是提高速度和准确性的解决方案。

    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 
    

    相对误差:

    结果:

    【讨论】:

    • 正如问题的 cmets 中所讨论的,我想解释的正是这些错误!尽管我非常感谢您在这里所做的努力 - 我实际上并没有意识到这种方法有多糟糕!出于这个原因,我会把它留在这里!
    • 我似乎从数值数学课程中记得多项式本质上不适合拟合谐波函数。所以相对误差对于其他类型的数据来说可能并没有那么糟糕。不过速度应该还是一样的。我将把它留在这里以供将来参考,即使它没有直接回答问题。
    【解决方案3】:

    很抱歉回答了我自己的问题,但尝试掌握它还有 20 分钟的时间,我有以下解决方案:

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

    一个令人困惑的部分是np.polyfit 返回具有最高幂的系数first。在np.polynomial.polynomial.polyfit 中,最高功率是last(因此使用-1 而不是0 索引)。

    另一个混淆是我们只使用了x (x_array[0]) 的第一部分。我认为这没关系,因为使用的不是独立向量x 的绝对值,而是它们之间的差异。或者,它就像更改引用 x 值。

    如果有更好的方法来做到这一点,我仍然很高兴听到它!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-27
      • 2018-03-17
      • 1970-01-01
      • 2021-04-27
      • 2020-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多