5. NumPy之:结构化数组详解
简介
普通的数组就是数组中存放了同一类型的对象。而结构化数组是指数组中存放不同对象的格式。
今天我们来详细探讨一下NumPy中的结构化数组。
结构化数组中的字段field
因为结构化数组中包含了不同类型的对象,所以每一个对象类型都被称为一个field。
每个field都有3部分,分别是:string类型的name,任何有效dtype类型的type,还有一个可选的title。
看一个使用filed构建dtype的例子:
In [165]: np.dtype([('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
Out[165]: dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
我们可以使用上面的dtype类型来构建一个新的数组:
In [166]: x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)],
...: dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
...:
In [167]: x
Out[167]:
array([('Rex', 9, 81.), ('Fido', 3, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
x是一个1维数组,每个元素都包含三个字段,name,age和weight。并且分别指定了他们的数据类型。
可以通过index来访问一行数据:
In [168]: x[1]
Out[168]: ('Fido', 3, 27.)
也可以通过name来访问一列数据 :
In [170]: x['name']
Out[170]: array(['Rex', 'Fido'], dtype='<U10')
还可以给所有的列统一赋值:
In [171]: x['age']
Out[171]: array([9, 3], dtype=int32)
In [172]: x['age'] = 10
In [173]: x
Out[173]:
array([('Rex', 10, 81.), ('Fido', 10, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
结构化数据类型
上面的例子让我们对结构化数据类型有了一个基本的认识。结构化数据类型就是一系列的filed的集合。
创建结构化数据类型
结构化数据类型是从基础类型创建的,主要有下面几种方式:
从元组创建
每个元组都是(fieldname, datatype, shape)这样的格式,其中shape 是可选的。fieldname 是 field的title。
In [174]: np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))])
Out[174]: dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))])
如果fieldname是空字符的话,会以f开头的形式默认创建。
In [177]: np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')])
Out[177]: dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])
从逗号分割的dtype创建
可以选择从逗号分割的dtype类型创建:
In [178]: np.dtype('i8, f4, S3')
Out[178]: dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')])
In [179]: np.dtype('3int8, float32, (2, 3)float64')
Out[179]: dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))])
从字典创建
从字典创建是这样的格式: {'names': ..., 'formats': ..., 'offsets': ..., 'titles': ..., 'itemsize': ...}
这种写法可以指定name列表和formats列表。
offsets 指的是每个字段的byte offsets。titles 是字段的title,itemsize 是整个dtype的size。
In [180]: np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']})
Out[180]: dtype([('col1', '<i4'), ('col2', '<f4')])
In [181]: np.dtype({'names': ['col1', 'col2'],
...: ... 'formats': ['i4', 'f4'],
...: ... 'offsets': [0, 4],
...: ... 'itemsize': 12})
...:
Out[181]: dtype({'names':['col1','col2'], 'formats':['<i4','<f4'], 'offsets':[0,4], 'itemsize':12})
操作结构化数据类型
可以通过dtype 的 names 和fields 字段来访问结构化数据类型的属性:
>>> d = np.dtype([('x', 'i8'), ('y', 'f4')])
>>> d.names
('x', 'y')
>>> d.fields
mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)})
Offsets 和Alignment
对于结构化类型来说,因为一个dtype中包含了多种数据类型,默认情况下这些数据类型是不对齐的。
我们可以通过下面的例子来看一下各个类型的偏移量:
>>> def print_offsets(d):
... print("offsets:", [d.fields[name][1] for name in d.names])
... print("itemsize:", d.itemsize)
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2'))
offsets: [0, 1, 2, 6, 7, 15]
itemsize: 17
如果在创建dtype类型的时候,指定了align=True,那么这些类型之间可能会按照C-struct的结构进行对齐。
对齐的好处就是可以提升处理效率。我们看一个对齐的例子:
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True))
offsets: [0, 1, 4, 8, 16, 24]
itemsize: 32
Field Titles
每个Filed除了name之外,还可以包含title。
有两种方式来指定title,第一种方式:
In [182]: np.dtype([(('my title', 'name'), 'f4')])
Out[182]: dtype([(('my title', 'name'), '<f4')])
第二种方式:
In [183]: np.dtype({'name': ('i4', 0, 'my title')})
Out[183]: dtype([(('my title', 'name'), '<i4')])
看一下fields的结构:
In [187]: d.fields
Out[187]:
mappingproxy({'my title': (dtype('float32'), 0, 'my title'),
'name': (dtype('float32'), 0, 'my title')})
结构化数组
从结构化数据类型创建结构化数组之后,我们就可以对结构化数组进行操作了。