简介

一般的数组就是数组中寄存了同一类型的对象。而结构化数组是指数组中寄存不同对象的格局。

明天咱们来具体探讨一下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]: xOut[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'] = 10In [173]: xOut[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.fieldsmappingproxy({'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.fieldsOut[187]:mappingproxy({'my title': (dtype('float32'), 0, 'my title'),              'name': (dtype('float32'), 0, 'my title')})

结构化数组

从结构化数据类型创立结构化数组之后,咱们就能够对结构化数组进行操作了。

赋值

咱们能够从元组中对结构化数组进行赋值:

>>> x = np.array([(1, 2, 3), (4, 5, 6)], dtype='i8, f4, f8')>>> x[1] = (7, 8, 9)>>> xarray([(1, 2., 3.), (7, 8., 9.)],     dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')])

还能够从标量对结构化数组进行赋值:

>>> x = np.zeros(2, dtype='i8, f4, ?, S1')>>> x[:] = 3>>> xarray([(3, 3., True, b'3'), (3, 3., True, b'3')],      dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])>>> x[:] = np.arange(2)>>> xarray([(0, 0., False, b'0'), (1, 1., True, b'1')],      dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])

结构化数组还能够赋值给非机构化数组,然而前提是结构化数组只有一个filed:

>>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')])>>> onefield = np.zeros(2, dtype=[('A', 'i4')])>>> nostruct = np.zeros(2, dtype='i4')>>> nostruct[:] = twofieldTraceback (most recent call last):...TypeError: Cannot cast array data from dtype([('A', '<i4'), ('B', '<i4')]) to dtype('int32') according to the rule 'unsafe'

结构化数组还能够相互赋值:

>>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')])>>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')])>>> b[:] = a>>> barray([(0., b'0.0', b''), (0., b'0.0', b''), (0., b'0.0', b'')],      dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')])

拜访结构化数组

之前讲到了,能够通过filed的名字来拜访和批改一列数据:

>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])>>> x['foo']array([1, 3])>>> x['foo'] = 10>>> xarray([(10, 2.), (10, 4.)],      dtype=[('foo', '<i8'), ('bar', '<f4')])

返回的数值是原始数组的一个视图,他们是共享内存空间的,所以批改视图同时也会批改原数据。

看一个filed是多维数组的状况:

In [188]: np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])Out[188]: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.]]),        (0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])]],      dtype=[('a', '<i4'), ('b', '<f8', (3, 3))])

下面构建了一个2 2 的矩阵,这个矩阵中的第一列是int类型,第二列是一个3 3 的float矩阵。

咱们能够这样来查看各个列的shape值:

>>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])>>> x['a'].shape(2, 2)>>> x['b'].shape(2, 2, 3, 3)

除了单列的拜访之外,咱们还能够一次拜访多列数据:

>>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')])>>> a[['a', 'c']]array([(0, 0.), (0, 0.), (0, 0.)],     dtype={'names':['a','c'], 'formats':['<i4','<f4'], 'offsets':[0,8], 'itemsize':12})

多列同时赋值:

>>> a[['a', 'c']] = (2, 3)>>> aarray([(2, 0, 3.), (2, 0, 3.), (2, 0, 3.)],      dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<f4')])

简略的替换列的数据:

>>> a[['a', 'c']] = a[['c', 'a']]

Record Arrays

结构化数组只能通过index来拜访,很不不便,为此NumPy提供了一个多维数组的子类 numpy.recarray, 而后能够通过属性来拜访。

咱们来看几个例子:

>>> recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")],...                    dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])>>> recordarr.bararray([ 2.,  3.], dtype=float32)>>> recordarr[1:2]rec.array([(2, 3., b'World')],      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])>>> recordarr[1:2].fooarray([2], dtype=int32)>>> recordarr.foo[1:2]array([2], dtype=int32)>>> recordarr[1].bazb'World'

recarray返回的后果是一个rec.array。除了应用np.rec.array来创立之外,还能够应用view:

In [190]: arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],     ...: ...                dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'a10')])     ...:In [191]: arrOut[191]:array([(1, 2., b'Hello'), (2, 3., b'World')],      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])In [192]: arr.view(dtype=np.dtype((np.record, arr.dtype)),     ...: ...                      type=np.recarray)     ...:Out[192]:rec.array([(1, 2., b'Hello'), (2, 3., b'World')],          dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])

如果是rec.array对象,它的dtype类型会被主动转换成为np.record类型:

In [200]: recordarr.dtypeOut[200]: dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]))

想要转换回原始的np.ndarray类型能够这样:

In [202]: recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray)Out[202]:array([(1, 2., b'Hello'), (2, 3., b'World')],      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])

如果通过index或者field来拜访rec.array对象的字段,如果字段是构造类型,那么会返回numpy.recarray,如果是非构造类型,则会返回numpy.ndarray:

>>> recordarr = np.rec.array([('Hello', (1, 2)), ("World", (3, 4))],...                 dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])])>>> type(recordarr.foo)<class 'numpy.ndarray'>>>> type(recordarr.bar)<class 'numpy.recarray'>

本文已收录于 http://www.flydean.com/05-python-structured-arrays/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!