NumPy 数组:分析专用的数据结构
在本节的讨论开始时,我们提到,数据科学始于将数据表示为数字数组。
你可能在想“稍等!” “不能就使用 Python 列表来实现这个目的吗?”
取决于数据,是的,可以(并且以 Python 处理数据就包括使用列表)。 但为了了解我们需要专用数据结构的原因,让我们来详细了解一下列表。
Python 中的列表
Python 列表只能保存一种对象。 让我们使用一种对象,创建一个全为整数的列表:
myList = list(range(10))
myList
输出为:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
还记得列表理解吗? 可以使用它来探测列表中项的数据类型:
[type(item) for item in myList]
输出为:
[int, int, int, int, int, int, int, int, int, int]
当然,Python 列表的一个便利功能是,它们可以在单个列表对象中保存异类数据:
myList2 = [True, "2", 3.0, 4]
[type(item) for item in myList2]
输出为:
[bool, str, float, int]
但这种灵活性有一定的代价。 列表中的每一项实际上都是一个单独的 Python 对象。 (列表本身就是一个对象,但大体上,这是一个作为容器的对象,用于存储指向构成对象的内存指针。)这意味着列表中的每一项都必须包含其自身的类型信息、引用计数和其他信息。
如果我们在列表中处理成千上万或数百万项,则所有这些信息都可能会消耗大量的内存和性能。 而且,对于数据科学中的许多用途,我们的数组只能存储单一类型的数据(如整数或浮点数)。 因此,此类数组中项的所有对象相关信息都是冗余的。 将数据存储在固定类型的数组中会更有效。
输入固定类型 NumPy 样式数组。
Python 中的固定类型数组
在计算机的实现层面上,ndarray 是 NumPy 包的一部分,其中包含一个指向一个连续数据块的指针。 这在内存和计算方面效率很高。 更好的是,NumPy 对 对象中存储的数据提供高效运算。
备注
在此部分中,我们将频繁地交替使用“数组”、“NumPy 数组”和“ndarray”来表示 ndarray 对象。
创建 NumPy 数组,方法 1:使用 Python 列表
可通过多种方法在 NumPy 中创建数组。 首先使用我们熟悉的传统 Python 列表。 我们将使用 np.array() 函数来执行此操作。 (请记住,我们已将 NumPy 导入为“np”。)
创建整数数组:
np.array([1, 4, 2, 5, 3])
输出为:
array([1, 4, 2, 5, 3])
请记住,与 Python 列表不同,NumPy 将数组约束为包含单一类型。 因此,如果送到 NumPy 数组中的数据类型不匹配,则 NumPy 将尝试向上转换它们(如果可能)。 例如,以下 NumPy 将整数向上转换为浮点数:
np.array([3.14, 4, 2, 3])
输出为:
array([3.14, 4. , 2. , 3. ])
亲自试一试
如果使用包含整数、浮点数和字符串组合的列表构建数组,会发生什么情况?
提示 (展开以显示)
np.array([3.14, 'pi', 3])
输出为:
array(['3.14', 'pi', '3'], dtype='<U32')
如果要在创建数组时显式设置数组的数据类型,可以使用 dtype 关键字:
np.array([1, 2, 3, 4], dtype='float32')
输出为:
array([1., 2., 3., 4.], dtype=float32)
亲自试一试
用不同的 dtype 试试。
提示 (展开以显示)
np.array([1.0, 2.5, 3, 4], dtype='int32')
输出为:
array([1, 2, 3, 4])
请记住,你可始终使用命令 np.array 引用文档。
在数据科学的许多应用程序中非常有用,NumPy 数组明显可以是多维的(如矩阵或张量)。 下面是使用列表的列表创建多维数组的一种方法。
嵌套列表可导致多维数组:
np.array([range(i, i + 3) for i in [2, 4, 6]])
输出为:
array([[2, 3, 4],
[4, 5, 6],
[6, 7, 8]])
在列表的列表中,内部列表被视为你创建的二维数组的行。
创建 NumPy 数组,方法 2:从头开始构建
在实践中,使用 NumPy 内置的函数从头开始创建数组通常更有效(特别是对于较大的数组)。 以下是一些示例。 这些示例将帮助介绍多个有用的 NumPy 函数。
创建一个长度为 10 的整数数组,并以 0 填充:
np.zeros(10, dtype=int)
输出为:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
创建一个 3 x 5 浮点数组,并以 1 填充:
np.ones((3, 5), dtype=float)
输出为:
array([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])
创建一个 3 x 5 数组,并以 3.14 填充。 元组中的第一个数字给出行数。 元组中的第二个数字设置列数。
np.full((3, 5), 3.14)
输出为:
array([[3.14, 3.14, 3.14, 3.14, 3.14],
[3.14, 3.14, 3.14, 3.14, 3.14],
[3.14, 3.14, 3.14, 3.14, 3.14]])
创建一个用线性序列填充的数组。 从 0 开始,到 20 结束,步长为 2。 (这类似于内置的 Python range() 函数。)
np.arange(0, 20, 2).
输出为:
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
创建一个 5 个值均匀分布的数组,值介于 0 到 1 之间:
np.linspace(0, 1, 5)
输出为:
array([0. , 0.25, 0.5 , 0.75, 1. ])
创建一个 3 x 3 均匀分布随机值数组,值介于 0 到 1 之间:
np.random.random((3, 3))
输出为:
array([[0.1293533 , 0.00963681, 0.76015197],
[0.97076867, 0.16947551, 0.51899825],
[0.28123745, 0.37741323, 0.01221669]])
创建一个 3 x 3 正态分布随机值数组,平均值为 0、标准偏差为 1:
np.random.normal(0, 1, (3, 3))
输出为:
array([[ 0.41781774, 1.10706673, -1.84875856],
[ 0.9436157 , 0.36446661, 0.1319522 ],
[-1.18743752, 0.48199796, 0.37693047]])
创建一个 3 x 3 随机整数数组,其间隔为 [0, 10):
np.random.randint(0, 10, (3, 3))
输出为:
array([[0, 3, 7],
[8, 1, 8],
[0, 5, 7]])
创建 3 x 3 单位矩阵:
np.eye(3)
输出为:
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
创建一个包含三个整数的未初始化数组。 无论发生什么情况,这些值都将位于那个内存位置。
np.empty(3)
输出为:
array([1., 1., 1.])
现在需要花费几分钟时间返回来使用这些代码片段,并更改参数。 这些函数是创建 NumPy 数组的必备工具。 你需要熟悉它们。
下表列出了 NumPy 中的多个数组创建函数:
| 函数 | 描述 |
|---|---|
array |
通过推断 ndarray 或显式指定 dtype,将输入数据(列表、元组、数组或其他序列类型)转换为 dtype。 默认复制输入数据。 |
asarray |
如果输入已是 ndarray,则将输入转换为 ndarray 但不复制。 |
arange |
与内置的 range() 函数类似,但返回 ndarray 而不是列表。 |
ones、ones_like |
ones 生成一个全为 1 的数组,并具有给定的形状和 dtype。 ones_like 使用另一个数组,并生成一个形状和 dtype 相同的 1 数组。 |
zeros、zeros_like |
与 ones 和 ones_like 相似,但生成的是 0 数组。 |
empty、empty_like |
通过分配新内存来创建新数组,但不要像 ones 和 zeros 那样填充值。 |
full、full_like |
full 生成给定形状和 dtype 的数组,并将所有值设置为指示的“填充值”。full_like 使用另一个数组,并生成一个形状和 dtype 相同的已填充的数组。 |
eye、identity |
创建一个方形 N x N 单位矩阵(对角线上为 1,其他位置为 0)。 |
NumPy 数据类型
下表列出了标准的 NumPy 数据类型。 请注意,构造数组时,可以使用字符串指定数据类型:
np.zeros(8, dtype='int16')
或者,可使用 NumPy 对象直接指定数据类型:
np.zeros(8, dtype=np.int16)
| 数据类型 | 描述 |
|---|---|
bool_ |
存储为字节的布尔值(True 或 False) |
int_ |
默认整数类型(与 C long 相同;通常为 int64 或 int32) |
intc |
与 C int 相同(通常为 int32 或 int64) |
intp |
用于索引的整数(与 C ssize_t 相同;通常为 int32 或 int64) |
int8 |
字节(-128 到 127) |
int16 |
整数(-32768 到 32767) |
int32 |
整数(-2147483648 到 2147483647) |
int64 |
整数(-9223372036854775808 到 9223372036854775807) |
uint8 |
无符号整数(0 到 255) |
uint16 |
无符号整数(0 到 65535) |
uint32 |
无符号整数(0 到 4294967295) |
uint64 |
无符号整数(0 到 18446744073709551615) |
float_ |
float64 的简写 |
float16 |
半精度浮点数:符号位,5 位指数,10 位尾数 |
float32 |
单精度浮点数:符号位,8 位指数,23 位尾数 |
float64 |
双精度浮点数:符号位,11 位指数,52 位尾数 |
complex_ |
complex128 的简写 |
complex64 |
复数,由 2 个 32 位浮点数表示 |
complex128 |
复数,由 2 个 64 位浮点数表示 |
如果这些数据类型看起来像 C 中的数据类型,这是因为 NumPy 是使用 C 生成的。
要点
NumPy 数组是类似于 Python 列表的数据结构,可在存储和处理大量同类数据时提供高性能,而这种数据正是数据科学中经常遇到的数据类型。 NumPy 数组支持许多数据类型,本课程未全部列出。 也就是说,不必费心记住所有 NumPy dtypes。 通常只需关心你要处理的常用数据类型:浮点、整数、布尔值、字符串或常规 Python 对象。