简介

普通的数组就是数组中存放了同一类型的对象。而结构化数组是指数组中存放不同对象的格式。

今天我们来详细探讨一下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')})

结构化数组

从结构化数据类型创建结构化数组之后,我们就可以对结构化数组进行操作了。

赋值

我们可以从元组中对结构化数组进行赋值:

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

还可以从标量对结构化数组进行赋值:

>>> x = np.zeros(2, dtype='i8, f4, ?, S1')
>>> x[:] = 3
>>> x
array([(3, 3., True, b'3'), (3, 3., True, b'3')],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
>>> x[:] = np.arange(2)
>>> x
array([(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[:] = twofield
Traceback (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
>>> b
array([(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
>>> x
array([(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)
>>> a
array([(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.bar
array([ 2., 3.], dtype=float32)
>>> recordarr[1:2]
rec.array([(2, 3., b'World')],
dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])
>>> recordarr[1:2].foo
array([2], dtype=int32)
>>> recordarr.foo[1:2]
array([2], dtype=int32)
>>> recordarr[1].baz
b'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]: arr
Out[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.dtype
Out[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/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

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

NumPy之:结构化数组详解的更多相关文章

  1. Python数据科学手册-Numpy的结构化数组

    结构化数组 和 记录数组 为复合的.异构的数据提供了非常有效的存储 (一般使用pandas 的 DataFrame来实现) 传入的dtpye 使用 Numpy数据类型 Character Descri ...

  2. Numpy 系列(九)- 结构化数组

      简介 之前我们操作Numpy的数组时,都是通过索引来操作的.针对二维数组,使用索引可以完成对行.列的操作.但是这是非常不直观的.可以把二维数组想象成一个excel表格,如果表格没有列名,操作起来会 ...

  3. Java基础之 数组详解

    前言:Java内功心法之数组详解,看完这篇你向Java大神的路上又迈出了一步(有什么问题或者需要资料可以联系我的扣扣:734999078) 数组概念 同一种类型数据的集合.其实数组就是一个容器. 数组 ...

  4. 3.awk数组详解及企业实战案例

    awk数组详解及企业实战案例 3.打印数组: [root@nfs-server test]# awk 'BEGIN{array[1]="zhurui";array[2]=" ...

  5. Scala 深入浅出实战经典 第53讲:Scala中结构类型实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  6. JavaScript进阶(十)Array 数组详解

    JS array 数组详解 数组的声明方法 arrayObj = new Array(); 的数组 ,并且第一位是5 数组的运算(传地址) var t2=new Array(); t2[0]=1; t ...

  7. iOS 组件化流程详解(git创建流程)

    [链接]组件化流程详解(一)https://www.jianshu.com/p/2deca619ff7e

  8. “全栈2019”Java第三十一章:二维数组和多维数组详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. “全栈2019”Java第三十章:数组详解(下篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. Spring IoC总结

    Spring 复习 1.Spring IoC 1.1 基本概念 1.1.1 DIP(Dependency Inversion Principle) 字面意思依赖反转原则,即调用某个类的构造器创建对象时 ...

  2. 番外----python入门----pip相关

    pip 是 Python 包管理工具,该工具提供了对Python 包的查找.下载.安装.卸载的功能. 但是,由于pip使用的pip仓库默认为:http://pypi.python.org/ 是国外的 ...

  3. CVE-2019-20372-Nginx error_page 请求走私

    一.漏洞简介 Nginx 1.17.7之前版本中 error_page 存在安全漏洞.攻击者可利用该漏洞读取未授权的Web页面. 二.漏洞影响 Ngnix < 1.17.7 三.复现过程 错误代 ...

  4. SQL SERVER跨数据库服务,联表进行查询

    SELECT * FROM 数据库A..表A a, 数据库B..表B b WHERE a.field=b.field

  5. FreeBSD jail 折腾记(一)

    创建jail目录 mkdir -p /usr/jail/ 放入基本系统 方案一 make buildworld #编译基本系统 make installworld DESTDIR=/usr/jail/ ...

  6. Apache配置 11. 访问控制-user_agent

    (1)介绍 user_agent是指用户浏览器端的信息.比如你是用IE的还是Firefox浏览器的.有些网站会根据这个来调整打开网站的类型,如是手机的就打开wap,显示非手机的就打开PC常规页面. ( ...

  7. Java8的新特性--并行流与串行流

    目录 写在前面 Fork/Join框架 Fork/Join框架与传统线程池的区别 传统的线程池 Fork/Join框架 Fork/Join框架的使用 Java8中的并行流 写在前面 我们都知道,在开发 ...

  8. 2018ICPC南京D. Country Meow

    题目: 题意:三维里有n个点,找一个最小的球将所有点覆盖. 题解:退火法模拟的一道板子题. 1 #include <stdio.h> 2 #include <iostream> ...

  9. P1308_统计单词数(JAVA语言)

    题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给 ...

  10. python那些需要知道的事儿——逻辑运算与比大小

    一.逻辑运算 逻辑运算符: and   or   not,结果为布尔值(True和False) 1.基本逻辑运算符介绍 not :将后面的逻辑运算结果取反 >>> not 1 < ...