NumPy数组中正数和负数岛屿的数量

4
我有一个包含负数和正数元素的数组。一个简化的例子是一个名为a的数组,看起来像这样: array([-3, -2, -1, 1, 2, 3, 4, 5, 6, -5, -4])(a<0).sum()(a>0).sum() 可以给我负数和正数元素的总数,但是如何按顺序计算它们?我的意思是,我想知道我的数组中包含前3个负元素,6个正元素和2个负元素。
这听起来像是一个已经在某个地方被解决的主题,可能已经存在一个重复的答案,但我找不到。
一种方法是在整个数组上循环使用numpy.roll(a,1),并计算出现在具有给定符号的元素数,例如在它滚动时第一个元素中的数,但对我来说,它看起来既不太numpyic(或pythonic),也不是很高效。

这很相似:/questions/42129021/counting-consecutive-1s-in-numpy-array - Julien
输出应该是什么,具体的格式是什么? - Divakar
@Julien 这是一个提示,但不是我想问的内容。@Divakar:输出可能是我提供的示例中的数组array([3,6,2])(我可以轻易地知道输入数组的第一个元素的符号,因此知道输出数组的第一个元素对应的符号)。 - calocedrus
既然我们在讨论效率的问题,我在我的帖子中添加了一个计时部分。 - Divakar
3个回答

3
这里有一种向量化的方法——
def pos_neg_counts(a):
    mask = a>0
    idx = np.flatnonzero(mask[1:] != mask[:-1])
    count = np.concatenate(( [idx[0]+1], idx[1:] - idx[:-1], [a.size-1-idx[-1]] ))
    if a[0]<0:
        return count[1::2], count[::2] # pos, neg counts
    else:
        return count[::2], count[1::2] # pos, neg counts

样例运行 -

In [155]: a
Out[155]: array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])

In [156]: pos_neg_counts(a)
Out[156]: (array([6]), array([3, 2]))

In [157]: a[0] = 3

In [158]: a
Out[158]: array([ 3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])

In [159]: pos_neg_counts(a)
Out[159]: (array([1, 6]), array([2, 2]))

In [160]: a[-1] = 7

In [161]: a
Out[161]: array([ 3, -2, -1,  1,  2,  3,  4,  5,  6, -5,  7])

In [162]: pos_neg_counts(a)
Out[162]: (array([1, 6, 1]), array([2, 1]))

运行时测试

其他方法 -

# @Franz's soln        
def split_app(my_array):
    negative_index = my_array<0
    splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)
    len_list = [len(i) for i in splits]
    return len_list

更大数据集的时间 -


In [20]: # Setup input array
    ...: reps = np.random.randint(3,10,(100000))
    ...: signs = np.ones(len(reps),dtype=int)
    ...: signs[::2] = -1
    ...: a = np.repeat(signs, reps)*np.random.randint(1,9,reps.sum())
    ...: 

In [21]: %timeit split_app(a)
10 loops, best of 3: 90.4 ms per loop

In [22]: %timeit pos_neg_counts(a)
100 loops, best of 3: 2.21 ms per loop

2

只需使用

my_array = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
negative_index = my_array<0

您可以获取负值的索引。然后,您可以将此数组拆分:

splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)

并且还要计算内部数组的大小:
len_list = [len(i) for i in splits]
print(len_list)

你将获得你所寻找的内容:

Out[1]: [3, 6, 2]

您只需要说明您的第一个元素是什么。根据我的代码定义,它是负一。

所以只需执行:

my_array = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
negative_index = my_array<0
splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)
len_list = [len(i) for i in splits]
print(len_list)

1
你可以使用negative_index = np.signbit(my_array)代替negative_index = my_array<0,这样会更快。 - Daniel F
我不知道 np.signbit()。谢谢,这很有帮助。 - Franz
@Franz,你的解决方案输出了我想要得到的结果,但Divakar的在我的2D数组上运行速度更快,并且有将正数和负数元素清晰分开的优势(实际上从你的解决方案中也很容易获得)。 - calocedrus

0

我的(相对简单且可能效率不高的)解决方案是:

import numpy as np
arr = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
sgn = np.sign(arr[0])
res = []
cntr = 1 # counting the first one
for i in range(1, len(arr)):
 if np.sign(arr[i]) != sgn:
  res.append(cntr)
  cntr = 0
  sgn *= -1
 cntr += 1
res.append(cntr)
print res

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接