Python 计算数组中过零次数的代码
Python code for counting number of zero crossings in an array
我想计算数组中的值改变极性的次数(编辑:数组中的值越过零的次数)。
假设我有一个数组:
[80.6 120.8 -115.6 -76.1 131.3 105.1 138.4 -81.3
-95.3 89.2 -154.1 121.4 -85.1 96.8 68.2]`
我希望计数为 8。
一个解决方案是 运行 循环并检查是否大于或小于 0,并保留先前极性的历史记录。
我们可以做得更快吗?
编辑:我的目的真的是找到更快的东西,因为我有这些长度约为 68554308 的数组,我必须对 100 多个这样的数组进行这些计算。
我认为循环是一种直接的方式:
a = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
def change_sign(v1, v2):
return v1 * v2 < 0
s = 0
for ind, _ in enumerate(a):
if ind+1 < len(a):
if change_sign(a[ind], a[ind+1]):
s += 1
print s # prints 8
您可以使用生成器表达式,但它变得很丑陋:
z_cross = sum(1 for ind, val in enumerate(a) if (ind+1 < len(a))
if change_sign(a[ind], a[ind+1]))
print z_cross # prints 8
编辑:
@Alik 指出,对于巨大的列表,space 和时间(至少在我们考虑过的解决方案之外)的最佳选择不是在生成器表达式中调用 change_sign
,而是简单地做:
z_cross = sum(1 for i, _ in enumerate(a) if (i+1 < len(a)) if a[i]*a[i+1]<0)
您似乎想按符号对数字进行分组。这可以使用内置方法 groupby
:
来完成
In [2]: l = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
In [3]: from itertools import groupby
In [5]: list(groupby(l, lambda x: x < 0))
Out[5]:
[(False, <itertools._grouper at 0x7fc9022095f8>),
(True, <itertools._grouper at 0x7fc902209828>),
(False, <itertools._grouper at 0x7fc902209550>),
(True, <itertools._grouper at 0x7fc902209e80>),
(False, <itertools._grouper at 0x7fc902209198>),
(True, <itertools._grouper at 0x7fc9022092e8>),
(False, <itertools._grouper at 0x7fc902209240>),
(True, <itertools._grouper at 0x7fc902209908>),
(False, <itertools._grouper at 0x7fc9019a64e0>)]
那么你应该使用函数 len
其中 returns 组数:
In [7]: len(list(groupby(l, lambda x: x < 0)))
Out[7]: 9
显然,至少会有一组(对于非空列表),但是如果你想计算序列改变其极性的点数,你可以只减去一组。不要忘记空列表的情况。
你还应该注意零元素:它们不应该被提取到另一个组中吗?如果是这样,您可以只更改 groupby
函数的 key
参数(lambda 函数)。
您可以使用列表理解来实现它:
myList = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
len([x for i, x in enumerate(myList) if i > 0 and ((myList[i-1] > 0 and myList[i] < 0) or (myList[i-1] < 0 and myList[i] > 0))])
这是一个 numpy
解决方案。 Numpy 的方法通常非常快速且经过优化,但如果您还没有使用 numpy
,那么将列表转换为 numpy
数组可能会产生一些开销:
import numpy as np
my_list = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
(np.diff(np.sign(my_list)) != 0).sum()
Out[8]: 8
基于
Scott 提出的生成器表达式使用 enumerate
其中 returns 个包含索引和列表项的元组。列表项根本不在表达式中使用,稍后将被丢弃。所以就时间而言更好的解决方案是
sum(1 for i in range(1, len(a)) if a[i-1]*a[i]<0)
如果您的列表 a
确实很大,range
可能会抛出异常。您可以将其替换为 itertools.islice
和 itertools.count
.
在 Python 版本 2.x 中,使用 xrange
代替 Python 3 的 range
。
在 Python 3 中,xrange
不再可用。
这会产生相同的结果:
import numpy as np
my_array = np.array([80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3,
89.2, -154.1, 121.4, -85.1, 96.8, 68.2])
((my_array[:-1] * my_array[1:]) < 0).sum()
给出:
8
似乎是最快的解决方案:
%timeit ((my_array[:-1] * my_array[1:]) < 0).sum()
100000 loops, best of 3: 11.6 µs per loop
与迄今为止最快的相比:
%timeit (np.diff(np.sign(my_array)) != 0).sum()
10000 loops, best of 3: 22.2 µs per loop
也适用于更大的数组:
big = np.random.randint(-10, 10, size=10000000)
这个:
%timeit ((big[:-1] * big[1:]) < 0).sum()
10 loops, best of 3: 62.1 ms per loop
对比:
%timeit (np.diff(np.sign(big)) != 0).sum()
1 loops, best of 3: 97.6 ms per loop
我想计算数组中的值改变极性的次数(编辑:数组中的值越过零的次数)。
假设我有一个数组:
[80.6 120.8 -115.6 -76.1 131.3 105.1 138.4 -81.3
-95.3 89.2 -154.1 121.4 -85.1 96.8 68.2]`
我希望计数为 8。
一个解决方案是 运行 循环并检查是否大于或小于 0,并保留先前极性的历史记录。
我们可以做得更快吗?
编辑:我的目的真的是找到更快的东西,因为我有这些长度约为 68554308 的数组,我必须对 100 多个这样的数组进行这些计算。
我认为循环是一种直接的方式:
a = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
def change_sign(v1, v2):
return v1 * v2 < 0
s = 0
for ind, _ in enumerate(a):
if ind+1 < len(a):
if change_sign(a[ind], a[ind+1]):
s += 1
print s # prints 8
您可以使用生成器表达式,但它变得很丑陋:
z_cross = sum(1 for ind, val in enumerate(a) if (ind+1 < len(a))
if change_sign(a[ind], a[ind+1]))
print z_cross # prints 8
编辑:
@Alik 指出,对于巨大的列表,space 和时间(至少在我们考虑过的解决方案之外)的最佳选择不是在生成器表达式中调用 change_sign
,而是简单地做:
z_cross = sum(1 for i, _ in enumerate(a) if (i+1 < len(a)) if a[i]*a[i+1]<0)
您似乎想按符号对数字进行分组。这可以使用内置方法 groupby
:
In [2]: l = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
In [3]: from itertools import groupby
In [5]: list(groupby(l, lambda x: x < 0))
Out[5]:
[(False, <itertools._grouper at 0x7fc9022095f8>),
(True, <itertools._grouper at 0x7fc902209828>),
(False, <itertools._grouper at 0x7fc902209550>),
(True, <itertools._grouper at 0x7fc902209e80>),
(False, <itertools._grouper at 0x7fc902209198>),
(True, <itertools._grouper at 0x7fc9022092e8>),
(False, <itertools._grouper at 0x7fc902209240>),
(True, <itertools._grouper at 0x7fc902209908>),
(False, <itertools._grouper at 0x7fc9019a64e0>)]
那么你应该使用函数 len
其中 returns 组数:
In [7]: len(list(groupby(l, lambda x: x < 0)))
Out[7]: 9
显然,至少会有一组(对于非空列表),但是如果你想计算序列改变其极性的点数,你可以只减去一组。不要忘记空列表的情况。
你还应该注意零元素:它们不应该被提取到另一个组中吗?如果是这样,您可以只更改 groupby
函数的 key
参数(lambda 函数)。
您可以使用列表理解来实现它:
myList = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
len([x for i, x in enumerate(myList) if i > 0 and ((myList[i-1] > 0 and myList[i] < 0) or (myList[i-1] < 0 and myList[i] > 0))])
这是一个 numpy
解决方案。 Numpy 的方法通常非常快速且经过优化,但如果您还没有使用 numpy
,那么将列表转换为 numpy
数组可能会产生一些开销:
import numpy as np
my_list = [80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3, 89.2, -154.1, 121.4, -85.1, 96.8, 68.2]
(np.diff(np.sign(my_list)) != 0).sum()
Out[8]: 8
基于
Scott 提出的生成器表达式使用 enumerate
其中 returns 个包含索引和列表项的元组。列表项根本不在表达式中使用,稍后将被丢弃。所以就时间而言更好的解决方案是
sum(1 for i in range(1, len(a)) if a[i-1]*a[i]<0)
如果您的列表 a
确实很大,range
可能会抛出异常。您可以将其替换为 itertools.islice
和 itertools.count
.
在 Python 版本 2.x 中,使用 xrange
代替 Python 3 的 range
。
在 Python 3 中,xrange
不再可用。
这会产生相同的结果:
import numpy as np
my_array = np.array([80.6, 120.8, -115.6, -76.1, 131.3, 105.1, 138.4, -81.3, -95.3,
89.2, -154.1, 121.4, -85.1, 96.8, 68.2])
((my_array[:-1] * my_array[1:]) < 0).sum()
给出:
8
似乎是最快的解决方案:
%timeit ((my_array[:-1] * my_array[1:]) < 0).sum()
100000 loops, best of 3: 11.6 µs per loop
与迄今为止最快的相比:
%timeit (np.diff(np.sign(my_array)) != 0).sum()
10000 loops, best of 3: 22.2 µs per loop
也适用于更大的数组:
big = np.random.randint(-10, 10, size=10000000)
这个:
%timeit ((big[:-1] * big[1:]) < 0).sum()
10 loops, best of 3: 62.1 ms per loop
对比:
%timeit (np.diff(np.sign(big)) != 0).sum()
1 loops, best of 3: 97.6 ms per loop