1.介绍
结构数组是NumPy
中的一种高级数据结构,它允许用户在单个数组中存储多种数据类型的元素。与普通的NumPy
数组不同,结构数组的每个元素可以具有不同的数据类型,并且可以使用字段名来引用这些元素。这使得结构数组非常适合处理表格数据、数据库查询结果以及其他复杂数据结构。
2. 创建数组
2.1 字符串式声明
import numpy as np
if __name__ == '__main__':
# 定义类型
dt = "U10,i4,f"
# 创建数组
arr = np.array([
[("Go", 2, 8.5)],
[("Java", 3, 8.0)],
[("Python", 1, 9.0)],
], dtype=dt)
print(arr)
"""
[[('Go', 2, 8.5)]
[('Java', 3, 8. )]
[('Python', 1, 9. )]]
"""
@注:字符串式声明比较简单,只要使用逗号隔开即可,如上述示例:
i1,i4,f
2.2 元组列表式声明
a. 声明类型:
dt = np.dtype([("字段名", "数据类型"),...,("字段名n", "数据类型n")])
b. 使用示例:
import numpy as np
if __name__ == '__main__':
# 定义类型
dt = np.dtype([("name", "U10"), ("age", int), ("address", "U20"), ("weight", float)])
# 创建数组
arr = np.array([
[("张三", 20, "北京昌平区", 74.5)],
[("小花", 18, "上海徐汇区", 48.5)]
], dtype=dt)
print("创建数组:\n", arr)
print("访问arr[0]:\n", arr[0])
print("访问arr['name']:\n", arr['name'])
print("访问arr['age']:\n", arr['age'])
"""
创建数组:
[[('张三', 20, '北京昌平区', 74.5)]
[('小花', 18, '上海徐汇区', 48.5)]]
访问arr[0]:
[('张三', 20, '北京昌平区', 74.5)]
访问arr['name']:
[['张三']
['小花']]
访问arr['age']:
[[20]
[18]]
"""
2.3 字段标题
元组列表除了上面的使用示例,还可以给字段加上标题,使用方式和语法如下:
a. 声明类型:
dt = np.dtype([(("标题", "字段名"), "数据类型"), ... ,(("标题n", "字段名n"), "数据类型")])
b. 使用示例:
import numpy as np
if __name__ == '__main__':
# 定义类型
dt = np.dtype([(("姓名", "name"), "U10"), (("年龄", "age"), int), (("体重", "weight"), float)])
# 创建数组
arr = np.array([
[("张三", 28, 80.23)],
[("小明", 16, 66.55)],
], dtype=dt)
print("创建数组:\n", arr)
print("访问 字段标题-> \n", arr["姓名"])
print("访问 字段名-> \n", arr["name"])
"""
创建数组:
[[('张三', 28, 80.23)]
[('小明', 16, 66.55)]]
访问 字段标题->
[['张三']
['小明']]
访问 字段名->
[['张三']
['小明']]
"""
2.4 字典式声明
a. 声明类型:
dt = {'names':('字段1',...'字段2'), 'formats':('类型1',...,'类型n')}
b. 使用示例:
import numpy as np
if __name__ == '__main__':
# 定义类型
dt = {"names": ("name", "age", "address", "weight"), "formats": ("U10", "i", "U20", "f")}
# 创建数组
arr = np.array([
[("张三", 20, "北京昌平区", 74.5)],
[("小花", 18, "上海徐汇区", 48.5)]
], dtype=dt)
print("创建数组:\n", arr)
print("访问arr[0]:\n", arr[0])
print("访问arr['name']:\n", arr['name'])
print("访问arr['age']:\n", arr['age'])
"""
创建数组:
[[('张三', 20, '北京昌平区', 74.5)]
[('小花', 18, '上海徐汇区', 48.5)]]
访问arr[0]:
[('张三', 20, '北京昌平区', 74.5)]
访问arr['name']:
[['张三']
['小花']]
访问arr['age']:
[[20]
[18]]
"""
3. 类型说明
3.1 类型汇总
以下是NumPy
结构数组的常见数据类型的列表,包括类型、字符代码和说明。
类型 | 字符代码 | 说明 |
---|---|---|
int | 'i' | 整数数据类型 |
int8 | 'i1' | 8位有符号整数类型 |
int16 | 'i2' | 16位有符号整数类型 |
int32 | 'i4' | 32位有符号整数类型 |
int64 | 'i8' | 64位有符号整数类型 |
uint8 | 'u1' | 8位无符号整数类型 |
uint16 | 'u2' | 16位无符号整数类型 |
uint32 | 'u4' | 32位无符号整数类型 |
uint64 | 'u8' | 64位无符号整数类型 |
float | 'f' | 浮点数数据类型 |
float16 | 'f2' | 16位浮点数类型 |
float32 | 'f4' | 32位浮点数类型 |
float64 | 'f8' | 64位浮点数类型 |
complex | 'c' | 复数数据类型 |
complex64 | 'c8' | 64位复数类型 |
complex128 | 'c16' | 128位复数类型 |
bool | 'b' | 布尔值数据类型 |
object | 'O' | 通用对象数据类型 |
string | 'S' | 字符串数据类型 |
unicode | 'U' | Unicode字符串数据类型 |
void | 'V' | 通用无类型数据类型 |
datetime | 'M' | 日期和时间数据类型 |
timedelta | 'm' | 时间间隔数据类型 |
数据类型的字符代码,用在
NumPy
中,表示对应的数据类型。
3.2 时间类型使用
import numpy as np
if __name__ == '__main__':
# 定义类型
dt = [
('day', 'datetime64[D]'), # 表示日期,精确到天
('minutes', 'datetime64[m]'), # 表示时间,精确到分钟
('second', 'datetime64[s]') # 表示时间,精确到秒
]
arr = np.array([
('2023-09-25', '2023-09-25T14:30', '2023-09-25T14:30:00'),
('2023-09-25', '2023-09-25T17:30', '2023-09-25T14:30:45')
], dtype=dt)
print("arr: ", arr)
print("计算时间差:", arr['minutes'][1] - arr['minutes'][0])
print("计算时间差:", arr['second'][1] - arr['second'][0])
"""
arr: [('2023-09-25', '2023-09-25T14:30', '2023-09-25T14:30:00')
('2023-09-25', '2023-09-25T17:30', '2023-09-25T14:30:45')]
计算时间差: 180 minutes
计算时间差: 45 seconds
"""
'datetime64[D]:'
精确到天,而'datetime64[m]:'
,精确到分钟,datetime64[s]:
精确到秒
4. 字符串
在
Numpy
使用字符串类型时,常见会是这种格式S10、U20
等,其中的10,20
指的就是字符的长度,单位是字节。
4.1 对比示例
在NumPy
结构数组中,使用字符串类型时一定要指定长度,因为字符串类型,需要在内存中预先分配内存,换句话说如果你不指定长度,那么值是存不进去的,如下示例:
import numpy as np
if __name__ == '__main__':
# 定义字符串--不指定长度
arr = np.array([
[("张三", 80.5)],
[("李四", 67.5)],
], dtype="U,f")
print("定义字符串--不指定长度: \n", arr)
# 定义字符串--指定长度
arr2 = np.array([
[("李白", 90)],
[("苏轼", 88)],
], dtype="U10,f")
print("定义字符串--指定长度: \n", arr2)
"""
定义字符串--不指定长度:
[[('', 80.5)]
[('', 67.5)]]
定义字符串--指定长度:
[[('张三', 80.5)]
[('李四', 67.5)]]
"""
4.2 原因汇总
使用字符串指定长度的原因,除了上面说的内存预分配的原因,还有其他原因,下面进行了汇总:
NumPy
无法确定要为每个字符串分配多少内存,这会导致内存分配错误或不确定性,可能会破坏数据的完整性。NumPy
为每个元素的字符串字段分配相应大小的内存块,确保数据存储的正确性。4.3 S、U的区别
在NumPy
中,字符串类型有两种常见的类型,分别是S(string)
(字节字符串)和U(Unicode)
(字符串)。这两种类型在处理字符串数据时具有不同的特性和用途,以下是它们的对比:
- 字符编码:S字符串使用字节编码,通常用于处理
ASCII
字符集。这意味着它不支持多语言字符或特殊字符,只能表示ASCII
字符。 - 存储效率:
S
字符串在内存中以字节为单位存储,因此在存储方面比U
字符串更加紧凑,但只能表示有限的字符集。 - 示例:
'S10'
表示包含最多10个ASCII字符的S字符串,例如'Hello'
。 - 适用场景:当你知道字符串只包含ASCII字符或需要节省内存时,可以选择使用S字符串。常见用途包括文件名、用户名、产品代码等。
- 字符编码:
U
字符串使用Unicode
编码,支持多语言字符、特殊字符和表情符号等。它更加通用,适用于处理各种语言的文本。 - 存储效率:
U
字符串在内存中以Unicode
字符的形式存储,因此通常会占用更多的内存。但它具有更广泛的字符支持。 - 示例:
'U20'
表示包含最多20个Unicode
字符的U
字符串,例如'你好,世界'
。 - 适用场景:当你需要处理多语言字符、特殊字符、国际化文本或包含
Unicode
字符的数据时,通常应选择使用U
字符串。它适用于Web
应用、国际化应用程序、文本处理等场景。
@注:当你需要存储中文字符时, 使用
Unicode
5. 字节顺序
5.1 概念说明
大端字节顺序(Big-Endian
)和小端字节顺序(Little-Endian
)是两种用于存储多字节数据(如整数、浮点数)的不同方式。它们决定了在内存中多字节数据的字节存储顺序。
5.2 大端字节顺序
在大端字节顺序中,数据的高位字节(Most Significant Byte,MSB
)存储在内存的低地址处,而低位字节(Least Significant Byte,LSB
)存储在内存的高地址处。这就好像你阅读英文文本,从左到右逐个字母阅读一样。
示例: 假设我们要存储整数值0x1234
(十进制为4660),在大端字节顺序下,在内存中的存储方式是:
高地址 --> 0x12 | 0x34 0x34 | 0x12
:指定大端字节顺序。