28

伙计们,

Numpy 与 python 的不同之处是否有一些陷阱,令人困惑和花费时间的点?

“那一刻的恐怖,我永远不会忘记!”
“不过,你会的,”女王说,“如果你不做备忘录的话。”

例如,NaN 在任何地方总是很麻烦。如果您可以在不运行它的情况下解释这一点,请给自己一点——

from numpy import array, NaN, isnan

pynan = float("nan")
print pynan is pynan, pynan is NaN, NaN is NaN
a = (0, pynan)
print a, a[1] is pynan, any([aa is pynan for aa in a])

a = array(( 0, NaN ))
print a, a[1] is NaN, isnan( a[1] )

(我不是在敲 numpy,那里有很多出色的工作,只是认为常见问题解答或问题 Wiki 会很有用。)

编辑:我希望收集六个陷阱(对于学习 Numpy 的人来说是个惊喜)。
然后,如果有共同的陷阱或者更好的共同解释,我们可以讨论将它们添加到社区 Wiki(在哪里?)到目前为止,我们似乎还不够。

4

15 回答 15

25

因为__eq__不返回布尔值,所以在任何类型的容器中使用 numpy 数组都可以防止在没有容器特定工作的情况下进行相等性测试。

例子:

>>> import numpy
>>> a = numpy.array(range(3))
>>> b = numpy.array(range(3))
>>> a == b
array([ True,  True,  True], dtype=bool)
>>> x = (a, 'banana')
>>> y = (b, 'banana')
>>> x == y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这是一个可怕的问题。例如,您不能为使用TestCase.assertEqual()并且必须编写自定义比较函数的容器编写单元测试。假设我们编写了一个变通函数special_eq_for_numpy_and_tuples。现在我们可以在单元测试中做到这一点:

x = (array1, 'deserialized')
y = (array2, 'deserialized')
self.failUnless( special_eq_for_numpy_and_tuples(x, y) )

现在,我们必须对可能用于存储 numpy 数组的每种容器类型执行此操作。此外,__eq__可能会返回一个 bool 而不是 bool 数组:

>>> a = numpy.array(range(3))
>>> b = numpy.array(range(5))
>>> a == b
False

现在,我们每个特定于容器的相等比较函数也必须处理这种特殊情况。

也许我们可以用一个子类来修补这个疣?

>>> class SaneEqualityArray (numpy.ndarray):
...   def __eq__(self, other):
...     return isinstance(other, SaneEqualityArray) and self.shape == other.shape and (numpy.ndarray.__eq__(self, other)).all()
... 
>>> a = SaneEqualityArray( (2, 3) )
>>> a.fill(7)
>>> b = SaneEqualityArray( (2, 3) )
>>> b.fill(7)
>>> a == b
True
>>> x = (a, 'banana')
>>> y = (b, 'banana')
>>> x == y
True
>>> c = SaneEqualityArray( (7, 7) )
>>> c.fill(7)
>>> a == c
False

这似乎做对了。该类还应该显式导出元素比较,因为这通常很有用。

于 2011-05-19T21:43:35.123 回答
22

我觉得这个很有趣:

>>> import numpy as n
>>> a = n.array([[1,2],[3,4]])
>>> a[1], a[0] = a[0], a[1]
>>> a
array([[1, 2],
       [1, 2]])

另一方面,对于 Python 列表,这可以按预期工作:

>>> b = [[1,2],[3,4]]
>>> b[1], b[0] = b[0], b[1]
>>> b
[[3, 4], [1, 2]]

有趣的旁注:numpy 本身在shuffle函数中有一个错误,因为它使用了该符号:-)(请参见此处)。

原因是在第一种情况下,我们正在处理数组的视图,因此这些值被原地覆盖。

于 2009-08-24T15:58:42.603 回答
22

对我来说最大的问题是几乎每个标准运算符都被重载以分布在整个阵列中。

定义一个列表和一个数组

>>> l = range(10)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> import numpy
>>> a = numpy.array(l)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

乘法复制了 python 列表,但分布在 numpy 数组上

>>> l * 2
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a * 2
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

python列表上没有定义加法和除法

>>> l + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> a + 2
array([ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
>>> l / 2.0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'list' and 'float'
>>> a / 2.0
array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5])

Numpy 重载有时将列表视为数组

>>> a + a
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
>>> a + l
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
于 2009-08-24T21:44:02.520 回答
12

NaN不是单例None,所以你不能真正使用 is 检查它。让它有点棘手的是,这NaN == NaNFalseIEEE-754 所要求的。这就是为什么您需要使用该numpy.isnan()函数来检查浮点数是否不是数字。或者,math.isnan()如果您使用的是 Python 2.6+,则使用标准库。

于 2009-08-24T13:36:41.230 回答
7

切片创建视图,而不是副本。

>>> l = [1, 2, 3, 4]
>>> s = l[2:3]
>>> s[0] = 5
>>> l
[1, 2, 3, 4]

>>> a = array([1, 2, 3, 4])
>>> s = a[2:3]
>>> s[0] = 5
>>> a
array([1, 2, 5, 4])
于 2009-09-12T02:19:46.187 回答
6

Numpy 数组的真值不同于 python 序列类型的真值,其中任何非空序列都为真。

>>> import numpy as np
>>> l = [0,1,2,3]
>>> a = np.arange(4)
>>> if l: print "Im true"
... 
Im true
>>> if a: print "Im true"
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use
a.any() or a.all()
>>>

数值类型在非零时为真,并且作为数字的集合,numpy 数组继承了这个定义。但是对于一组数字,真相可以合理地表示“所有元素都非零”或“至少一个元素非零”。Numpy 拒绝猜测是指哪个定义并引发上述异常。使用.any()and.all()方法可以指定 true 的含义。

>>> if a.any(): print "Im true"
... 
Im true
>>> if a.all(): print "Im true"
... 
>>>
于 2009-09-12T01:45:23.343 回答
6
In [1]: bool([])
Out[1]: False

In [2]: bool(array([]))
Out[2]: False

In [3]: bool([0])
Out[3]: True

In [4]: bool(array([0]))
Out[4]: False

所以不要通过检查数组的真值来测试数组是否为空。使用size(array()).

也不要使用len(array())

In [1]: size(array([]))
Out[1]: 0

In [2]: len(array([]))
Out[2]: 0

In [3]: size(array([0]))
Out[3]: 1

In [4]: len(array([0]))
Out[4]: 1

In [5]: size(array(0))
Out[5]: 1

In [6]: len(array(0))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-5b2872696128> in <module>()
----> 1 len(array(0))

TypeError: len() of unsized object
于 2013-02-20T17:35:11.563 回答
5

(相关,但 NumPy 与 SciPy 的陷阱,而不是 NumPy 与 Python)


超出数组实际大小的切片工作方式不同:

>>> import numpy, scipy.sparse

>>> m = numpy.random.rand(2, 5) # create a 2x5 dense matrix
>>> print m[:3, :] # works like list slicing in Python: clips to real size
[[ 0.12245393  0.20642799  0.98128601  0.06102106  0.74091038]
[ 0.0527411   0.9131837   0.6475907   0.27900378  0.22396443]]

>>> s = scipy.sparse.lil_matrix(m) # same for csr_matrix and other sparse formats
>>> print s[:3, :] # doesn't clip!
IndexError: row index out of bounds

因此,在scipy.sparse对数组进行切片时,您必须手动确保切片边界在范围内。这与 NumPy 和普通 Python 的工作方式不同。

于 2011-07-29T16:21:20.147 回答
3
print pynan is pynan, pynan is NaN, NaN is NaN

这测试身份,即它是否是同一个对象。因此,结果显然应该是 True、False、True,因为当您执行 float(whatever) 时,您正在创建一个新的 float 对象。

a = (0, pynan)
print a, a[1] is pynan, any([aa is pynan for aa in a])

我不知道你对此感到惊讶的是什么。

a = array(( 0, NaN ))
print a, a[1] is NaN, isnan( a[1] )

这是我必须运行的。:-) 当您将 NaN 粘贴到数组中时,它会转换为 numpy.float64 对象,这就是 a[1] is NaN 失败的原因。

这一切对我来说似乎都不足为奇。但是我对 NumPy 的了解并不多。:-)

于 2009-08-24T13:31:25.247 回答
3

到目前为止,似乎没有人提到这一点:

>>> all(False for i in range(3))
False
>>> from numpy import all
>>> all(False for i in range(3))
True
>>> any(False for i in range(3))
False
>>> from numpy import any
>>> any(False for i in range(3))
True

numpy 的any,并且all不能很好地与生成器一起使用,并且不要引发任何错误警告您它们没有。

于 2013-02-20T17:39:45.457 回答
2

来自 Neil Martinsen-Burrell 在9 月 7 日的numpy 讨论中——

Numpy 中可用的 ndarray 类型在概念上并不是 Python 可迭代对象的扩展。如果您想帮助其他 Numpy 用户解决此问题,您可以在 numpy-docs的在线文档编辑器中编辑文档

于 2009-09-11T15:30:19.127 回答
2

我发现将元素列表相乘只会创建元素视图的事实让我感到震惊。

>>> a=[0]*5
>>>a
[0,0,0,0,0]
>>>a[2] = 1
>>>a
[0,0,1,0,0]
>>>b = [np.ones(3)]*5
>>>b
[array([ 1.,  1.,  1.]), array([ 1.,  1.,  1.]), array([ 1.,  1.,  1.]), array([ 1.,  1.,  1.]), array([ 1.,  1.,  1.])]
>>>b[2][1] = 2
>>>b
[array([ 1.,  2.,  1.]), array([ 1.,  2.,  1.]), array([ 1.,  2.,  1.]), array([ 1.,  2.,  1.]), array([ 1.,  2.,  1.])]

因此,如果您创建一个这样的元素列表并打算对它们进行不同的操作,那么您将陷入困境......

一个直接的解决方案是迭代地创建每个数组(使用“for 循环”或列表推导式)或使用更高维数组(例如,这些 1D 数组中的每一个都是 2D 数组中的一行,这通常更快)。

于 2010-09-24T22:39:20.223 回答
2

没有那么大的问题:使用布尔切片,我有时希望我能做到

  x[ 3 <= y < 7 ]

像python双重比较。相反,我必须写

  x[ np.logical_and(3<=y, y<7) ]

(除非你知道更好的东西?)

此外, np.logical_and 和 np.logical_or 每个只接受两个参数,我希望它们接受一个变量号或一个列表,所以我可以输入两个以上的逻辑子句。

(numpy 1.3,也许这在以后的版本中都改变了。)

于 2011-09-12T06:26:24.217 回答
2

*=与以下任务相结合的惊喜numpy.array

>>> from numpy import array

>>> a = array([1, 2, 3])
>>> a *= 1.1  
>>> print(a) 
[1 2 3]  # not quite what we expect or would like to see

>>> print(a.dtype)
int64  # and this is why

>>> a = 1.1 * a  # here, a new array is created
>>> print(a, a.dtype)
[ 1.1  2.2  3.3] float64  # with the expected outcome

令人惊讶,烦人,但可以理解。*=运算符不会改变数据的类型,array因此在这种乘法的传统含义中,将int arraya 与 a相乘float将失败。a = 1; a *= 1.1另一方面,Python 版本按预期工作。

于 2015-01-28T01:43:25.937 回答
1

None 的 0-d 数组看起来像 None 但不一样:

In [1]: print None
None

In [2]: import numpy

In [3]: print numpy.array(None)
None

In [4]: numpy.array(None) is None
Out[4]: False

In [5]: numpy.array(None) == None
Out[5]: False

In [6]: print repr(numpy.array(None))
array(None, dtype=object)
于 2013-08-15T12:21:21.097 回答