在计算机中,没有任何数据类型是固定的,完全取决于如何看待这片数据的内存区域。
在numpy.ndarray.view中,提供对内存区域不同的切割方式,来完成数据类型的转换,而无须要对数据进行额外的copy,可以节约内存空间,我们可以将view看做对内存的展示方式。
如:
import numpy as np
x = np.arange(10, dtype=np.int) print('An integer array:', x)
print ('An float array:', x.view(np.float))
An integer array: [0 1 2 3 4 5 6 7 8 9]

An float array: [  0.00000000e+000   4.94065646e-324
9.88131292e-324 1.48219694e-323 1.97626258e-323
2.47032823e-323 2.96439388e-323 3.45845952e-323
3.95252517e-323 4.44659081e-323]

在实际使用中我们往往会采取更复杂的dtype(也就是说view可以与dtype搭配使用)输出内存中的值,后面我们会示范对于结构化数组的较为复杂的view使用。

一、view和copy

我们从numpy.reshape()函数入手,文档对于其返回值的解释:

Returns
    -------
    reshaped_array : ndarray
        This will be a new view object if possible; otherwise, it will
        be a copy.  Note there is no guarantee of the *memory layout* (C- or
        Fortran- contiguous) of the returned array.

其返回值可能是一个view,或是一个copy。相应的条件为:
  1、返回一个view条件:数据区域连续的时候
  2、反之,则返回一个copy
我们得到了一个新概念,数组内存区域是否连续,numpy数组有flags['C_CONTIGUOUS']表示是否连续,有np.may_share_memory方法判断两个数组内存区域是否一致:

a = np.zeros([2,10], dtype=np.int32)
b = a.T # 转置破坏连续结构 a.flags['C_CONTIGUOUS'] # True
b.flags['C_CONTIGUOUS'] # False np.may_share_memory(a,b) # True
b.base is a # True
id(b)==id(a) # False a.shape = 20 # a的shape变了
a.flags['C_CONTIGUOUS'] # True # b.shape = 20
# AttributeError: incompatible shape for a non-contiguous array
# 想要使用指定shape的方式,只能是连续数组,但是reshape方法由于不改变原数组,所以reshape不受影响

数组切片是否会copy数据?

不过,数组的切片对象虽然并非contiguous,但是对它的reshape操作并不会copy新的对象,

a = np.arange(16).reshape(4,4)  

print(a.T.flags['C_CONTIGUOUS'],a[:,0].flags['C_CONTIGUOUS'])
# False False print (np.may_share_memory(a,a.T.reshape(16)),
np.may_share_memory(a,a[:,0].reshape(4)))
# False True

但是,下一小节会介绍,高级切片会copy数组,开辟新的内存。

二、numpy的结构数组

利用np.dtype可以构建结构数组,numpy.ndarray.base会返回内存主人的信息,文档如下,

Help on getset descriptor numpy.ndarray.base:

base
    Base object if memory is from some other object.
    
    Examples
    --------
    The base of an array that owns its memory is None:
    
    >>> x = np.array([1,2,3,4])
    >>> x.base is None
    True
    
    Slicing creates a view, whose memory is shared with x:
    
    >>> y = x[2:]
    >>> y.base is x
    True

1、建立结构数组

persontype = np.dtype({
'names':['name','age','weight','height'],
'formats':['S30','i','f','f']}, align=True)
a = np.array([('Zhang',32,72.5,167),
('Wang',24,65,170)],dtype=persontype)
a['age'].base

array([(b'Zhang', 32, 72.5, 167.),

(b'Wang', 24, 65. , 170.)],

dtype={'names':['name','age','weight','height'],

'formats':['S30','<i4','<f4','<f4'],

'offsets':[0,32,36,40],

'itemsize':44,

'aligned':True})

2、高级切片和普通切片的不同

In [26]: a.base
In [27]: a[0].base
In [28]: a[:1].base
Out[28]: array([123, 4, 5, 6, 78])
In [29]: a[[0,1]].base In [30]: a.base is None
Out[30]: True
In [31]: a[0].base is None
Out[31]: True
In [32]: a[:1].base is None
Out[32]: False
In [33]: a[[0,1]].base is None
Out[33]: True

由上可见高级切片会开辟新的内存,复制被切出的数据,这是因为这种不规则的内存访问使用原来的内存结构效率很低(逻辑相邻元素内存不相邻,标准的访问由于固定了起始和步长相当于访问相邻元素,所以效率较高),拷贝出来就是连续的内存数组了。

3、高级切片且不开辟新内存的方法

回到上上小节的结构数组,

print(a['age'].base is a)
print(a[['age', 'height']].base is None)

True

True

我们通过指定内存解析方式,实现不开辟新内存,将原内存解析为高级切片指定的结构数组,

def fields_view(arr, fields):
dtype2 = np.dtype({name:arr.dtype.fields[name] for name in fields})
# print(dtype2)
# {'names':['age','weight'], 'formats':['<i4','<f4'], 'offsets':[32,36], 'itemsize':40}
# print([(name,arr.dtype.fields[name]) for name in fields])
# [('age', (dtype('int32'), 32)), ('weight', (dtype('float32'), 36))]
# print(arr.strides)
# (44,)
return np.ndarray(arr.shape, dtype2, arr, 0, arr.strides)
'''
ndarray(shape, dtype=float, buffer=None, offset=0,
| strides=None, order=None) 参数 类型 作用
shape int型tuple 多维数组的形状
dtype data-type 数组中元素的类型
buffer 用于初始化数组的buffer
offset int buffer中用于初始化数组的首个数据的偏移
strides int型tuple 每个轴的下标增加1时,数据指针在内存中增加的字节数
order 'C' 或者 'F' 'C':行优先;'F':列优先
''' v = fields_view(a, ['age', 'weight'])
print(v.base is a) v['age'] += 10
print('+++'*10)
print(v)
print(v.dtype)
print(v.dtype.fields)
print('+++'*10)
print(a)
print(a.dtype)
print(a.dtype.fields)
True
++++++++++++++++++++++++++++++
[(42, 72.5) (34, 65. )]
{'names':['age','weight'], 'formats':['<i4','<f4'], 'offsets':[32,36], 'itemsize':40}
{'age': (dtype('int32'), 32), 'weight': (dtype('float32'), 36)}
++++++++++++++++++++++++++++++
[(b'Zhang', 42, 72.5, 167.) (b'Wang', 34, 65. , 170.)]
{'names':['name','age','weight','height'], 'formats':['S30','<i4','<f4','<f4'], 'offsets':[0,32,36,40], 'itemsize':44, 'aligned':True}
{'name': (dtype('S30'), 0), 'age': (dtype('int32'), 32), 'weight': (dtype('float32'), 36), 'height': (dtype('float32'), 40)}

这里注意一下.dtype的’itemsize‘参数,表示添加一条(行)数据,内存增加了多少字节,由于保存了'offsets'偏移信息,我们生成的dtype展示的是一个稀疏的结构,但是每一行不会有多余的尾巴,这是因为空元素是由实元素记录偏移量的空隙产生的。

『Numpy』内存分析_numpy.dtype解析内存数据中我们会更详细的介绍有关数组内存解析的方法。

『Numpy』内存分析_高级切片和内存数据解析的更多相关文章

  1. 『TensorFlow』分布式训练_其三_多机分布式

    本节中的代码大量使用『TensorFlow』分布式训练_其一_逻辑梳理中介绍的概念,是成熟的多机分布式训练样例 一.基本概念 Cluster.Job.task概念:三者可以简单的看成是层次关系,tas ...

  2. 『Re』正则表达式模块_常用方法记录

    『Re』知识工程作业_主体识别 一个比较完备的正则表达式介绍 几个基础函数 re.compile(pattern, flags=0) 将正则表达式模式编译成一个正则表达式对象,它可以用于匹配使用它的m ...

  3. 『Numpy』内存分析_利用共享内存创建数组

    引.内存探究常用函数 id(),查询对象标识,通常返回的是对象的地址 sys.getsizeof(),返回的是 这个对象所占用的空间大小,对于数组来说,除了数组中每个值占用空间外,数组对象还会存储数组 ...

  4. 『Numpy』常用方法记录

    numpy教程 防止输出省略号 import numpy as np np.set_printoptions(threshold=np.inf) 广播机制 numpy计算函数返回默认是一维行向量: i ...

  5. 『TensorFlow』分布式训练_其二_单机多GPU并行&GPU模式设定

    建议比对『MXNet』第七弹_多GPU并行程序设计 一.tensorflow GPU设置 GPU指定占用 gpu_options = tf.GPUOptions(per_process_gpu_mem ...

  6. 『TensorFlow』读书笔记_降噪自编码器

    『TensorFlow』降噪自编码器设计  之前学习过的代码,又敲了一遍,新的收获也还是有的,因为这次注释写的比较详尽,所以再次记录一下,具体的相关知识查阅之前写的文章即可(见上面链接). # Aut ...

  7. 『PyTorch』第九弹_前馈网络简化写法

    『PyTorch』第四弹_通过LeNet初识pytorch神经网络_上 『PyTorch』第四弹_通过LeNet初识pytorch神经网络_下 在前面的例子中,基本上都是将每一层的输出直接作为下一层的 ...

  8. 内存分析_.Net垃圾回收介绍

    垃圾回收 1.       .Net垃圾回收中涉及的名称 1.1.什么是代? 垃圾回收器为了提升性能使用了代的机制,共分为三代(Gen0.Gen1.Gen2).GC工作机制基于以下假设, 1)  对象 ...

  9. 内存分析_.Net内存原理介绍

    内存原理介绍 1.       .Net应用程序中的内存 1.1.Net内存类型 Windows使用一个系统:虚拟寻址系统.这个系统的作用是将程序可用的内存地址映射到硬件内存中的实际地址上.其实际结果 ...

随机推荐

  1. MAC OSX Xcode硬盘清理

    1.移除对旧设备的支持影响:可重新生成:再连接旧设备调试时,会重新自动生成.我移除了4.3.2, 5.0, 5.1等版本的设备支持.路径:~/Library/Developer/Xcode/iOS D ...

  2. HTML链接式引入CSS和JS

    <!-调用CSS-> <link href="./XXXXX.css" rel="stylesheet" type="text/cs ...

  3. Linux下安装pymysql

    直接使用pip进行安装: [root@mycentos ~]#pip install pymysql

  4. python进阶(二) 多进程+协程

    我们大多数的时候使用多线程,以及多进程,但是python中由于GIL全局解释器锁的原因,python的多线程并没有真的实现 实际上,python在执行多线程的时候,是通过GIL锁,进行上下文切换线程执 ...

  5. #WEB安全基础 : HTTP协议 | 0x5 URI和URL

    URI(统一资源标识符)和URL(统一资源定位符)相信大家都知道URL吧,我们看看它们有什么区别 URI 长得就像这样 /images/hackr.jepg URL 长得像这样 http://hack ...

  6. [macOS] Cannot find libz when install php56

    After upgraded to 10.12 and xcode8.2, when updating php with homebrew, i got these errors: /usr/loca ...

  7. manjaro使用国内软件源

    虽然manjaro是基于arch修改的,但毕竟还是有些改动,如果可以用manjaro仓库里的,尽量不要用arch的源.如果嫌官方的软件源慢,可以直接一条命令修改成国内的软件源 sudo pacman- ...

  8. Zookeeper应用之——队列(Queue)

    为了在Zookeeper中实现分布式队列,首先需要设计一个znode来存放数据,这个节点叫做队列节点,我们的例子中这个节点是/zookeeper/queue. 生产者向队列中存放数据,每一个消息都是队 ...

  9. spring 获取对象的注解

    BeanDefinition definition = registry.getBeanDefinition(name); if (definition instanceof AnnotatedBea ...

  10. Apriori

    基本概念 项与项集:设itemset={item1, item_2, …, item_m}是所有项的集合,其中,item_k(k=1,2,…,m)成为项.项的集合称为项集(itemset),包含k个项 ...