有效地检测python中的符号更改

chr*_*ley 29 python math performance numpy

我想完成这个人做的事情:

Python - 计算符号更改

但是我需要优化它以超快速运行.简而言之,我想要一个时间序列,并告诉每次它越过零(改变符号).我想记录过零点之间的时间.由于这是真实的数据(32位浮点数),我怀疑我每个都有一个正好为零的数字,所以这并不重要.我目前有一个计时程序,所以我会把你的结果计算在内,看看谁赢了.

我的解决方案给出(微秒):

open data       8384
sign data       8123
zcd data        415466
Run Code Online (Sandbox Code Playgroud)

如您所见,过零检测器是缓慢的部分.这是我的代码.

import numpy, datetime

class timer():
    def __init__(self):
        self.t0 = datetime.datetime.now()
        self.t = datetime.datetime.now()
    def __call__(self,text='unknown'):
        print text,'\t',(datetime.datetime.now()-self.t).microseconds
        self.t=datetime.datetime.now()

def zcd(data,t):
    sign_array=numpy.sign(data)
    t('sign data')
    out=[]
    current = sign_array[0]
    count=0
    for i in sign_array[1:]:
        if i!=current:
            out.append(count)
            current=i
            count=0
        else: count+=1
    t('zcd data')
    return out

def main():
    t = timer()
    data = numpy.fromfile('deci.dat',dtype=numpy.float32)
    t('open data')
    zcd(data,t)

if __name__=='__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

Jim*_*som 65

关于什么:

import numpy
a = [1, 2, 1, 1, -3, -4, 7, 8, 9, 10, -2, 1, -3, 5, 6, 7, -10]
zero_crossings = numpy.where(numpy.diff(numpy.sign(a)))[0]
Run Code Online (Sandbox Code Playgroud)

输出:

> zero_crossings
array([ 3,  5,  9, 10, 11, 12, 15])
Run Code Online (Sandbox Code Playgroud)

即,zero_crossings将包含元素的索引,之后发生过零.如果您想要之前的元素,只需向该数组添加1即可.

  • 当数组中存在零时,这不起作用.它会检测到它们两次!示例:`a = [2,1,0,-1,2]`将给出`array([1,2,3])` (14认同)
  • 我认为你倒退了。“ zero_crossings”包含元素_before_的遮盖物,发生零交叉,如果想要元素_after_,则将1加到数组中。否则,答案简明扼要! (2认同)

Dom*_*ise 28

正如Jay Borseth所说,接受的答案不能正确处理包含0的数组.

我建议使用:

import numpy as np
a = np.array([-2, -1, 0, 1, 2])
zero_crossings = np.where(np.diff(np.signbit(a)))[0]
print(zero_crossings)
# output: [1]
Run Code Online (Sandbox Code Playgroud)

因为a)使用numpy.signbit()比numpy.sign()快一点,因为它的实现更简单,我想和b)它在输入数组中正确处理零.

但是有一个缺点,可能是:如果您的输入数组以零开始和停止,它将在开头找到零交叉,但不会在结束时找到...

import numpy as np
a = np.array([0, -2, -1, 0, 1, 2, 0])
zero_crossings = np.where(np.diff(np.signbit(a)))[0]
print(zero_crossings)
# output: [0 2]
Run Code Online (Sandbox Code Playgroud)


lmj*_*ns3 11

另一种计算零交叉并从代码中挤出几毫秒的方法是直接使用nonzero和计算符号.假设你有一个一维数组data:

def crossings_nonzero_all(data):
    pos = data > 0
    npos = ~pos
    return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0]
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想计算特定零交叉方向的过零点(例如,从正到负),则更快:

def crossings_nonzero_pos2neg(data):
    pos = data > 0
    return (pos[:-1] & ~pos[1:]).nonzero()[0]
Run Code Online (Sandbox Code Playgroud)

在我的机器上,这些比where(diff(sign))方法快一些(10000个正弦样本数组的定时包含20个循环,总共40个交叉点):

$ python -mtimeit 'crossings_where(data)'
10000 loops, best of 3: 119 usec per loop

$ python -mtimeit 'crossings_nonzero_all(data)'
10000 loops, best of 3: 61.7 usec per loop

$ python -mtimeit 'crossings_nonzero_pos2neg(data)'
10000 loops, best of 3: 55.5 usec per loop
Run Code Online (Sandbox Code Playgroud)

  • 您可以缩短 `(pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])` 到 `pos[:-1] ^ npos[1:]`,其中 `^` 是 XOR 运算符。 (2认同)

Jay*_*eth 8

如果a包含值0,Jim Brissom的答案将失败:

import numpy  
a2 = [1, 2, 1, 1, 0, -3, -4, 7, 8, 9, 10, -2, 1, -3, 5, 6, 7, -10]  
zero_crossings2 = numpy.where(numpy.diff(numpy.sign(a2)))[0]  
print zero_crossings2  
print len(zero_crossings2)  # should be 7
Run Code Online (Sandbox Code Playgroud)

输出:

[ 3  4  6 10 11 12 13 16]  
8  
Run Code Online (Sandbox Code Playgroud)

零交叉的数量应为7,但因为如果传递0则sign()返回0,正数为1,负值为-1,diff()将计算包含零的过渡两次.

另一种选择可能是:

a3 = [1, 2, 1, 1, 0, -3, -4, 7, 8, 9, 10, 0, -2, 0, 0, 1, 0, -3, 0, 5, 6, 7, -10]  
s3= numpy.sign(a3)  
s3[s3==0] = -1     # replace zeros with -1  
zero_crossings3 = numpy.where(numpy.diff(s3))[0]  
print s3  
print zero_crossings3  
print len(zero_crossings3)   # should be 7
Run Code Online (Sandbox Code Playgroud)

给出正确答案:

[ 3  6 10 14 15 18 21]
7
Run Code Online (Sandbox Code Playgroud)

  • 这不考虑 0 之前和之后的元素具有相同符号的情况。 (2认同)