Numpy与普通Python的不同之处?
大家好,
有没有一些关于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 个回答
我觉得这个挺有趣的:
>>> 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,因为它用了那种写法 :-) (可以查看这里)。
原因在于,在第一个例子中,我们处理的是数组的视图,所以值是在原地被覆盖的。
因为 __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
这样似乎能解决问题。这个类还应该明确提供逐元素比较的功能,因为这通常是很有用的。
对我来说,最大的一个坑就是几乎所有的标准运算符都被重载了,可以在数组上进行操作。
先定义一个列表和一个数组
>>> 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])