python四种结构_Python科学计算神器之numpy-4:结构数组

一. 为什么需要结构数组

数据分析过程中,经常会有多种不同数据类型同时出现,而不仅仅是期望的数值型数据,而Array只能含有一种数据类型,Numpy/pandas该如何处理呢?在C语言中经常通过结构体struct来定义不同数据类型形成结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小均相同,类似的Numpy可以很容易的定义结构数组。和C语言一样,在Numpy中也可以操作这些字段对这种结构数组进行操作。只要Numpy的结构和C语言中的定义相同,Numpy就可以很方便地读取C语言的结构数组的二进制数据,转换为Numpy的结构数组。

例如,我们定义一个结构数组,它的每个元素有name,age和weight字段。在Numpy中可以如下定义:

>>>import numpy as np

>>>## 数据类型升级为string,其他类似C语言数据类型转换规则, 不会出现多种数据类型

>>>np.array([1,2,'a'])

array(['1', '2', 'a'], dtype='|S21')

二. 定义结构数组

现在我们定义一个结构数组,它的每个元素有name,age和weight字段。在Numpy中可以如下定义:

>>>persontype = np.dtype({

"names":["name", "height", "weight"],

"formats":["S32", "i", "f"]

})

>>>p = np.array([("Lee", 180, 74.5), ("Zhang", 170, 55)],

dtype=persontype)

我们创建一个dtype对象persontype,通过其字典参数描述结构类型的各个字段。字典有两个关键字:names、formats. 每个关键字对应的值都是一个列表, names定义结构中的每个字段名,formats则定义每个字段的数据类型:

S32: 32个字节的字符串类型, 由于结构中每个元素的大小必须固定, 因此需要指定字符串的长度

i: 32bit的整数类型, 相当于np.int32

f: 32bit的单精度浮点数, 相当于np.float32

然后我们调用array函数创建数组, 通过关键字参数dtype=persontype, 指定所创建的数组的元素类型维结构数组persontype. 可以看到数组p的元素类型:

>>>p.dtype

dtype([('name', 'S32'), ('height', '

这里我们看到了另一种描述结构类型的方法: 一个包含多个组元的列表,其中如 (字段名, 类型描述) 的组元来描述结构数组中的每个字段. 类型描述前面的"|","<",">"描述了字段值的字节顺序:

|: 忽视字节顺序

<: 低位字节在前, 即大段序

>: 高位字节在前, 即小端序

2.1 四种定义方法

结构数组有四种定义方式,即表述dtype对象结构的参数方式:

string

tuple

list

dict

上面例子即为dict定义的dtype结构类型。

2.1.1 string参数

dtype类型用一个逗号分割数据类型的string,每个类型对一个的数据采用默认名字‘f0’、‘f1’、‘f2’...,数据类型有四种形式:

a) b1, i1, i2, i4, i8, u1, u2, u4, u8, f2, f4, f8, c8, c16, a

(分别对应 bytes, ints, unsigned ints, floats, complex and

fixed length strings of specified byte lengths-固定字节长度的字符串)

b) int8,...,uint8,...,float16, float32, float64, complex64, complex128 (这里是按位长计算bit sizes)

此外还有 Numerric/numarray类型(如Float32)和单字符类型(如H代表usigned short ints),但已弃用,就不建议使用了。

>>>x = np.zeros(3, dtype='3int, float32, (2,3)float64')

>>>x

array([([0, 0, 0], 0., [[ 0., 0., 0.], [ 0., 0., 0.]]),

([0, 0, 0], 0., [[ 0., 0., 0.], [ 0., 0., 0.]]),

([0, 0, 0], 0., [[ 0., 0., 0.], [ 0., 0., 0.]])],

dtype=[('f0', '

2.1.2 tuple参数

元组仅适用于结构的数据和现有的数据类型对应的结构数组,呈元组对出现.

>>>x = np.zeros(3, dtype=('i4', [('r','u1'), ('g','u1'), ('b','u1'), ('a','u1')]))

>>>x['r']

array([0, 0, 0], dtype=uint8)

2.1.3 list参数

dtype由一组tuple的list定义,每个元组包含2-3个元素:1)数据结构域的名字,2)对应的数据类型,3)数据域的shape(可选).

>>>x = np.zeros(3, dtype=[('x', 'f4'), ('y', np.float32), ('value', 'f4', (2,2))])

>>>x

array([( 0., 0., [[ 0., 0.], [ 0., 0.]]),

( 0., 0., [[ 0., 0.], [ 0., 0.]]),

( 0., 0., [[ 0., 0.], [ 0., 0.]])],

dtype=[('x', '

2.1.4 dict参数

有两种不同的形式,一种字典是必须含'names'和'formats'关键字,对应一个相同长度的list,其中'names'必须是字符串. 另有两个可选关键字'offsets'和'titles', 'offsets'对应的list是每个数据域字节为单位偏移量, 'titles'对应的list是数据域元数据的对象. 开始的例子便是此种类型.

>>>x1 = np.zeros(3, dtype={'names':['col1', 'col2'], 'formats':['i4', 'f4']})

>>>x1

array([(0, 0.), (0, 0.), (0, 0.)],

dtype=[('col1', '

另一种字典是以数据域name做keys, 对应的value为含有(type,offset,title)的tuple, 其中title可选. 由于dict是没有顺序的, 每个数据域的顺序需要在定义时给出, 这个即是offset. 如height字段的偏移量为25个字节:

>>>np.dtype({"name":("S25", 0), "age":(np.uint8, 25)})

dtype([('name', 'S25'), ('age', 'u1')])

>>>x1 = np.zeros(3, dtype={'col1':('i1',0,'title 1'), 'col2':('f4',1,'title 2')})

>>>x1

array([(0, 0.), (0, 0.), (0, 0.)],

dtype=[(('title 1', 'col1'), 'i1'), (('title 2', 'col2'), '

三. 混合结构数组

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

>>>np.dtype([("f1", [("f2", np.int16)])])

dtype([('f1', [('f2', '

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

>>>np.dtype([("f0", "i4"), ("f1", "f8", (2, 3))])

dtype([('f0', '

四. 结构数组的存取

结构数组的存取和一般array相同, 通过下标即可, 需要注意的是元素的值虽然看着像组元, 但实际上是个结构数组:

>>>>p[0], p[0].dtype.names

(('Lee', 180, 74.5), ('name', 'height', 'weight'))

>>>p[0].dtype

dtype([('name', 'S32'), ('height', '

p[0]是一个结构元素, 它和a共享内存数据, 因而可以通过修改元素字段来改变原始结构数组中的对应字段:

>>>c = p[1]

>>>c["name"] = "Li"

>>>p[1].dtype

dtype([('name', 'S32'), ('height', '

结构数组可以像dict一样通过下标获取对应的字段值:

>>>b = p[:]["height"]

>>>b

array([180, 170], dtype=int32)

>>>b[0] = 200

>>>p[0]["height"]

200

同样的, 结构数组也可以调用tostring()和tofile()方法, 直接输出数组p的二进制形式:

p.tofile("test.bin")

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

C语言的结构体为了内存寻址方便, 会自动对较短字段填充一些字节, 称为内存对齐. 如果把下面的name[32]改为name[30], 由于内存对齐的原因, 在name和height中间会填补两个字节, 结构体大小并不改变. 因此如果numpy中锁配置的内存大小不符合C语言的对齐规范, 将会出现数据错位. 为了解决这个问题, 在创建dtype对象时, 可以传递参数align=True, 这样numpy的结构数组就和C语言的结构体的内存对齐是一致的.

#include

struct person{

char name[32];

int height;

float weight;

};

struct person p[2];

void main(){

FILE *fp;

int i;

fp = fopen("test.bin", "rb");

fread(p, sizeof(struct person), 2, fp);

fclose(fp);

for(i=0; i<2; i++)

printf("%s %d %f\n", p[i].name, p[i].weight);

getchar();

}

虽然上面罗嗦了这么多, 然而并没什么卵用, 对这类数据, 我们当然是交给将Numpy封装的更好的Pandas来处理了_

Numpy系列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值