为什么Python会复制尺寸长度相同的NumPy数组?

sho*_*lli 36 python numpy

我在引用NumPy数组时遇到问题.我有一个表格的数组

import numpy as np
a = [np.array([0.0, 0.2, 0.4, 0.6, 0.8]),
     np.array([0.0, 0.2, 0.4, 0.6, 0.8]),
     np.array([0.0, 0.2, 0.4, 0.6, 0.8])]
Run Code Online (Sandbox Code Playgroud)

如果我现在创建一个新变量,

b = np.array(a)
Run Code Online (Sandbox Code Playgroud)

并做

b[0] += 1
print(a)
Run Code Online (Sandbox Code Playgroud)

然后a没有改变.

a = [array([0. , 0.2, 0.4, 0.6, 0.8]),
     array([0. , 0.2, 0.4, 0.6, 0.8]),
     array([0. , 0.2, 0.4, 0.6, 0.8])]
Run Code Online (Sandbox Code Playgroud)

但如果我做同样的事情:

a = [np.array([0.0, 0.2, 0.4, 0.6, 0.8]),
     np.array([0.0, 0.2, 0.4, 0.6, 0.8]),
     np.array([0.0, 0.2, 0.4, 0.6])]
Run Code Online (Sandbox Code Playgroud)

所以我在最后一个维度的末尾删除了一个数字.然后我再做一次:

b = np.array(a)
b[0] += 1
print(a)
Run Code Online (Sandbox Code Playgroud)

现在a正在改变,我认为是Python中的正常行为.

a = [array([1. , 1.2, 1.4, 1.6, 1.8]),
     array([0. , 0.2, 0.4, 0.6, 0.8]),
     array([0. , 0.2, 0.4, 0.6])]
Run Code Online (Sandbox Code Playgroud)

谁能解释一下这个?

hpa*_*ulj 24

In [1]: a = [np.array([0.0, 0.2, 0.4, 0.6, 0.8]), 
   ...:      np.array([0.0, 0.2, 0.4, 0.6, 0.8]), 
   ...:      np.array([0.0, 0.2, 0.4, 0.6, 0.8])]                               
In [2]:                                                                         
In [2]: a                                                                       
Out[2]: 
[array([0. , 0.2, 0.4, 0.6, 0.8]),
 array([0. , 0.2, 0.4, 0.6, 0.8]),
 array([0. , 0.2, 0.4, 0.6, 0.8])]
Run Code Online (Sandbox Code Playgroud)

a是一个数组列表. b是一个二维数组.

In [3]: b = np.array(a)                                                         
In [4]: b                                                                       
Out[4]: 
array([[0. , 0.2, 0.4, 0.6, 0.8],
       [0. , 0.2, 0.4, 0.6, 0.8],
       [0. , 0.2, 0.4, 0.6, 0.8]])
In [5]: b[0] += 1                                                               
In [6]: b                                                                       
Out[6]: 
array([[1. , 1.2, 1.4, 1.6, 1.8],
       [0. , 0.2, 0.4, 0.6, 0.8],
       [0. , 0.2, 0.4, 0.6, 0.8]])
Run Code Online (Sandbox Code Playgroud)

b从中获取值a但不包含任何a对象.这个基础数据结构与列表b非常不同a.如果不清楚,您可能需要查看numpy基础知识(谈论形状,步幅和数据缓冲区).

在第二种情况下,b是一个对象数组,包含与以下相同的对象a:

In [8]: b = np.array(a)                                                         
In [9]: b                                                                       
Out[9]: 
array([array([0. , 0.2, 0.4, 0.6, 0.8]), array([0. , 0.2, 0.4, 0.6, 0.8]),
       array([0. , 0.2, 0.4, 0.6])], dtype=object)
Run Code Online (Sandbox Code Playgroud)

b表现得很像a- 都包含数组.

该对象数组的构造与2d数字数组完全不同.我认为数值数组是默认的,或正常的,numpy行为,而对象数组是一个"让步",给我们一个有用的工具,但是它没有多维数组的计算能力.

很容易错误地制作一个对象数组 - 有人说太容易了.通过设计可靠地制造一个可能更难.与原始的例子a,我们必须做:

In [17]: b = np.empty(3, object)                                                
In [18]: b[:] = a[:]                                                            
In [19]: b                                                                      
Out[19]: 
array([array([0. , 0.2, 0.4, 0.6, 0.8]), array([0. , 0.2, 0.4, 0.6, 0.8]),
       array([0. , 0.2, 0.4, 0.6, 0.8])], dtype=object)
Run Code Online (Sandbox Code Playgroud)

甚至 for i in range(3): b[i] = a[i]


cs9*_*s95 15

简而言之,这是您的数据的结果.你会注意到它的工作/不起作用(取决于你如何查看),因为你的数组大小不同.

对于相同大小的子阵列,可以将元件紧凑地加载到存储器有效方案中,其中任何ND阵列可以由存储器中的紧凑1-D阵列表示.然后,NumPy在内部处理多维索引到1D索引的转换.例如,2D阵列的索引[i,j]将映射到i*N + j(如果以行主格式存储).原始数组列表中的数据被复制到一个紧凑的1D数组中,因此对此数组所做的任何修改都不会影响原始数组.

使用不规则的列表/数组,这是不可能的.该数组实际上是一个python列表,其中每个元素都是一个python对象.为了提高效率,只复制对象引用而不是数据.这就是为什么你可以改变第二种情况下的原始列表元素而不是第一种情况.


use*_*ica 11

在第一种情况下,NumPy看到输入numpy.array可以解释为3x5,2维数组,所以它就是这样.结果是一个新的float64 dtype数组,输入数据被复制到其中,与输入对象无关.b[0]是新数组的第一行视图,完全独立a[0],并且修改b[0]不影响a[0].

在第二种情况下,由于子阵列的长度不相等,因此输入不能被解释为二维阵列.但是,考虑到子阵列是不透明的对象,列表可以被解释为对象的一维数组,这是NumPy所依赖的解释.numpy.array调用的结果是对象dtype的一维数组,包含对作为输入列表元素的数组对象的引用.b[0]是同一个数组对象a[0],并且b[0] += 1变异该对象.

这种长度依赖性是在NumPy 中尝试制作锯齿状数组或数组数组是一个非常非常糟糕的理由的众多原因之一.说真的,不要这样做.