我想完成这个人的所作所为:
然而,我需要优化它以超快速运行。简而言之,我想要一个时间序列,并告诉每次它越过零(改变符号)。我想记录过零点之间的时间。由于这是真实的数据(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()
答案 0 :(得分:58)
怎么样:
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将包含在之后发生过零的元素的索引。如果您想要之前的元素,只需将1添加到该数组。
答案 1 :(得分:25)
正如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]
答案 2 :(得分:10)
计算零交叉并从代码中挤出几毫秒的另一种方法是使用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))
方法快一些(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
答案 3 :(得分: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
输出:
[ 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
答案 4 :(得分:3)
时间很简单。运行它数十万次,秒表它,并划分数十亿。
为了尽可能快地做到这一点,你需要做的是找出花费时间的事情,并且你可以做得更好。我使用1)随机暂停技术,或2)单步技术。
答案 5 :(得分:1)
我看到人们在他们的解决方案中使用了很多差异,但是xor看起来要快得多,并且bool的结果是相同的(一个好的指针也可能是使用diff给出了一个不赞成的警告... 。:)) 这是一个例子:
positive = a2 > 0
np.where(np.bitwise_xor(positive[1:], positive[:-1]))[0]
它测量它的速度大约快一倍半的时间对我来说:)
如果您不关心边缘情况,最好使用
positive = np.signbit(a2)
但是正= a2> 0似乎比signbit更快(更干净)并且检查0(例如,正= np.bitwise_or(np.signbit(a2),np.logical_not(a2))更慢......)
答案 6 :(得分:0)
可能适合某些应用程序的另一种方法是扩展表达式np.diff(np.sign(a))
的评估。
如果我们比较这个表达式对某些情况的反应:
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])
因此,我们必须在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]
这种方法应该是合理的,因为它可以在不使用“慢”循环的情况下进行管理。
这结合了其他几个答案的方法。