首页 > 其他分享 >numpy 数组 的 视图和副本

numpy 数组 的 视图和副本

时间:2023-10-07 11:38:23浏览次数:25  
标签:arr 副本 视图 base 数组 print numpy view

numpy 数组 的 视图

https://zhuanlan.zhihu.com/p/199615109?utm_id=0

https://finthon.com/numpy-arrayview-copy/

https://blog.csdn.net/weixin_44226181/article/details/128401161

 

==================================

在编程的过程中很可能会使用到原数组,这就涉及到视图和副本的概念,简单来说视图与副本是使用原数组的两种不同的方式。

import numpy as np

a = np.arange(4)

>>> print(a)
[0 1 2 3]
>>> print(a[1:3])
[1 2]
>>> print(a[[1, 2]])
[1 2]

# base属性
>>> print(a.base)
None
>>> print(a[1:3].base)
[0 1 2 3]
>>> print(a[[1, 2]].base)
None

# flags.owndata属性
>>> print(a.flags.owndata)
True
>>> print(a[1:3].flags.owndata)
False
>>> print(a[[1, 2]].flags.owndata)
True

虽然a[1:3]a[[1, 2]]的输出结果都是 [1 2],但是从baseflags.owndata两个属性的输出结果来看,a[1:3]a[[1, 2]]还是有一些差别的。a[1:3]得到的是原数组的视图,而a[[1, 2]]得到的是原数组的副本。那baseflags.owndata到底表示什么呢?

base与flags.owndata

下图是 Numpy 数组的内部结构组成。

其中可以分为数组数据结构信息区以及数据存储区。简单来说,数组数据结构信息区中有 Numpy 数组的形状(shape)以及数据类型(data-type)等信息,而数据存储区则是用于存储数组的数据,Numpy 数组中的数据可以指向其它数组中的数据,这样多个数组可以共用同一个数据:

  • ndarray.base用于判断数组中的数据是否来自于别的数组;
  • ndarray.flags.owndata用于判断数组是否是数据的所有者;

就上例而言,a.basea[1, 2].base返回的都是 None,说明aa[1, 2]两个数组中的数据都来自于自己,不是来自别的数组。a.flags.owndataa[1, 2].flags.owndata返回的都是True,说明aa[1, 2]两个数组都是数组中数据的所有者。

视图与副本

a[1:3]得到的是原数组的视图,而a[[1, 2]]得到的是原数组的副本。具体来说:

  • 视图是对原数组的引用,或者自身没有数据,与原数组共享数据;
  • 副本是对原数组的完整拷贝,虽然经过拷贝后的数组中的数据来自于原数组,但是它相对于原数组是独立的;

视图

Numpy 有两种方式能够产生原数组的视图:

  1. 对原数组的引用;
  2. 自身没有数据,与原数组共享数据;
import numpy as np

arr = np.arange(12)
cite_of_arr = arr

>>> print(arr)
[ 0  1  2  3  4  5  6  7  8  9 10 11]
>>> print(cite_of_arr)
[ 0  1  2  3  4  5  6  7  8  9 10 11]

>>> print(id(arr))
2517208831840
>>> print(id(cite_of_arr))
2517208831840

>>> print(arr.base)
None
>>> print(cite_of_arr.base)
None

>>> print(arr.flags.owndata)
True
>>> print(cite_of_arr.flags.owndata)
True

通过arrcite_of_arr的内存地址(id函数)可以看出,其实两个数组是同一个,相当于多了一个名字而已,比较简单,这里不再赘述。view()reshape()函数可以返回原数组的视图,此时的返回的数组自身没有数据,与原数组共享数据。

import numpy as np

arr = np.arange(12)

view_of_arr = arr.view()
view_of_arr.shape = (3, 4)

reshape_of_arr = arr.reshape(4, 3)

>>> print(arr.base)
None
>>> print(view_of_arr.base)
[ 0  1  2  3  4  5  6  7  8  9 10 11]
>>> print(reshape_of_arr.base)
[ 0  1  2  3  4  5  6  7  8  9 10 11]

>>> print(arr.flags.owndata)
True
>>> print(view_of_arr.flags.owndata)
False
>>> print(reshape_of_arr.flags.owndata)
False

其中view_of_arrreshape_of_arr两个数组中的数据使用的都是arr数组中的数据,这也是为什么view_of_arr.basereshape_of_arr.base返回的都是 [ 0 1 2 3 4 5 6 7 8 9 10 11] 的原因,而由于arr数组中的数据是它本身的,因此arr.base返回 None。view_of_arrreshape_of_arr两个数组都不是数据的所有者,只有arr数组是数据的所有者,所以两个数组的flags.owndata返回都是False

到底是不是共享数据,我们可以通过查看数组所占的内存地址进行验证。Numpy 数组所占的内存空间包含两个部分,数据结构信息区以及数据存储区,使用nbytes属性可以查看数组中的数据所占的字节数。

>>> print(arr.nbytes)
48
>>> print(cite_of_arr.nbytes)
48
>>> print(view_of_arr.nbytes)
48
>>> print(reshape_of_arr.nbytes)
48

使用sys.getsizeof()函数可以直接得到每个数组所占内存空间的大小。

>>> print(getsizeof(arr))
144
>>> print(getsizeof(cite_of_arr))
144
>>> print(getsizeof(view_of_arr))
112
>>> print(getsizeof(reshape_of_arr))
112

从输出结果可以发现,只有view_of_arrreshape_of_arr两个数组所占的内存空间大小为 112,这是因为这两个数组自身没有数据,而使用的是原数组arr的数据,而通过nbytes属性知道了数据的内存大小为 48,这也从侧面证明了,view_of_arrreshape_of_arr两个数组使用的是外部数据,而这外部数据的内存大小刚好是 48。

从图中可以看出,虽然view_of_arrreshape_of_arr两个数组共用arr数组的数据,但是由于它们有属于自己的数据结构信息区,因此可以将arr数组中的原始数据以自己的方式进行表达(指定不同的 shape 以及 dtype 等)。

无论是对原数组的引用,还是自身没有数据,与原数组共享数据。这两种产生原数组视图的方式共享相同的数据,因此无论是修改原数组还是修改原数组的视图中的数据元素,共享数据的数组都会相对应的发生改变。

import numpy as np

arr = np.arange(12)

view_of_arr = arr.view()
view_of_arr.shape = (3, 4)

reshape_of_arr = arr.reshape(4, 3)

arr[1] = 100
view_of_arr[2] = 200
reshape_of_arr[3] = 300

>>> print(arr)
[  0 100   2   3   4   5   6   7 200 300 300 300]
>>> print(view_of_arr)
[[  0 100   2   3]
 [  4   5   6   7]
 [200 300 300 300]]
>>> print(reshape_of_arr)
[[  0 100   2]
 [  3   4   5]
 [  6   7 200]
 [300 300 300]]

副本

副本是对原数组的完整拷贝,虽然经过拷贝后数组中的数据来自于原数组,但是它相对于原数组是独立的。使用copy()方法可以返回原数组的副本。

import numpy as np

arr = np.arange(12)
copy_of_arr = arr.copy()

>>> print(arr)
[ 0  1  2  3  4  5  6  7  8  9 10 11]
>>> print(copy_of_arr)
[ 0  1  2  3  4  5  6  7  8  9 10 11]

>>> print(arr.base)
None
>>> print(copy_of_arr.base)
None

>>> print(arr.flags.owndata)
True
>>> print(copy_of_arr.flags.owndata)
True

arrcopy_of_arr两个数组的baseflags.owndata返回的值一样,这也可以验证虽然copy_of_arr中的数据来源于原数组,但是它相对于原数组是独立的。

既然副本和原数组是相互独立的,改变副本或者原数组中的元素值,相对应的原数组和副本中的元素值并不会发生改变。

import numpy as np

arr = np.arange(12)
copy_of_arr = arr.copy()

arr[1] = 100
copy_of_arr[2] = 200

>>> print(arr)
[  0 100   2   3   4   5   6   7   8   9  10  11]
>>> print(copy_of_arr)
[  0 100   2   3   4   5   6   7   8   9  10  11]

小结

  • 视图也被称为浅拷贝,而副本被称为深拷贝;
  • 视图和副本的主要区别在于,修改原数组,视图会受到影响,而副本不会受到影响;
  • 返回原数组视图和副本的常见操作:
    • 视图:赋值引用,Numpy 的切片操作,调用view()函数,调用reshape()函数;
    • 副本:Fancy Indexing(花式索引,例如a[[1, 2]]),调用copy()函数;
  • 不能通过id()函数来区分视图和副本;
>>> print(id(arr) == id(cite_of_arr)) # 视图
True
>>> print(id(arr) == id(view_of_arr)) # 视图
False
>>> print(id(arr) == id(reshape_of_arr)) # 视图
False
>>> print(id(arr) == id(copy_of_arr)) # 副本
False
原创首发:公众号【AI机器学习与深度学习算法】

 

标签:arr,副本,视图,base,数组,print,numpy,view
From: https://www.cnblogs.com/emanlee/p/17628238.html

相关文章

  • Numpy 数组的内部结构组成
    Numpy数组的内部结构组成 下图是Numpy数组的内部结构组成。其中可以分为数组数据结构信息区以及数据存储区。简单来说,数组数据结构信息区中有Numpy数组的形状(shape)以及数据类型(data-type)等信息,而数据存储区则是用于存储数组的数据,Numpy数组中的数据可以指向其它数组中......
  • Numpy 创建随机数数组 随机数组
     创建随机数数组NumPy提供了强大的生成随机数的功能。真正的随机数很难获得,实际中使用的都是伪随机数。大部分情况下,伪随机数就能满足需求。当然,某些特殊情况除外,如进行高精度的模拟实验。对于NumPy,与随机数相关的函数都在random模块中,其中包括了可以生成服从多种概率分布随机数......
  • numpy 数组 的 轴
    numpy数组的轴 1认识“轴”的概念如同笛卡尔坐标系一样,NumPy张量也有轴。现在我们先以熟悉二维向量为例来说明这个概念,二维向量的轴是沿行和列的方向。轴的编号是从0开始的,因此“第一轴”实际上是“axis0”。“第二轴”是“axis1”,依此类推。在可视化观感上,“axis0”就......
  • numpy 多维数据的理解(三维数据,更多维度)
    numpy 多维数据的理解(三维数据,更多维度)In[22]:a=np.array([[11,12,13,14,15],...:[16,17,18,19,20],...:[21,22,23,24,25],...:[26,27,28,29,30],...:[31,32,33,34,......
  • Numpy手撸神经网络实现线性回归
    Numpy手撸神经网络实现线性回归简介在深度学习理论学习之后,我们常常会直接使用深度学习框架(如PaddlePaddle、PyTorch或TensorFlow)来构建模型,而忽略了底层各种层结构的实现。但对于深度学习的学习者来说,是否能够亲手编写一个简单的模型呢?本文将介绍如何使用NumPy手动实现一个神经......
  • numpy手搓卷积
    numpy实现卷积1卷积本质设计这样的一个滤波器(filter,也称为kernel),用这个filter,往我们的图片上“盖”,覆盖一块跟filter一样大的区域之后,对应元素相乘,然后求和。计算一个区域之后,就向其他区域挪动,接着计算,直到把原图片的每一个角落都覆盖到了为止。这个过程就是“卷积”。可以......
  • MongoDB高阶特性:副本集、分片、事务、索引
    一、副本集(主从复制)1、docker-compose.ymlversion:'3'services:mongo1:image:mongocontainer_name:mongo1command:mongod--replSetrs0--port27017volumes:-./mongodb-cluster/mongod1:/data/dbports:-"27017:2......
  • 基本入门案例、视图类
    flask-restful中有两个基本的类,一个是Api、一个是ResourceApi(用于构建restful风格的主类,需要将flask实例app传递给其实例化。)Resource(视图类,类似django的View,理念上和django、drf都是类似的,以请求方式名作为每个视图函数入口),Resource视图类默认返回Content-type为application/j......
  • 附录A NumPy高级应用
    在这篇附录中,我会深入NumPy库的数组计算。这会包括ndarray更内部的细节,和更高级的数组操作和算法。本章包括了一些杂乱的章节,不需要仔细研究。A.1ndarray对象的内部机理NumPy的ndarray提供了一种将同质数据块(可以是连续或跨越)解释为多维数组对象的方式。正如你之前所看到的那......
  • 第04章 NumPy基础:数组和矢量计算
    NumPy(NumericalPython的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。NumPy的部分功能如下:ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。用于对整组数据进行快速运算的标准数学函数(无需编写循环)。......