Numpy与直蟒不同的地方?

时间:2009-08-24 13:21:37

标签: python numpy

民间,

是否存在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就会有用。)

编辑:我希望能收集到6个陷阱(人们学习Numpy的惊喜)。
然后,如果有共同的陷阱或更好的常见解释, 我们可以谈谈将它们添加到社区Wiki(在哪里?) 到目前为止看起来我们还不够。

15 个答案:

答案 0 :(得分:24)

因为__eq__没有返回bool,所以在任何类型的容器中使用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

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

答案 1 :(得分: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])

答案 2 :(得分:21)

我认为这个很有趣:

>>> 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函数中有一个错误,因为它使用了这种符号:-)(参见here)。

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

答案 3 :(得分:12)

NaN不是None之类的单身,所以你不能真正使用它来检查它。令人有点棘手的是,NaN == NaNFalse,正如IEEE-754所要求的那样。这就是为什么你需要使用numpy.isnan()函数来检查浮点数是否不是数字。或者标准库math.isnan()如果你使用的是Python 2.6 +。

答案 4 :(得分: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])

答案 5 :(得分: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().all()方法可以指定true的含义。

>>> if a.any(): print "Im true"
... 
Im true
>>> if a.all(): print "Im true"
... 
>>>

答案 6 :(得分: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

答案 7 :(得分: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的工作方式不同。

答案 8 :(得分:3)

print pynan is pynan, pynan is NaN, NaN is NaN

这会测试身份,即它是否是同一个对象。因此结果显然应该是True,False,True,因为当你浮动(无论如何)时,你正在创建一个新的浮动对象。

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对象,这就是[1] NaN失败的原因。

这对我来说似乎都不足为奇。但后来我对NumPy一无所知。 : - )

答案 9 :(得分: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的anyall不能很好地与发电机配合使用,并且不会发出任何错误警告你他们没有。

答案 10 :(得分:2)

来自Neil Martinsen-Burrell,numpy-discussion 9月7日 -

  

Numpy中提供的ndarray类型是   不是概念上的延伸   Python的迭代。如果你愿意的话   帮助其他Numpy用户   问题,您可以编辑文档   在线文档编辑器中   numpy-docs

答案 11 :(得分: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数组中的一行,这通常是快)。

答案 12 :(得分:2)

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

  x[ 3 <= y < 7 ]
像python双重比较一样。相反,我必须写

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

(除非你知道更好的事情吗?)

另外,np.logical_and和np.logical_or每个只有两个参数,我希望它们采用一个变量号或一个列表,所以我可以提供的不只是两个逻辑子句。

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

答案 13 :(得分: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 array乘以float将会失败,这与传统意义相同乘法。另一方面,Python版本a = 1; a *= 1.1按预期工作。

答案 14 :(得分:1)

无的0-d数组看起来像无,但它不相同:

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)