用于计算数组

时间:2015-05-16 06:27:24

标签: python arrays performance distribution

我希望计算数组中的值改变极性的次数(编辑:数组中的值交叉为零的次数)。

假设我有一个数组:

[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多个这样的数组上进行这些计算。

6 个答案:

答案 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's answer

Scott提出的生成器表达式使用enumerate返回包含索引和列表项的元组。列表项根本不在表达式中使用,稍后会丢弃。因此,在时间方面更好的解决方案是

sum(1 for i in range(1, len(a)) if a[i-1]*a[i]<0)

如果您的列表a非常庞大,range可能会引发异常。您可以将其替换为itertools.isliceitertools.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))])