NumPy入门 之 数组初探: 图解view和copy (中)

本文深入探讨NumPy数组的view和copy特性,解析不同数据类型下数组元素的内存布局及解读方式,演示如何通过改变形状、步幅和数据类型实现数组的灵活应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读 :在上一篇《NumPy入门 之 数组初探: 图解view和copy (上)》中,我们知道view可以化身不同的shape去看待base数组的数据。实际上还允许以不同数据类型来解读数据,这就是本篇的主题。提示: 如果代码框中的代码没有全部显示,只需用你的手指头轻轻一划。

在这里插入图片描述

我们知道,程序处理的数据就是躺在内存里的一串01表示的二进制数。人们以字节(8个bit)作为基本单元来组织这些二进制数。比如拿连续的四个字节,把它们组装成一个32bit的二进制码,可以把它看成带符号的32bit整数类型,也可以把它看成带符号的单精度浮点数类型,分别对应C语言中的int和float类型。
在《Numpy入门 之 数组初探 1: shape和stride》这篇里,我们已经知道NumPy支持16 bit和8 bit的整数类型。为了简化问题,接下来我们就使用它们来做实验。
import numpy as np
a = np.arange(5, dtype ='int16') 
'数组a:', a 
('数组a:', array([0, 1, 2, 3, 4], dtype=int16))
我们来看看数组a在内存中的样子。前方高能,二进制方队正迈着整齐的步伐向我们走来,
np.vectorize(np.binary_repr)(a, width=16)
array(['0000000000000000', '0000000000000001', '0000000000000010',
       '0000000000000011', '0000000000000100'], dtype='<U16')
v = a.view('int8') 
('v眼中的数组a:', v) 
('v眼中的数组a:', array([0, 0, 1, 0, 2, 0, 3, 0, 4, 0], dtype=int8))
np.vectorize(np.binary_repr)(v, width=8)
array(['00000000', '00000000', '00000001', '00000000', '00000010',
       '00000000', '00000011', '00000000', '00000100', '00000000'],
      dtype='<U8')

在这里插入图片描述

为了看得更清楚一点,我们把二进制方队也放到图里来,
a = np.arange(5, dtype ='int16') 
bin_16 = np.vectorize(np.binary_repr)(a, width=16)

v = a.view('int8') 
bin_8 = np.vectorize(np.binary_repr)(v, width=8)

在这里插入图片描述

看上图,我们知道数组a里面的一个16bit的整数在v看来变成了两个整数。但是细心的你一定发现了,16bit被拆成两个8bit时,顺序好像反过来了。看上图中的红色框框里的1被拆成了下面蓝色框框里的1和0,为什么不是0和1呢。
为了解释上面的二进制方队,我们需要一个概念,即cpu的大小端模式。
  • 大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
  • 小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
从上面的图来看,这里应该是小端模式,下面我们来验证一下。
import sys
sys.byteorder
'little'
到这里,应该好理解了,int16数组中的元素1(二进制0000000000000001)的两个字节被按地址从低到高存放,低字节00000001放在内存的低地址,高字节00000000放在内存的高地址。因此,数组a中的元素1,在v眼中变成了1和0。数组a其他的4个元素也是一样的处理。
我们知道,v不仅能以不同的角度来看待数组a的数据,它甚至还有权限修改它。我们继续测试验证一下。
v += 1
('v眼中的数组a每个元素加1以后:', v) 
('v眼中的数组a每个元素加1以后:', array([1, 1, 2, 1, 3, 1, 4, 1, 5, 1], dtype=int8))
('此时的数组a:', a) 
('此时的数组a:', array([257, 258, 259, 260, 261], dtype=int16))

在这里插入图片描述

v把它眼中看到的每一个数都加上1,结果导致a中的数发生了大变。为了看得更清晰一些,我们再次输出每个数的二进制。
a = np.arange(5, dtype ='int16') 

v = a.view('int8') 
v += 1

bin_8 = np.vectorize(np.binary_repr)(v, width=8)
bin_16 = np.vectorize(np.binary_repr)(a, width=16)

在这里插入图片描述

观察上图,我们可以看到,变量bin_16对应数组a的二进制,蓝色框框里的int16数字正是来自红色框框里的两个int8数字交换顺序后组装起来的。二进制0000000100000011翻译成10进制正是259,看看是不是等于数组a此时的第3个数?
上篇里,我们讲到,三观包括shape、strides和dtype。上篇里改变的是shape和strides,而本篇里上面是仅仅改变dtype。下面我们来看看三者全部被改变后的效果会如何?
a = np.arange(5, dtype ='int16') 
  
v = a.view('int8') 
v.shape = (5, 2)

v_base  = v.base
v_dtype = v.dtype
v_shape = v.shape
v_strides = v.strides

在这里插入图片描述

再来看看此时v的三观,即dtype, shape, strides,

在这里插入图片描述

# 最后再看看v加1以后的结果
v += 1 

在这里插入图片描述

再次输出二进制,
bin_8 = np.vectorize(np.binary_repr)(v, width=8)
bin_16 = np.vectorize(np.binary_repr)(a, width=16)

在这里插入图片描述

相信大家能看明白上面的图。
copy: 这一集的戏份都给了view吗,我没有存在感,宝宝不开森啊。好,那我们也为copy插入一段戏吧,我们来看看copy和view不同顺序的组合会有什么效果?
# 先定义个函数,用于获取数组x的数据的内存地址,以16进制显示
def data_pointer(x):
    return hex(x.__array_interface__['data'][0])
a = np.arange(5, dtype ='int16') 

b = a.copy().reshape(5,1)
c = a.reshape(5,1).copy()
# 顺便看看 = 
d = a
然后来看看a,b,b的base、c以及d,它们的数据分别放在内存哪里呢?
data_pointer(a), data_pointer(b), data_pointer(b.base), data_pointer(c), data_pointer(d)
('0x56318c4023c0',
 '0x56318c3bf180',
 '0x56318c3bf180',
 '0x56318c2ccac0',
 '0x56318c4023c0')
可以看到,a、b、c的数据地址各不相同,显然b的数据不是自己的,上面显示的是它base的数据地址,而d显然跟a一样,相当于它的别名。
# 确认一把
d is a
True
再来看看view的view,
e = a.reshape(1,5)
f = e.reshape(5,)
f.base is e, f.base is a
(False, True)

在这里插入图片描述

好啦,今天就到这里了。至于copy,其实还有内容,那就是关于它的参数order,
  • numpy.copy(a, order=‘K’) or
  • ndarray.copy(order=‘C’)
但这个问题不属于本篇议题,后面再讲。

在这里插入图片描述

小结一下,view相当于是它base数组的化身(宗教用语,通常指神或精灵等超自然力量,以人类或动物的形态出现在世间),没有数据的所有权,但能超视距感应和使用base的数据,而且对数据有自己的解读方式。通过创建不同的view可以用不同方式来解读和处理base数组的数据。而copy方法会对数组及其数据进行完全拷贝,结果和原数组是两个独立的对象,两者的数据存放在内存中的不同地址。
本文获得原文作者授权,
转载自公众号“机器学习与数学”。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值