• NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构


    本文摘自《用Python做科学计算》,版权归原作者所有。

    上一篇讲到:NumPy-快速处理数据--ndarray对象--数组的创建和存取

    接下来接着介绍多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构

    一、多维数组的存取

    多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。如二维数组需要(x, y)的元组标记一个数组元素;三维数组需要(x, y, z)的元组标记一个元素。

    如下图所示,a为一个6x6的二维数组,图中用颜色区分了各个下标以及其对应的选择区域。

     1 >>> a
     2 array([[ 0,  1,  2,  3,  4,  5],
     3        [10, 11, 12, 13, 14, 15],
     4        [20, 21, 22, 23, 24, 25],
     5        [30, 31, 32, 33, 34, 35],
     6        [40, 41, 42, 43, 44, 45],
     7        [50, 51, 52, 53, 54, 55]])
     8 >>> a[0, 3:5]#黄色部分
     9 array([3, 4])
    10 >>> a[4:, 4:]#蓝色部分
    11 array([[44, 45],
    12        [54, 55]])
    13 >>> a[:, 2]#棕色部分
    14 array([ 2, 12, 22, 32, 42, 52])
    15 >>> a[2::2, ::2]#青色部分
    16 array([[20, 22, 24],
    17        [40, 42, 44]])

    如何创建这个6×6的二维数组?

    数组a实际上是一个加法表,纵轴的值为0, 10, 20, 30, 40, 50;横轴的值为0, 1, 2, 3, 4, 5。纵轴的每个元素都和横轴的每个元素求和,就得到图中所示的数组a。你可以用下面的语句创建它:

    1 np.arange(0, 60, 10).reshape(-1, 1) + np.arange(0, 6)

    多维数组同样也可以使用整数序列和布尔数组进行存取。

     1 >>> a[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5)]#取出坐标为(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)的元素
     2 array([ 1, 12, 23, 34, 45])
     3 >>> a[3:, [0, 2, 5]]#取出第3, 4, 5行,第0, 2, 5列的元素
     4 array([[30, 32, 35],
     5        [40, 42, 45],
     6        [50, 52, 55]])
     7 >>> mask = np.array([1, 0, 1, 0, 0, 1], dtype=np.bool)#先制造出一个布尔数组
     8 >>> mask
     9 array([ True, False,  True, False, False,  True], dtype=bool)
    10 >>> a[mask, 2]      #取出第二列下标为True的元素
    11 array([ 2, 22, 52])

    二、结构体数组

    类似C语言中的结构体数组,在NumPy中也很容易对这种结构数组进行操作。只要NumPy中的结构定义和C语言中的定义相同,NumPy就可以很方便地读取C语言的结构数组的二进制数据,转换为NumPy的结构数组。

     1 >>> personType = np.dtype({'names':['name', 'age', 'weight'],
     2                          'formats':['S32', 'i', 'f']})#创建personType数据类型
     3 #字典有两个关键字:names,formats。每个关键字对应的值都是一个列表。
     4 #names定义结构中的每个字段名,而formats则定义每个字段的类型:
     5 #    S32 : 32个字节的字符串类型
     6 #    i   :32bit的整数类型,相当于np.int32 
     7 #    f   :32bit的单精度浮点数类型,相当于np.float32 
     8 >>> personType
     9 dtype([('name', 'S32'), ('age', '<i4'), ('weight', '<f4')])
    10 #描述结构类型的方法: 一个包含多个组元的列表,其中形如 (字段名, 类型描述) 
    11 #的组元描述了结构中的每个字段。类型描述前面的 '|', '<' 等字符用来描述字段
    12 #值的字节顺序:
    13 #  | :忽视字节顺序
    14 #  < : 低位字节在前
    15 #  > : 高位字节在前
    16 >>> a = np.array([('zhang', 32, 75.5), ('wang', 24, 65.2)], dtype=personType )
    17 >>> a[0]        #读取结构体数组的第0个结构元素
    18 ('zhang', 32, 75.5)
    19 >>> a[1]        #读取结构体数组的第1个结构元素
    20 ('wang', 24, 65.19999694824219)
    21 >>> a[0].dtype
    22 dtype([('name', 'S32'), ('age', '<i4'), ('weight', '<f4')])
    23 >>> c = a[1]    #c和a[1]共享同一块内存
    24 >>> c['name'] = 'li'#修改c的字段
    25 >>> a[1]['name']#则a[1]相应的字段也被修改
    26 'li'
    27 >>> a[0]['name']#读取a[0]字段的name成员
    28 'zhang'
    29 >>> b = a[:]['age']#或者 a['age']
    30 >>> b
    31 array([32, 24])
    32 >>> b[0] = 40   #通过b[0]修改a[0]['age']
    33 >>> a[0]['age']
    34 40
    35 >>> a.tofile('d:\test.bin')
    36 #调用a.tostring或者a.tofile方法,可以直接输出数组a的二进制形式

    利用下面的C语言程序可以将test.bin文件中的数据读取出来。

    三、内存对齐

    C语言的结构体为了内存寻址方便,会自动的添加一些填充用的字节,这叫做内存对齐。内存对齐与操作系统以及编译器有关。例如如果把下面的name[32]改为name[30]的话,由于内存对齐问题,在name和age中间会填补两个字节,最终的结构体大小不会改变。因此如果numpy中的所配置的内存大小不符合C语言的对齐规范的话,将会出现数据错位。为了解决这个问题,在创建dtype对象时,可以传递参数align=True,这样numpy的结构数组的内存对齐和C语言的结构体就一致了。

     1 #include <stdio.h>
     2 
     3 struct person
     4 {
     5     char name[32];
     6     int age;
     7     float weight;
     8 };//创建结构体数据类型
     9 
    10 struct person p[2];//定义长度为2的一维结构体数组
    11 
    12 int main (void)
    13 {
    14     FILE *fp;
    15     int i;
    16     fp=fopen("d:\test.bin","rb");//以二进制只读方式打开文件
    17     fread(p, sizeof(struct person), 2, fp);//读取的内容放在结构体数组p[2]中
    18     fclose(fp);
    19     for(i=0;i<2;i++)
    20         printf("%s %d %f
    ", p[i].name, p[i].age, p[i].weight);
    21     getchar();
    22     return 0;
    23 }
    24 /*
    25 在VC++6.0输出结果是:
    26 ----------------------------
    27 zhang 40 75.500000
    28 li 24 65.199997
    29 
    30 Press any key to continue
    31 ----------------------------
    32 */

    结构类型中可以包括其它的结构类型,下面的语句创建一个有一个字段f1的结构,f1的值是另外一个结构,它有字段f2,其类型为16bit整数。

    1 >>> np.dtype([('f1', [('f2', np.int16)])])#结构体套结构体
    2 dtype([('f1', [('f2', '<i2')])])
    3 #用dtype([ ])来定义结构体,[('f2', np.int16)]是一个结构体
    4 #('f1', [('f2', np.int16)])是一个元组
    5 #最外层用dtype([ ])再定义一层结构体

    当某个字段类型为数组时,用组元的第三个参数表示,下面描述的f1字段是一个shape为(2,3)的双精度浮点数组:

    1 >>> np.dtype([('f0', 'i4'), ('f1', 'f8', (2, 3))])
    2 dtype([('f0', '<i4'), ('f1', '<f8', (2, 3))])

    用下面的字典参数也可以定义结构类型,字典的关键字为结构中字段名,值为字段的类型描述,但是由于字典的关键字是没有顺序的,因此字段的顺序需要在类型描述中给出,类型描述是一个组元,它的第二个值给出字段的字节为单位的偏移量,例如age字段的偏移量为25个字节:

    1 >>> np.dtype({'surname':('S25',0),'age':(np.uint8,25)})
    2 dtype([('surname', 'S25'), ('age', 'u1')])

    四、Numpy内存结构

    下面让我们来看看ndarray数组对象是如何在内存中储存的。

    dtype对象则知道如何将元素的二进制数据转换为可用的值。如上图每32位表示一个有用数据

    dim count表示数组维数,上图为2维数组

    dimmension 3×3给出数组的shape

    strides中保存的是当每个轴的下标增加1时,数据存储区中的指针所增加的字节数。例如图中的strides为12,4,即第0轴的下标增加1时,数据的地址增加12个字节:即a[1,0]的地址比a[0,0]的地址要高12个字节,正好是3个单精度浮点数的总字节数;第1轴下标增加1时,数据的地址增加4个字节,正好是单精度浮点数的字节数。

    1 >>> a = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32)
    2 >>> a
    3 array([[ 0.,  1.,  2.],
    4        [ 3.,  4.,  5.],
    5        [ 6.,  7.,  8.]], dtype=float32)

    如果strides中的数值正好和对应轴所占据的字节数相同的话,那么数据在内存中是连续存储的。然而数据并不一定都是连续储存的,前面介绍过通过下标范围得到新的数组是原始数组的视图,即它和原始视图共享数据存储区域:

    1 >>> b = a[::2,::2]
    2 >>> b
    3 array([[ 0.,  2.],
    4        [ 6.,  8.]], dtype=float32)
    5 >>> b.strides
    6 (24, 8)

    由于数组b和数组a共享数据存储区,而b中的第0轴和第1轴都是数组a中隔一个元素取一个,因此数组b的strides变成了24,8,正好都是数组a的两倍。 对照前面的图很容易看出数据0和2的地址相差8个字节,而0和6的地址相差24个字节。

    元素在数据存储区中的排列格式有两种:C语言格式和Fortan语言格式。在C语言中,多维数组的第0轴是最上位的,即第0轴的下标增加1时,元素的地址增加的字节数最多;而Fortan语言的多维数组的第0轴是最下位的,即第0轴的下标增加1时,地址只增加一个元素的字节数。在NumPy中,元素在内存中的排列缺省是以C语言格式存储的,如果你希望改为Fortan格式的话,只需要给数组传递order="F"参数:

    1 >>> c = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32, order="F")
    2 >>> c.strides
    3 (4, 12)
    http://www.cnblogs.com/moon1992/
  • 相关阅读:
    Mac OS terminal 查看内存使用情况的命令
    【教程】5分钟在以太坊上发行自己的代币
    Mac下eclipse的快捷键
    Mac上编辑文本的一些实用快捷键:跳转、选中等操作
    Mysql数据库用source命令导入SQL文件,利用navicat修改MySQL用户root密码,忘记mysql密码如何修改
    比特币1个确认和6个确认的区别在哪里?什么是双花问题?
    以 1、3、bc1 开头的比特币地址都有啥区别?转账手续费那种便宜,隔离见证地址为什么手续费便宜?
    以太坊 2.0 中的 DeFi:城市、郊区、农村
    Mac版navicat生成ER图把表结构导出pdf
    下载文件(API接口,Angularjs前端)
  • 原文地址:https://www.cnblogs.com/moon1992/p/4946717.html
Copyright © 2020-2023  润新知