三、numpy的结构数组
『Numpy』内存分析_numpy.dtype内存数据解析方式指导
利用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展示的是一个稀疏的结构,但是每一行不会有多余的尾巴,这是因为空元素是由实元素记录偏移量的空隙产生的。
4、view
『Numpy』numpy.ndarray.view_数组视图_reshape、数组切片、数组内存开辟分析
如果两者的itemsize相同,我们可以直接使用array.view(dtype=dtype)来指定数组内存数据的展示视图形式,上面的代码这样修改一下,
def fields_view(arr, fields): dtype2 = np.dtype({name:arr.dtype.fields[name] for name in fields}) return np.ndarray(arr.shape, dtype2, arr, 0, arr.strides) v = fields_view(a, [‘age‘, ‘height‘]) a.view(dtype=v.dtype)
array([(52, 167.), (44, 170.)], dtype={‘names‘:[‘age‘,‘height‘], ‘formats‘:[‘<i4‘,‘<f4‘], ‘offsets‘:[32,40], ‘itemsize‘:44})
当我们最后一个偏移取到行末尾即属性‘height‘时,两者itemsize相等,可以指定view方法解析,
print(v.dtype) # {‘names‘:[‘age‘,‘height‘], ‘formats‘:[‘<i4‘,‘<f4‘], ‘offsets‘:[32,40], ‘itemsize‘:44} print(a.dtype) # {‘names‘:[‘name‘,‘age‘,‘weight‘,‘height‘], ‘formats‘:[‘S30‘,‘<i4‘,‘<f4‘,‘<f4‘], ‘offsets‘:[0,32,36,40], ‘itemsize‘:44, ‘aligned‘:True}
这是因为view本来用于解析内存数据的解析方式,对于结构数组就是每一行的解析方式,这个解析的方式(即输入参数我们自建的dtype)当然必须和内存数据的描述参数(即内存从属的数组的dtype)一致才行,否则解析数据无法对齐。
a.view(dtype=v.dtype)
array([(72, 167.), (64, 170.)], dtype={‘names‘:[‘age‘,‘height‘], ‘formats‘:[‘<i4‘,‘<f4‘], ‘offsets‘:[32,40], ‘itemsize‘:44})
原文地址:https://www.cnblogs.com/hellcat/p/8711192.html