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')})
结构化数组
从结构化数据类型创建结构化数组之后,我们就可以对结构化数组进行操作了。
赋值
我们可以从元组中对结构化数组进行赋值:
>>> 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之:结构化数组详解的更多相关文章
- Python数据科学手册-Numpy的结构化数组
结构化数组 和 记录数组 为复合的.异构的数据提供了非常有效的存储 (一般使用pandas 的 DataFrame来实现) 传入的dtpye 使用 Numpy数据类型 Character Descri ...
- Numpy 系列(九)- 结构化数组
简介 之前我们操作Numpy的数组时,都是通过索引来操作的.针对二维数组,使用索引可以完成对行.列的操作.但是这是非常不直观的.可以把二维数组想象成一个excel表格,如果表格没有列名,操作起来会 ...
- Java基础之 数组详解
前言:Java内功心法之数组详解,看完这篇你向Java大神的路上又迈出了一步(有什么问题或者需要资料可以联系我的扣扣:734999078) 数组概念 同一种类型数据的集合.其实数组就是一个容器. 数组 ...
- 3.awk数组详解及企业实战案例
awk数组详解及企业实战案例 3.打印数组: [root@nfs-server test]# awk 'BEGIN{array[1]="zhurui";array[2]=" ...
- Scala 深入浅出实战经典 第53讲:Scala中结构类型实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- JavaScript进阶(十)Array 数组详解
JS array 数组详解 数组的声明方法 arrayObj = new Array(); 的数组 ,并且第一位是5 数组的运算(传地址) var t2=new Array(); t2[0]=1; t ...
- iOS 组件化流程详解(git创建流程)
[链接]组件化流程详解(一)https://www.jianshu.com/p/2deca619ff7e
- “全栈2019”Java第三十一章:二维数组和多维数组详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第三十章:数组详解(下篇)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
随机推荐
- Layui 源码浅读(模块加载原理)
经典开场 // Layui ;! function (win) { var Lay = function () { this.v = '2.5.5'; }; win.layui = new Lay() ...
- Win10下ctrl与alt键互换
我之前尝试过用第三方软件修改,但是总是不成功,后来发现直接去修改注册表也不麻烦,记录一下步骤. win + r 输入 regedit 进到这个路径 点击Keyboard Layout 右键,新建一个 ...
- 基于CefSharp开发浏览器(八)浏览器收藏夹栏
一.前言 上一篇文章 基于CefSharp开发(七)浏览器收藏夹菜单 简单实现了部分收藏夹功能 如(添加文件夹.添加收藏.删除.右键菜单部分功能) 后续代码中对MTreeViewItem进行了扩展,增 ...
- Python Flask框架路由简单实现
Python Flask框架路由的简单实现 也许你听说过Flask框架.也许你也使用过,也使用的非常好.但是当你在浏览器上输入一串路由地址,跳转至你所写的页面,在Flask中是怎样实现的,你是否感到好 ...
- CMDB项目要点总结之中控机
1.基于paramiko对远程主机执行命令操作 秘钥形式 private_key = paramiko.RSAKey.from_private_key_file('c:/Users/用户名/.ssh/ ...
- vue项目安装sass步骤等遇到的问题
1.安装sass依赖包 npm install --save-dev sass-loader 注释(可能会出现问题:sass-loader版本过高导致,可以将其package.json中的版本改为7. ...
- C语言之简易了解程序环境
C语言之简易了解程序环境 大纲: 程序的翻译环境 预编译 编译 汇编 链接 程序的运行环境 在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机 ...
- (数据科学学习手札114)Python+Dash快速web应用开发——上传下载篇
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- [Azure Devops] 使用 Azure Repos 管理代码
1. 什么是 Azure Repos Azure Repos 是一组版本控制工具,可用于管理代码.无论您的软件项目是大型项目还是小型项目,都应尽快使用版本控制. 版本控制系统是可帮助您跟踪随时间变化对 ...
- 基于Hive进行数仓建设的资源元数据信息统计:Hive篇
在数据仓库建设中,元数据管理是非常重要的环节之一.根据Kimball的数据仓库理论,可以将元数据分为这三类: 技术元数据,如表的存储结构结构.文件的路径 业务元数据,如血缘关系.业务的归属 过程元数据 ...