1

我正在用 Python 编写一个程序,该程序正在处理实验期间生成的一些数据,它需要估计数据的斜率。我已经编写了一段代码,可以很好地做到这一点,但是速度非常慢(而且我不是很有耐心)。让我解释一下这段代码是如何工作的:

1)它抓取一小块大小为 dx 的数据(从 3 个数据点开始)

2) 评估差值(即 |y(x+dx)-y(x-dx)| )是否大于某个最小值(40x std.dev. of noise)

3)如果差异足够大,它将使用OLS回归计算斜率。如果差值太小,会增加 dx 并用这个新的 dx 重做循环

4) 这对所有数据点都继续

[进一步查看更新的代码]

对于大约 100k 次测量的数据大小,这大约需要 40 分钟,而程序的其余部分(它执行的处理比这更多)大约需要 10 秒。我敢肯定有一种更有效的方法来进行这些操作,你们能帮帮我吗?

谢谢

编辑:

好的,所以我通过仅使用二进制搜索解决了问题,将允许的步骤数限制为 200。我感谢大家的输入,我选择了对我帮助最大的答案。

最终更新代码:

def slope(self, data, time):
    (wave1, wave2) = wt.dwt(data, "db3")
    std = 2*np.std(wave2)
    e = std/0.05
    de = 5*std
    N = len(data)
    slopes = np.ones(shape=(N,))
    data2 = np.concatenate((-data[::-1]+2*data[0], data, -data[::-1]+2*data[N-1]))
    time2 = np.concatenate((-time[::-1]+2*time[0], time, -time[::-1]+2*time[N-1]))
    for n in xrange(N+1, 2*N):     
        left = N+1
        right = 2*N
        for i in xrange(200):
            mid = int(0.5*(left+right))
            diff = np.abs(data2[n-mid+N]-data2[n+mid-N])
            if diff >= e:
                if diff < e + de:  
                    break
                right = mid - 1
                continue
            left = mid + 1
        leftlim = n - mid + N
        rightlim = n + mid - N
        y = data2[leftlim:rightlim:int(0.05*(rightlim-leftlim)+1)]
        x = time2[leftlim:rightlim:int(0.05*(rightlim-leftlim)+1)]
        xavg = np.average(x)
        yavg = np.average(y)
        xlen = len(x)
        slopes[n-N] = (np.dot(x,y)-xavg*yavg*xlen)/(np.dot(x,x)-xavg*xavg*xlen)
    return np.array(slopes) 
4

3 回答 3

0

如何优化这将取决于您的数据的某些属性,但这里有一些想法:

  1. 您是否尝试过分析代码?使用其中一个Python 分析器可以为您提供一些有用的信息,了解什么是最耗时的。通常,您刚刚编写的一段代码会有一个最大的瓶颈,而且并不总是很明显它是哪一段;分析可以让您找出并首先解决主要瓶颈。

  2. 你知道什么是典型值i吗?如果你有一些想法,你可以从i大于 0 开始(正如@vhallac 指出的那样),或者通过增加i更大的数量来加快速度——如果你经常看到 的值很大i,一次增加i2 或 3;如果is 的分布有一条长尾,则尝试每次加倍;等等

  3. 进行最小二乘回归时是否需要所有数据?如果该函数调用是瓶颈,您可以通过仅使用该范围内的一些数据来加速它。例如,假设在某个特定点,您需要i200 岁才能看到数据中足够大(高于噪声)的变化。但是您可能不需要全部 400 个点来获得对斜率的良好估计——仅使用 10 或 20 个点,在start:end范围内均匀分布,可能就足够了,并且可能会大大加快代码速度。

于 2012-11-25T19:50:15.970 回答
0

我使用 Python 进行类似的分析,并提出一些建议。我没有查看您的代码的详细信息,只是针对您的问题陈述:

1)它抓取一小块大小为 dx 的数据(从 3 个数据点开始)

2) 评估差值(即 |y(x+dx)-y(x-dx)| )是否大于某个最小值(40x std.dev. of noise)

3)如果差异足够大,它将使用OLS回归计算斜率。如果差值太小,会增加 dx 并用这个新的 dx 重做循环

4) 这对所有数据点都继续

我认为执行缓慢的更明显的原因是代码的循环性质,也许你可以使用 Numpy 的向量化(基于数组的操作)性质。

对于第1步,可以直接执行`data[3:] - data[-3:] 并在单个数组操作中获取所有差异,而不是取点对;

对于第 2 步,您可以使用基于数组的测试的结果,numpy.argwhere(data > threshold)而不是测试某个循环中的每个元素;

第 3 步在我看来在概念上是错误的。你说如果差异太小,它会增加dx。但是,如果差异很小,则生成的斜率会很小,因为它实际上很小。然后,获得一个小的值是正确的结果,而人为地增加dx以获得“更好”的结果可能不是您想要的。好吧,它实际上可能是您想要的,但您应该考虑这一点。我建议您计算dx整个数据的固定斜率,然后使用生成的斜率数组来选择您感兴趣的区域(例如,使用data_slope[numpy.argwhere(data_slope > minimum_slope)].

希望这可以帮助!

于 2012-11-28T16:01:12.007 回答
0

您的评论表明您需要找到一种更好的方法来估计 i k+1给定的 i k。不知道 in 的值data会屈服于朴素的算法:

在 的每次迭代中n,保留i先前的值,并查看该abs(data[start]-data[end])值是否小于e。如果是,请保留i其先前的值,并像现在一样通过将其增加 1 来找到新的值。如果它大于或等于,则进行二进制搜索i以找到适当的值。您可以向前进行二进制搜索,但是在不知道的情况下找到一个好的候选上限data可能会很困难。该算法的性能不会比您当前的估计方法差。

如果您知道这data是一种平滑(没有突然的跳跃,因此所有i值都是平滑图)并且单调递增,您可以通过将二进制搜索的值减 1 来替换二进制搜索。

于 2012-11-25T20:34:38.387 回答