numpy 有效检测Python中的符号变化

gblwokeq  于 2023-04-21  发布在  Python
关注(0)|答案(7)|浏览(154)

我想做这个人做过的事:
Python - count sign changes
然而,我需要优化它运行超快。简而言之,我想采取一个时间序列,并告诉每一次它交叉过零(改变符号)。我想记录零交叉之间的时间。因为这是真实的数据(32位浮点数)我怀疑我会有一个完全为零的数字,所以这并不重要。我现在有一个计时程序,所以我会计时你的结果,看看谁赢了。
我的解决方案给出(微秒):

open data       8384
sign data       8123
zcd data        415466

正如你所看到的,过零检测器是慢的部分。

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()
blmhpbnm

blmhpbnm1#

关于:

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]

输出:

> zero_crossings
array([ 3,  5,  9, 10, 11, 12, 15])

也就是说,zero_crossings将包含发生零交叉的元素 before 的索引。如果你想要 after 的元素,只需向该数组添加1。

mspsb9vt

mspsb9vt2#

正如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]

因为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]
sczxawaw

sczxawaw3#

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

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

或者,如果你只想计算过零点的特定方向(例如,从正到负)的过零点,这甚至更快:

def crossings_nonzero_pos2neg(data):
    pos = data > 0
    return (pos[:-1] & ~pos[1:]).nonzero()[0]

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

$ 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
13z8s7eq

13z8s7eq4#

如果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

输出:

[ 3  4  6 10 11 12 13 16]  
8

零交叉的数量应该是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

其给予的正确答案为:

[ 3  6 10 14 15 18 21]
7
2uluyalo

2uluyalo5#

另一种可能适合某些应用程序的方法是扩展表达式np.diff(np.sign(a))的求值。
如果我们比较这个表达式在某些情况下的React:
1.上升无零交叉:np.diff(np.sign([-10, 10]))返回array([2])
1.上升过零:np.diff(np.sign([-10, 0, 10]))返回array([1, 1])
1.无零点落交:np.diff(np.sign([10, -10]))返回array([-2])
1.落零交叉:np.diff(np.sign([10, 0, -10]))返回array([-1, -1])
因此,我们必须为1.和2.中返回的模式计算np.diff(...)

sdiff = np.diff(np.sign(a))
rising_1 = (sdiff == 2)
rising_2 = (sdiff[:-1] == 1) & (sdiff[1:] == 1)
rising_all = rising_1
rising_all[1:] = rising_all[1:] | rising_2

以及对于情况3和4:

falling_1 = (sdiff == -2) #the signs need to be the opposite
falling_2 = (sdiff[:-1] == -1) & (sdiff[1:] == -1)
falling_all = falling_1
falling_all[1:] = falling_all[1:] | falling_2

在此之后,我们可以很容易地找到索引

indices_rising = np.where(rising_all)[0]
indices_falling = np.where(falling_all)[0]
indices_both = np.where(rising_all | falling_all)[0]

这种方法应该是合理的快,因为它可以管理,而不使用“慢”循环。
这结合了其他几个答案的方法。

roqulrg3

roqulrg36#

我看到人们在他们的解决方案中使用了很多diff,但xor似乎更快,结果对于bools是一样的(一个很好的指针也可能是使用diff给出了一个不推荐的警告...:))这里有一个例子:

positive = a2 > 0
np.where(np.bitwise_xor(positive[1:], positive[:-1]))[0]

timeit测量它对我来说比diff快一点半:)
如果您不关心边缘情况,那么使用

positive = np.signbit(a2)

但是positive = a2〉0看起来比signbit AND检查0更快(并且更干净)(例如positive = np.bitwise_or(np.signbit(a2),np.logical_not(a2))更慢...)

9nvpjoqh

9nvpjoqh7#

使用移位数组的元素乘法应该是最快的:

X = np.array([ -7,   5,  -9,   4, -10,   6,   3,   3,  -5,   5])
sign_changes = np.signbit(X[1:]*X[:-1]) 
#Prepend 0 to get array of the same size
sign_changes = np.insert(sign_changes, 0, 0)

相关问题