Numpy与普通Python的不同之处?

29 投票
15 回答
11640 浏览
提问于 2025-04-15 13:50

大家好,

有没有一些关于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,那里有很多好的工作,只是觉得如果有一个常见问题或注意事项的汇总会很有用。)

编辑:我希望能收集到六个左右的注意事项(让学习Numpy的人感到惊讶的地方)。
如果有一些常见的注意事项,或者更好的是常见的解释,我们可以讨论把它们添加到一个社区Wiki(在哪里呢?)看起来目前我们还没有足够的内容。

15 个回答

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函数中也有一个bug,因为它用了那种写法 :-) (可以查看这里)。

原因在于,在第一个例子中,我们处理的是数组的视图,所以值是在原地被覆盖的。

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__ 可能返回一个布尔值,而不是布尔数组:

>>> 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

这样似乎能解决问题。这个类还应该明确提供逐元素比较的功能,因为这通常是很有用的。

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])

撰写回答