我希望计算数组中的值改变极性的次数(编辑:数组中的值交叉为零的次数)。
假设我有一个数组:
[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多个这样的数组上进行这些计算。
答案 0 :(得分:9)
这会产生相同的结果:
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
VS
%timeit (np.diff(np.sign(big)) != 0).sum()
1 loops, best of 3: 97.6 ms per loop
答案 1 :(得分:5)
这是一个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
答案 2 :(得分:2)
Scott提出的生成器表达式使用enumerate
返回包含索引和列表项的元组。列表项根本不在表达式中使用,稍后会丢弃。因此,在时间方面更好的解决方案是
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
不再可用。
答案 3 :(得分:1)
我认为循环是一种直接的方式:
str = str.Replace(ControlChars.Lf, "%n%")
str = str.Replace(ControlChars.Cr, "%n%")
str = str.Replace("%n%%n%", "%n%")
str = str.Replace("%n%", ControlChars.NewLine)
您可以使用生成器表达式,但它会变得丑陋:
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
修改强>
@Alik指出,对于大型列表,空间和时间的最佳选择(至少在我们考虑过的解决方案中)不是在生成器表达式中调用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
而是简单地执行:
change_sign
答案 4 :(得分: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
来返回组的数量:
In [7]: len(list(groupby(l, lambda x: x < 0)))
Out[7]: 9
显然,至少会有一个组(对于非空列表),但如果要计算点数,序列改变其极性,则可以减去一个组。不要忘记清单案例。
您还应该注意零元素:不应该将它们提取到另一个组中吗?如果是这样,您可以只更改key
函数的groupby
参数(lambda函数)。
答案 5 :(得分:0)
你可以使用列表理解来实现它:
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))])