有效检测python中的符号变化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3843017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Efficiently detect sign-changes in python
提问by chriscauley
I want to do exactly what this guy did:
我想做的正是这个人所做的:
However I need to optimize it to run super fast. In brief I want to take a time series and tell every time it crosses crosses zero (changes sign). I want to record the time in between zero crossings. Since this is real data (32 bit float) I doubt I'll every have a number which is exactly zero, so that is not important. I currently have a timing program in place so I'll time your results to see who wins.
但是我需要优化它以超快运行。简而言之,我想记录一个时间序列,并在它每次越过零(改变符号)时告诉它。我想记录零交叉之间的时间。由于这是真实数据(32 位浮点数),我怀疑每个人都会有一个恰好为零的数字,因此这并不重要。我目前有一个计时计划,所以我会为你的结果计时,看看谁赢了。
My solution gives (micro seconds):
我的解决方案给出(微秒):
open data 8384
sign data 8123
zcd data 415466
As you can see the zero-crossing detector is the slow part. Here's my code.
如您所见,过零检测器是缓慢的部分。这是我的代码。
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()
采纳答案by Jim Brissom
What about:
关于什么:
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]
Output:
输出:
> zero_crossings
array([ 3, 5, 9, 10, 11, 12, 15])
I.e., zero_crossings will contain the indices of elements beforewhich a zero crossing occurs. If you want the elements after, just add 1 to that array.
即, zero_crossings 将包含发生过零之前元素的索引。如果你想要的元素后,只需添加1到该阵列。
回答by Mike Dunlavey
Do you want to time it? Or do you want to make it as fast as possible?
你想计时吗?或者你想让它尽可能快?
Timing is easy. Run it a zillion times, stopwatch it, and divide by a zillion.
时间安排很容易。运行无数次,秒表,然后除以无数次。
To make it as fast as possible, what you need to do is find out what's taking time and that you could do in a better way. I use either 1) the random-pause technique, or 2) the single-step technique.
为了尽可能快地完成,您需要做的是找出什么需要时间以及您可以以更好的方式完成。我使用 1) 随机暂停技术,或 2) 单步技术。
回答by Jay Borseth
Jim Brissom's answer fails if acontains the value 0:
如果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
Output:
输出:
[ 3 4 6 10 11 12 13 16]
8
The number of zero crossing should be 7, but because sign() returns 0 if 0 is passed, 1 for positive, and -1 for negative values, diff() will count the transition containing zero twice.
过零的次数应该是 7,但是因为如果传递 0,sign() 返回 0,1 表示正值,-1 表示负值,diff() 将对包含零的转换计数两次。
An alternative might be:
另一种选择可能是:
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
which give the correct answer of:
这给出了正确的答案:
[ 3 6 10 14 15 18 21]
7
回答by lmjohns3
Another way to count zero crossings and squeeze just a few more milliseconds out of the code is to use nonzeroand compute the signs directly. Assuming you have a one-dimensional array of data:
另一种计算过零并从代码中多挤出几毫秒的方法是直接使用nonzero和计算符号。假设您有一个一维数组data:
def crossings_nonzero_all(data):
pos = data > 0
npos = ~pos
return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0]
Alternatively, if you just want to count the zero crossings for a particular direction of crossing zero (e.g., from positive to negative), this is even faster:
或者,如果您只想计算过零的特定方向(例如,从正到负)的过零,这甚至更快:
def crossings_nonzero_pos2neg(data):
pos = data > 0
return (pos[:-1] & ~pos[1:]).nonzero()[0]
On my machine these are a bit faster than the where(diff(sign))method (timings for an array of 10000 sine samples containing 20 cycles, 40 crossings in all):
在我的机器上,这些比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
回答by Dominik Neise
As remarked by Jay Borseth the accepted answer does not handle arrays containing 0 correctly.
正如 Jay Borseth 所说,接受的答案没有正确处理包含 0 的数组。
I propose using:
我建议使用:
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]
Since a) using numpy.signbit() is a little bit quicker than numpy.sign(), since it's implementation is simpler, I guess and b) it deals correctly with zeros in the input array.
因为 a) 使用 numpy.signbit() 比 numpy.sign() 快一点,因为它的实现更简单,我猜和 b) 它正确处理输入数组中的零。
However there is one drawback, maybe: If your input array starts and stops with zeros, it will find a zero crossing at the beginning, but not at the end...
但是有一个缺点,也许:如果您的输入数组以零开始和停止,它会在开始时找到零交叉,但不会在结束时找到...
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]
回答by ntg
I see people using diff a lot in their solutions, but xor seems to be much faster and the result is the same for bools (a good pointer to that might also be the fact that using diff gives a deprecated warning.... :) ) Here is an example:
我看到人们在他们的解决方案中经常使用 diff,但是 xor 似乎要快得多,并且结果对于 bool 来说是一样的(一个很好的指针也可能是使用 diff 给出了一个已弃用的警告....:) ) 这是一个例子:
positive = a2 > 0
np.where(np.bitwise_xor(positive[1:], positive[:-1]))[0]
Time it measures it to be around one and a half faster to diff for me:)
时间它测量它对我来说比差异快了大约一个半:)
If you do not care about edge cases it might be better to use
如果您不关心边缘情况,最好使用
positive = np.signbit(a2)
but positive = a2 >0 seems faster (and cleaner) than signbit AND checking for 0s (e.g. positive = np.bitwise_or(np.signbit(a2),np.logical_not(a2)) is slower...)
但正 = a2 >0 似乎比符号位和检查 0 更快(更干净)(例如正 = np.bitwise_or(np.signbit(a2),np.logical_not(a2)) 更慢......)
回答by lagru
Another way that might suit certain applications is to extend the evaluation of the expression np.diff(np.sign(a)).
另一种可能适合某些应用程序的方法是扩展表达式的计算np.diff(np.sign(a))。
If we compare how this expression reacts to certain cases:
如果我们比较这个表达式对某些情况的反应:
- Rising crossing without zero:
np.diff(np.sign([-10, 10]))returnsarray([2]) - Rising crossing with zero:
np.diff(np.sign([-10, 0, 10]))returnsarray([1, 1]) - Falling crossing without zero:
np.diff(np.sign([10, -10]))returnsarray([-2]) - Falling crossing with zero:
np.diff(np.sign([10, 0, -10]))returnsarray([-1, -1])
- 没有零的上升交叉点:
np.diff(np.sign([-10, 10]))返回array([2]) - 与零上升交叉:
np.diff(np.sign([-10, 0, 10]))返回array([1, 1]) - 没有零的下降交叉点:
np.diff(np.sign([10, -10]))返回array([-2]) - 下降与零交叉:
np.diff(np.sign([10, 0, -10]))返回array([-1, -1])
So we have to evaluate np.diff(...)for the returned patterns in 1. and 2:
所以我们必须评估np.diff(...)1. 和 2 中返回的模式:
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
and for the cases 3. and 4.:
对于情况 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
After this we can easily find the indices with
在此之后,我们可以轻松找到索引
indices_rising = np.where(rising_all)[0]
indices_falling = np.where(falling_all)[0]
indices_both = np.where(rising_all | falling_all)[0]
This approach should be reasonable fast because it can manage without using a "slow" loop.
这种方法应该是合理的快速,因为它可以在不使用“慢”循环的情况下进行管理。
This combines the approach of several other answers.
这结合了其他几个答案的方法。

