8.11.结构化数组
import numpy as np
import pprint
Windows 10
Python 3.7.3 @ MSC v.1915 64 bit (AMD64)
Latest build date 2020.03.28
numpy version: 1.18.1
结构化数组常用操作
x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)],
dtype=[('name', 'U10'),
('age', 'i4'),
('weight', 'f4')])
pprint.pprint(x)
array([('Rex', 9, 81.), ('Fido', 3, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
通过索引,可以获得一个结构
pprint.pprint(x[0])
# 可以通过使用 字段名称 建立索引来访问和修改结构化数组的各个 字段:
pprint.pprint(x["name"])
pprint.pprint(x[["age","name"]])
# 根据 字段名称 做过滤
pprint.pprint(x[x["age"]>5]["name"])
# 修改全部数组的age字段
x["age"] = 20
pprint.pprint(x)
('Rex', 9, 81.)
array(['Rex', 'Fido'], dtype='<U10')
array([(9, 'Rex'), (3, 'Fido')],
dtype={'names':['age','name'], 'formats':['<i4','<U10'],
'offsets':[40,0], 'itemsize':48})
array(['Rex'], dtype='<U10')
array([('Rex', 20, 81.), ('Fido', 20, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
创建结构化数组
list of tuple
可以通过 a list of tuple 的形式创建结构化数组。
Tuple的形式是(fieldname, datatype, Shape)
。fieldname
是字符串,如果使用title,则为元组。fieldname
、Shape
可以省略,如果fieldname
省略,则用 f#
的默认名称代替,shape
如果省略,则默认为(1,)
。
np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))])
dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))])
如果 fieldname
是空字符串 ''
,则将为字段指定格式为 f#
的默认名称,其中 #
是字段的整数索引,从左侧开始从0开始计数:
np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')])
dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])
逗号分割的字符串
可以通过“A string of comma-separated dtype specifications”创建结构化数组。字符串要符合规范。
np.dtype('i8, f4, S3')
np.dtype('3int8, float32, (2, 3)float64')
dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))])
dict of field parameter arrays
通过“关键字参数组成的字典”创建结构化数组。这是最灵活的规范形式,因为它允许控制字段的字节偏移和结构的项目大小。
dict有两个必需键names
和format
,以及四个可选键 offsets
、itemsize
、Aligned
和 title
:
-
names
和format
的值应该分别是相同长度的字段名列表和dtype规范列表。 -
可选的
offsets
值应该是整数字节偏移量的列表,结构中的每个字段都有一个偏移量。如果未给出offsets
,则自动确定偏移量。 -
可选的
itemsize
值应该是一个整数, 描述dtype
的总大小(以字节为单位),它必须足够大以包含所有字段。 -
可选的
Aligned
值可以设置为True,以使自动偏移计算使用对齐的偏移量(请参阅自动字节偏移量和对齐 )。 -
可选的
titles
值应该是长度与names
相同的标题列表。
np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']})
np.dtype({'names': ['col1', 'col2'],
'formats': ['i4', 'f4'],
'offsets': [0, 4],'itemsize': 12})
dtype({'names':['col1','col2'], 'formats':['<i4','<f4'],
'offsets':[0,4], 'itemsize':12})
dict of fieldnam
通过 dict of fieldname 创建结构化数组。不鼓励使用这种形式,因为Python字典在Python 3.6之前的Python版本中不保留顺序,但结构化dtype中字段的顺序有意义。
字典的关键字是字段名称,值是指定类型和偏移量的元组:
np.dtype({'col1': ('i1', 0), 'col2': ('f4', 1)})
dtype([('col1', 'i1'), ('col2', '<f4')])
操作和显示结构化数据类型
d = np.dtype([('x', 'i8'), ('y', 'f4')])
print(d.names)
print(d.fields)
('x', 'y')
{'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)}
为什么需要结构化数组
numpy结构化数组中的用法好像类似于Python的dict of list,那为什么还要用numpy结构化数组呢?
因为numpy的结构化数组底层是类似C语言的结构,占用一块连续的内存区域,并且numpy底层是C实现,numpy数组中的类型都是静态类型的,性能好于Python的的字典列表。
可以来做一下性能比较。
import time
### 定义计时器
def timer(func):
def wrapper(self, *args, **kwargs):
start = time.process_time()
something = func(self, *args, **kwargs)
end = time.process_time()
type_str = func.__repr__()
print("当前函数:", type_str, "\n", "用时:",end - start, "秒", sep="")
return something
return wrapper
# numpy版本长一岁
@timer
def addage_numpy(data, loop):
for i in range(loop):
data['age'] += 1
# python循环长一岁
@timer
def addage_python(data, loop):
for j in range(loop):
for i in range(4):
data[i]["age"] += 1
# 创建结构化数组
names = ['Lin', 'Pan', 'Shen', 'Zhou']
ages = [28, 33, 34, 29]
grades = [25, 26, 27, 24]
data_np = np.zeros(4, dtype={
'names':('name', 'age', 'grade'),
'formats':('U10', 'i4', 'i4')
})
data_np['name'] = names
data_np['age'] = ages
data_np['grade'] = grades
data_py = []
for i in range(4):
person = {"name": names[i], "age": ages[i], "grade": grades[i]}
data_py.append(person)
addage_numpy(data_np, loop=1000000)
addage_python(data_py, loop=1000000)
当前函数:<function addage_numpy at 0x0000021944ECBA60>
用时:4.453125秒
当前函数:<function addage_python at 0x0000021944ECB9D8>
用时:1.421875秒
记录数组
记录数组和结构数组没有太大区别,它们所使用的场景也是类似的,只不过记录数组可以通过属性的方式访问数据。
创建记录数组的最简单方法是numpy.rec.array
。
recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")],
dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
print(type(recordarr))
# 通过属性访问数据
recordarr.foo
<class 'numpy.recarray'>
array([1, 2])
numpy.rec.array
可以将各种参数转换为记录数组,包括结构化数组:
arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],
dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')])
recordarr = np.rec.array(arr)
pprint.pprint(recordarr)
rec.array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])
numpy.rec
模块提供了多个便利的函数来创建记录数组,请参阅记录数组创建方法列表。
注意:numpy.rec
是numpy.core.records
的首选别名。
-
core.records.array(obj[, dtype, shape, …])
:从各种各样的对象构造一个记录数组。 -
core.records.fromarrays(arrayList[, dtype, …])
:从一维的list或array创建记录数组。 -
core.records.fromrecords(recList[, dtype, …])
:从文本格式的list创建一个记录数组。 -
core.records.fromstring(datastring[, dtype, …])
:从二进制字符串数据创建(只读)记录数组。 -
core.records.fromfile(fd[, dtype, shape, …])
:从二进制文件数据创建记录数组。