【问题标题】:Understanding references: why this Numpy assignment is not working?理解参考:为什么这个 Numpy 分配不起作用?
【发布时间】:2021-01-17 17:12:32
【问题描述】:

我有一个像这样的小测试代码:

import numpy as np

foo = np.zeros(1, dtype=int)
bar = np.zeros((10, 1), dtype=int)


foo_copy = np.copy(foo)
bar[-1] = foo_copy

foo_copy[-1] = 10

print(foo_copy)
print(bar)

我原以为foo_copybar 的最后一个元素都包含值10,但bar 的最后一个元素仍然是一个np 数组,其中的值为0。

[10]
[[0]
 [0]
 [0]
 [0]
 [0]
 [0]
 [0]
 [0]
 [0]
 [0]]  # <<--- why not 10?

最后一个元素不是指向foo_copy吗?

或者在所有作业中,np 都会复制数据而我无法使用原始 ndarray 来更改它?

如果是这样,有没有办法将最后一个元素保留为指向 foo_bar 的指针?

【问题讨论】:

  • numpy.ndarray不保留指针。这就是他们的全部目的,为原始数值的真正多维数组提供高效、快速的实现。您也许可以通过使用dtype=object 来破解某些东西,这基本上意味着 dtype 是 PyObject 指针,尽管您可能真的不应该这样做。如果是这种情况,您应该几乎只使用常规的 python 列表,因为带有 dtype=object 的 numpy 数组几乎是一个效率较低的 python 列表。
  • IOW,当你想要列表语义时,为什么要使用数组?
  • @juanpa.arrivillaga 这只是一个典型的例子,在我的代码中我确实需要高性能,但并不清楚这一点。你想把它作为答案吗?那我就可以接受了。谢谢!
  • 但是你会失去 numpy 的高性能这种性能来自于不使用指向 Python 对象的指针。如果您使用指向 Python 对象的指针,那么您将再次获得一个效率较低的 Python 列表。无论如何,我可以发誓我已经看到了另一个问题,它详细解释了这一点,试图找到它以将其作为重复目标提供
  • numpy 中的高性能来自于使用编译方法。那些主要使用数字 dtypes,例如int。您可能需要(重新)阅读numpy 基础知识,重点关注数组的存储方式。 bar 的元素是整数,而不是数组(或列表)。

标签: python numpy numpy-ndarray


【解决方案1】:

numpy 数组具有数值,而不是引用(至少对于数字 dtypes):

制作一维数组,并将其重塑为二维:

In [64]: bar = np.arange(12).reshape(4,3)
In [65]: bar
Out[65]: 
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

另一个一维数组:

In [66]: foo = np.array([10])
In [67]: foo
Out[67]: array([10])

此分配是按值分配的:

In [68]: bar[1,1] = foo
In [69]: bar
Out[69]: 
array([[ 0,  1,  2],
       [ 3, 10,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

也是这样,尽管整行的值为broadcasted

In [70]: bar[2] = foo
In [71]: bar
Out[71]: 
array([[ 0,  1,  2],
       [ 3, 10,  5],
       [10, 10, 10],
       [ 9, 10, 11]])

我们可以view 二维数组作为一维数组。这是值实际存储方式的更接近的表示(但在 c 字节数组中,12*8 字节长):

In [72]: bar1 = bar.ravel()
In [73]: bar1
Out[73]: array([ 0,  1,  2,  3, 10,  5, 10, 10, 10,  9, 10, 11])

改变view的一个元素会改变2d的对应元素:

In [74]: bar1[3] = 30
In [75]: bar
Out[75]: 
array([[ 0,  1,  2],
       [30, 10,  5],
       [10, 10, 10],
       [ 9, 10, 11]])

虽然我们可以创建 object dtype 数组,它像列表一样存储引用,但它们没有任何性能优势。

包含bar'原始数据'的字节串:

In [76]: bar.tobytes()
Out[76]: b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00'

传说中的numpy 速度来自于使用已编译的c 代码处理这些原始数据。使用 Python 代码访问单个元素相对较慢。像bar*3 这样的全数组操作速度很快。

【讨论】:

  • 非常感谢!这非常有用。
猜你喜欢
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
相关资源
最近更新 更多