如何在Numpy数组中计算特定范围内的值?

47
我有一个 NumPy 数组,我想要统计这些值中有多少个在特定范围内,例如 x<100 和 x>25。我已经了解过 counter,但似乎只适用于特定的值而不是数值范围。我搜索过,但没有找到与我的问题相关的任何内容。如果有人可以指向正确的文档,我将不胜感激。谢谢。
我已经尝试过这个。
   X = array(X)
   for X in range(25, 100):
       print(X)

但它只给出25到99之间的数字。

编辑 我使用的数据是由另一个程序创建的。然后我使用脚本读取数据并将其存储为列表。然后我取出该列表并使用array(r)将其转换为数组。

编辑

运行结果是

 >>> a[0:10]
 array(['29.63827346', '40.61488812', '25.48300065', '26.22910525',
   '42.41172923', '20.15013315', '34.95323355', '13.03604098',
   '29.71097606', '9.53222141'], 
  dtype='<U11')

@Senderle,做到了,非常感谢!!在重新转换数组后,我尝试了Sven的方法,它完美地起作用了!再次感谢。 - Stripers247
5个回答

91
如果您的数组名为a,满足25 < x < 100条件的元素数量为。
((25 < a) & (a < 100)).sum()
表达式 (25 < a) & (a < 100) 的结果是一个布尔数组,与 a 具有相同的形状,对于满足条件的所有元素,其值均为True。 将这个布尔数组求和时,将True值视为1,将False值视为0

1
@Sven 我尝试了你的方法,但是出现了这个错误“TypeError: unorderable types: int() < numpy.ndarray()”。 - Stripers247
1
@Surfcast23:对我来说可以工作。 你使用的NumPy和Python版本是什么? - Sven Marnach
在我的电脑上,Python 3.2.2和numpy 1.6.1都可以正常运行。 - DSM
3
哦,我知道可能发生了什么。如果数据类型(dtype)是意外的类型,比如说 2 < numpy.array(range(10), dtype=str),就会出现无法排序类型(unorderable types)的错误。 - DSM
+1:这是我能想到的最节省内存的解决方案,当需要速度和简洁的NumPy代码时。 - Eric O. Lebigot
显示剩余3条评论

12

在Sven的好方法的基础上,你还可以稍微更明确一些:

numpy.count_nonzero((25 < a) & (a < 100))

首先创建一个布尔类型的数组,数组中每个元素对应数组a中的一个输入数字,然后计算非False值(即True)的数量(这给出匹配数字的数量)。

但需要注意的是,在包含10万个数字的数组上,此方法比Sven的.sum()方法慢两倍(NumPy 1.6.1,Python 2.7.3),大约是300微秒与150微秒的差距。


11

您可以使用histogram。以下是一个基本用法示例:

>>> import numpy
>>> a = numpy.random.random(size=100) * 100 
>>> numpy.histogram(a, bins=(0.0, 7.3, 22.4, 55.5, 77, 79, 98, 100))
(array([ 8, 14, 34, 31,  0, 12,  1]), 
 array([   0. ,    7.3,   22.4,   55.5,   77. ,   79. ,   98. ,  100. ]))

在您的特定情况下,它将看起来像这样:

>>> numpy.histogram(a, bins=(25, 100))
(array([73]), array([ 25, 100]))

此外,当你拥有一个字符串列表时,你必须明确指定类型,以便 numpy 知道生成的是浮点数数组而不是字符串列表。

>>> strings = [str(i) for i in range(10)]
>>> numpy.array(strings)
array(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 
      dtype='|S1')
>>> numpy.array(strings, dtype=float)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

@Surfcast23,是的,这个更通用,但如果你只需要一个bin,Sven的会更快。 - senderle
我运行了代码并得到了 (array([-481], dtype=int32), array([ 25, 100]))。让我担心的是负号,我该如何解释它? - Stripers247
@Surfcast23,出现了很多奇怪的结果。我开始认为你对数据没有告诉我们所有的事情。这个数组的dtype是什么? - senderle
@ Senderle 这里的数据只是一组浮点数数组,以下是前几个值: 40.61488812 25.48300065 26.22910525 42.41172923 20.15013315 34.95323355 13.03604098 29.71097606 9.53222141 13.08244932 38.04509923 20.16046549 29.40530862 - Stripers247

6
如果您不希望进一步处理匹配值,则Sven的回答是解决方案。以下两个示例仅返回具有匹配值的副本:
np.compress((25 < a) & (a < 100), a).size

或者:
a[(25 < a) & (a < 100)].size

示例解释器会话:

>>> import numpy as np
>>> a = np.random.randint(200,size=100)
>>> a
array([194, 131,  10, 100, 199, 123,  36,  14,  52, 195, 114, 181, 138,
       144,  70, 185, 127,  52,  41, 126, 159,  39,  68, 118, 124, 119,
        45, 161,  66,  29, 179, 194, 145, 163, 190, 150, 186,  25,  61,
       187,   0,  69,  87,  20, 192,  18, 147,  53,  40, 113, 193, 178,
       104, 170, 133,  69,  61,  48,  84, 121,  13,  49,  11,  29, 136,
       141,  64,  22, 111, 162, 107,  33, 130,  11,  22, 167, 157,  99,
        59,  12,  70, 154,  44,  45, 110, 180, 116,  56, 136,  54, 139,
        26,  77, 128,  55, 143, 133, 137,   3,  83])
>>> np.compress((25 < a) & (a < 100),a).size
34
>>> a[(25 < a) & (a < 100)].size
34

以上示例使用“按位与”(&)对两个布尔数组进行逐元素的计算,用于比较目的。
另一种编写Sven的优秀答案的方法是:
np.bitwise_and(25 < a, a < 100).sum() 

布尔数组在条件匹配时包含True值,在不匹配时包含False值。
布尔值的一个额外优点是,True等价于1,False等价于0。

@Sevn 和 Adam,我对 Python 还是很新的,大概 6 个月了,但学习也不是很一贯。你们可以解释一下你们的脚本是如何运行的以及为什么有效吗?谢谢。 - Stripers247
@Surfcast23:我添加了一点解释。继续加油! - mechanical_meat
谢谢你提出这个好问题。看到如此广泛的方法和解决方案真是太有趣了。 - mechanical_meat
1
在这里使用numpy.logical_and()似乎更自然,而不是numpy.bitwise_and()。结果将是相同的,但感觉上更符合“概念上的正确性”。 - Sven Marnach
与Sven的解决方案相反,这创建了一个额外的数组,它消耗了大量的内存却没有太多好处。 - Eric O. Lebigot
@SvenMarnach。假设我想对数组中范围内的值进行进一步处理,而不仅仅是计数。代码应该怎么写? - Gathide

2

我认为@Sven Marnach的回答非常好,因为它操作的是numpy数组本身,这将快速高效(C实现)。

我喜欢把测试放在一个条件中,比如25 < x < 100,所以我可能会这样做:

len([x for x in a.ravel() if 25 < x < 100])


1
不错。使用生成表达式:sum(1 for i in a.ravel() if 25 < i < 100) - mechanical_meat
起初我尝试在生成器上使用 len(),但令我惊讶的是它不起作用。 - wim
@wim:len()不能与生成器表达式一起使用,因为迭代器通常没有固定的长度。这就是为什么sum(1...)方法更好:它具有固定且更小的内存占用,因为您不必创建中间列表。 - Eric O. Lebigot

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