我想了解NumPy行为。
当我尝试获取NumPy数组的内部数组的引用,然后将其与对象本身进行比较时,我得到返回值False
。
以下是示例:
In [198]: x = np.array([[1,2,3], [4,5,6]])
In [201]: x0 = x[0]
In [202]: x0 is x[0]
Out[202]: False
另一方面,对于Python本机对象,返回的是True
。
In [205]: c = [[1,2,3],[1]]
In [206]: c0 = c[0]
In [207]: c0 is c[0]
Out[207]: True
我的问题是,NumPy的预期行为是什么?如果是这样,如果我想创建NumPy数组的内部对象的引用,我该怎么办。
答案 0 :(得分:2)
当我第一次写这篇文章时,我构建并索引了一个1d数组。但是OP正在使用2d数组,因此x[0]
是'行',是原始数据的一部分。
In [81]: arr = np.array([[1,2,3], [4,5,6]])
In [82]: arr.__array_interface__['data']
Out[82]: (181595128, False)
In [83]: x0 = arr[0,:]
In [84]: x0.__array_interface__['data']
Out[84]: (181595128, False) # same databuffer pointer
In [85]: id(x0)
Out[85]: 2886887088
In [86]: x1 = arr[0,:] # another slice, different id
In [87]: x1.__array_interface__['data']
Out[87]: (181595128, False)
In [88]: id(x1)
Out[88]: 2886888888
我之前写的关于切片的内容仍然适用。索引单个元素,与arr[0,0]
一样,与1d数组的工作方式相同。
这个2d arr与1d arr.ravel()
具有相同的数据缓冲区;形状和步幅不同。 view
,copy
和item
之间的区别仍然适用。
在C中实现2d数组的常用方法是使用指向其他数组的指针数组。 numpy
采用不同的strided
方法,只使用一个平面数据阵列,并使用shape
和strides
参数来实现横向。因此,子数组需要自己的shape
和strides
以及指向共享数据缓冲区的指针。
我将尝试说明索引数组时发生的事情:
In [51]: arr = np.arange(4)
数组是具有各种属性(如形状和数据缓冲区)的对象。缓冲区将数据存储为字节(在C数组中),而不是Python数字对象。您可以使用以下命令查看有关阵列的信息:
In [52]: np.info(arr)
class: ndarray
shape: (4,)
strides: (4,)
itemsize: 4
aligned: True
contiguous: True
fortran: True
data pointer: 0xa84f8d8
byteorder: little
byteswap: False
type: int32
或
In [53]: arr.__array_interface__
Out[53]:
{'data': (176486616, False),
'descr': [('', '<i4')],
'shape': (4,),
'strides': None,
'typestr': '<i4',
'version': 3}
一个数据指针为十六进制,另一个为十进制。我们通常不会直接引用它。
如果我索引一个元素,我会得到一个新对象:
In [54]: x1 = arr[1]
In [55]: type(x1)
Out[55]: numpy.int32
In [56]: x1.__array_interface__
Out[56]:
{'__ref': array(1),
'data': (181158400, False),
....}
In [57]: id(x1)
Out[57]: 2946170352
它有一些数组的属性,但不是全部。例如,您无法分配给它。另请注意,它的“数据”值完全不同。
从同一个地方做另一个选择 - 不同的ID和不同的数据:
In [58]: x2 = arr[1]
In [59]: id(x2)
Out[59]: 2946170336
In [60]: x2.__array_interface__['data']
Out[60]: (181143288, False)
此外,如果此时更改数组,则不会影响先前的选择:
In [61]: arr[1] = 10
In [62]: arr
Out[62]: array([ 0, 10, 2, 3])
In [63]: x1
Out[63]: 1
x1
和x2
不具有相同的id
,因此不会与is
匹配,并且他们不会使用arr
数据缓冲区。没有任何记录表明该变量来自arr
。
使用slicing
,可以获得原始数组的view
,
In [64]: y = arr[1:2]
In [65]: y.__array_interface__
Out[65]:
{'data': (176486620, False),
'descr': [('', '<i4')],
'shape': (1,),
....}
In [66]: y
Out[66]: array([10])
In [67]: y[0]=4
In [68]: arr
Out[68]: array([0, 4, 2, 3])
In [69]: x1
Out[69]: 1
它的数据指针比arr
大4个字节 - 也就是说,它指向同一个缓冲区,只是一个不同的位置。更改y
确实会更改arr
(但不会更改独立的x1
)。
我甚至可以对这个项目进行0d视图
In [71]: z = y.reshape(())
In [72]: z
Out[72]: array(4)
In [73]: z[...]=0
In [74]: arr
Out[74]: array([0, 0, 2, 3])
在Python代码中,我们通常不会使用这样的对象。当我们使用c-api
或cython
时,可以直接访问数据缓冲区。 nditer
是一种迭代机制,可以像这样使用0d对象(在Python或c-api中)。 cython
typed memoryviews
对于低级别访问特别有用。
http://cython.readthedocs.io/en/latest/src/userguide/memoryviews.html
https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html
https://docs.scipy.org/doc/numpy/reference/c-api.iterator.html#c.NpyIter
回复评论Comparing NumPy object references
np.array([1])== np.array([2])将返回数组([False],dtype = bool)
==
被定义为数组作为元素操作。它比较各个元素的值并返回匹配的布尔数组。
如果需要在标量上下文中使用此类比较(例如if
),则需要将其缩减为单个值,与np.all
或np.any
一样。< / p>
is
测试比较对象ID(不仅仅是numpy对象)。它在实际编码中的价值有限。我经常在像is None
这样的表达式中使用它,其中None
是一个具有唯一id的对象,并且它不能很好地与相等测试一起使用。
答案 1 :(得分:1)
我认为你对Numpy数组缺乏了解。您认为Numpy中的多维数组(如Python列表中)中的子数组是单独的对象,嗯,它们不是。
Numpy数组,无论其大小如何,都只是一个对象。这是因为Numpy在C级创建数组,当它们作为python对象加载时,它不能分解为多个对象。这使得Python可以在您使用split()
,__getitem__
,take()
等属性时创建一个用于保留新零件的新对象,事实上,这就是它的方式该python抽象出Numpy数组的类似列表的行为。
您还可以实时检查瘦身,如下所示:
In [7]: x
Out[7]:
array([[1, 2, 3],
[4, 5, 6]])
In [8]: x[0] is x[0]
Out[8]: False
因此,只要你有一个数组或任何可以在其中包含其他对象的可变对象,你就会有一个python可变对象,因此你将失去性能和所有其他Numpy数组的酷炫功能。
同样如评论中提到的@Imanol,如果要在修改带引用的数组时进行内存优化和灵活操作,可能需要使用Numpy视图对象。 view
个对象可以通过以下两种方式构建:
a.view(some_dtype)
或a.view(dtype=some_dtype)
构建了一个视图 数组的内存具有不同的数据类型。这可能会导致 重新解释内存的字节。
a.view(ndarray_subclass)
或a.view(type=ndarray_subclass)
只会返回 ndarray_subclass的一个实例,它查看相同的数组(相同 形状,dtype等)这不会导致重新解释 存储器中。对于
a.view(some_dtype)
,如果some_dtype
具有不同的字节数 每个条目比上一个dtype
(例如,转换常规 数组到结构化数组),然后视图的行为不能 只是从一个表面的外观预测(显示为print(a)
)。它还取决于a如何存储在内存中。 因此,如果a是C顺序而不是fortran-ordered,则定义为 切片或转置等,视图可能会给出不同的结果。
答案 2 :(得分:0)
不确定目前是否有用,但是numpy.ndarray.ctypes
似乎有用:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html
使用了类似的内容(缺少dtype
,但没错)
def is_same_array(a, b):
return (a.shape == b.shape) and (a == b).all() and a.ctypes.data == b.ctypes.data