2.1 数组、形状、轴与数据类型
第 1 章建立了一份契约:每行和每个字段都有声明好的含义,每个结果都能够复现。NumPy 提供了一种紧凑的计算结构,让这份契约进入数值运算。
NumPy 是 Numerical Python 的简称,它的核心是多维数组(multidimensional array),对应类型名 ndarray。数组并不是用方括号写得更短的 Python 列表。它具有固定的矩形形状、带编号的轴,而且通常只使用一种数据类型。这些约束使 NumPy 能够紧凑存储数值并高效执行运算。
按照惯例,用别名 np 导入 NumPy:
import numpy as np从 Python 列表得到 ndarray
假设三行代表周一到周三,四列代表北区、中区、南区和西区的配送时间:
delivery_minutes = np.array([
[31, 38, 44, 36],
[29, 41, 47, 35],
[33, 39, 42, 37],
])输入形式很像嵌套列表,但产生的对象带有数组元数据:
print(delivery_minutes.ndim) # 2
print(delivery_minutes.shape) # (3, 4)
print(delivery_minutes.size) # 12
print(delivery_minutes.dtype) # 64 位系统上通常是 int64ndim 是轴的数量;shape 按顺序给出每条轴的长度;size 是元素总数,对矩形数组来说等于各维长度之积;dtype 描述每个元素怎样存储。
形状不是装饰信息,它承载着分析含义:
- 第 0 轴长度为 3,代表日期。
- 第 1 轴长度为 4,代表区域。
- 坐标
[1, 2]表示第二个日期、第三个区域的值,因为 Python 从零开始编号。
如果另一位分析者误以为行代表区域,后续计算即使正常运行,汇总标签也会全部颠倒。应把轴的含义放在数组附近,用变量名、注释或配套标签记录下来。
有意图地创建数组
np.array 用来转换已有值。NumPy 也为常见结构提供了构造函数:
zeros = np.zeros((3, 4), dtype=np.float64)
ones = np.ones((2, 3), dtype=np.int64)
placeholder = np.full((2, 2), fill_value=-1)
hours = np.arange(14, 22)
grid = np.arange(12).reshape(3, 4)np.arange(14, 22) 遵循 Python 的左闭右开规则:从 14 开始,在 22 以前停止。reshape(3, 4) 改变坐标系统,却不改变 12 个值。目标形状必须能够容纳同样数量的元素;12 个元素不能变成 (5, 3),因为后者需要 15 个值。
不要随意使用占位数并把它当成真实数据。只有当模式明确规定配送时间不可能为负,而且代码会把 -1 当作哨兵值处理时,-1 才能安全表示特殊状态。本章稍后会用 np.nan 更清晰地表示缺失的浮点观测。
一个数组,一种 dtype
普通 NumPy 数组与 Python 列表不同,它是同质的(homogeneous):所有元素共享一种数据类型(data type, dtype)。输入类型不同时,NumPy 会通过类型提升(type promotion)寻找共同表示。
whole = np.array([31, 38, 44])
mixed_numeric = np.array([31, 38.5, 44])
mixed_text = np.array([31, "missing", 44])
print(whole.dtype) # 整数 dtype
print(mixed_numeric.dtype) # 浮点 dtype
print(mixed_text.dtype) # Unicode 字符串 dtypemixed_numeric 中的整数都能表示为浮点数,所以整个数组被提升为浮点类型。单词 "missing" 无法表示成数值,因此第三个数组会变成文本。在清洗并转换以前,对它调用 .mean() 之类的算术操作没有意义。
也可以显式指定 dtype:
precise = np.array([31, 38.5, 44], dtype=np.float64)
truncated = np.array([31, 38.5, 44], dtype=np.int32)第二次转换会把 38.5 变成 38。NumPy 只是遵照指令执行,并不知道小数部分是否重要。因此,显式转换不仅是性能设置,也是一项数据决策。
根据含义与范围选择 dtype
常见 dtype 包括 int32、int64 等有符号整数、无符号整数、float32、float64 等浮点数、布尔值和定宽 Unicode 字符串。名称中的数字通常表示每个元素占用的位数。位数越多,通常能够支持更大的整数范围或更高的浮点精度,同时占用更多内存。
不要仅仅因为当前样例装得下,就选择最小 dtype。有符号 8 位整数只能表示 到 。接近边界的数继续相加时可能溢出并绕回意外结果。同样,float32 适用于许多大型数组和机器学习任务,却比 float64 保留更少的有效数字。
转换前先检查:
print(values.min(), values.max())
converted = values.astype(np.float64)astype 通常会创建转换后的数组。使用它以前,应验证缺失标记、数值范围和小数值。转换成功只说明操作可以执行,并不能证明结果保留了原有含义。
下一节会把数组当作坐标系统。我们将选择单个值、矩形窗口和满足多项条件的记录,同时继续区分“位置”和“数据含义”。