处理 NumPy 数组:基础知识(续)
接下来继续了解 NumPy 中数组操作的基础知识。 你将详细了解数组切片,如何改变数组的形状,以及如何进行联接和拆分。
切片是无副本视图
务必知道,切片会生成数组数据(而不是副本)的视图。 这是 NumPy 数组切片和 Python 列表切片的巨大差别。 对于 Python 列表,切片只是列表的卷影副本。 如果修改副本,它不会影响父列表。 修改 NumPy 子数组时,会修改原始列表。 请注意:当你只想处理大型数据集的一小部分而又不想更改整个数据集时,这可能会产生难以预料的后果。 让我们详细了解一下。
print(a2)
输出为:
[[12 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
从 a2 提取 2 x 2 子数组:
a2_sub = a2[:2, :2]
print(a2_sub)
输出为:
[[12 5]
[ 7 6]]
现在修改此子数组:
a2_sub[0, 0] = 99
print(a2_sub)
输出为:
[[99 5]
[ 7 6]]
a2 现在也修改了:
print(a2)
输出为:
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
切片生成视图而不是副本这一事实对于数据科学工作非常有用。 处理大型数据集时,通常会发现访问和处理这些数据集的各个部分比完全复制它们更容易。
复制数组
有时需要将一个数组中的数据复制到另一个数组,而不仅仅是创建视图。 如果需要执行此操作,请使用 copy() 方法:
a2_sub_copy = a2[:2, :2].copy()
print(a2_sub_copy)
输出为:
[[99 5]
[ 7 6]]
如果现在修改此子数组,不会涉及原始数组:
a2_sub_copy[0, 0] = 42
print(a2_sub_copy)
输出为:
[[42 5]
[ 7 6]]
对于:
print(a2)
输出为:
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
改变数组的形状
还需要通过改变数组的形状来对其进行操作。 改变形状涉及更改数组的维度数量和大小。 若要使数据满足机器学习程序或 API 的期望,这种操作很重要。
执行这种操作最灵活的方法是 reshape 方法。 例如,如果要将数字 1 到 9 置于 3 x 3 网格中,可以使用以下代码:
grid = np.arange(1, 10).reshape((3, 3))
print(grid)
输出为:
[[1 2 3]
[4 5 6]
[7 8 9]]
数据科学中执行的另一种常见操作是将一维数组转换为二维行或列矩阵。 为机器学习执行线性代数时,此操作很常见。 尽管可使用 reshape 方法来实现此目的,但更简单的方法是在切片运算中使用 newaxis 关键字。
通过 reshape 的行向量:
a = np.array([1, 2, 3])
a.reshape((1, 3))
输出为:
array([[1, 2, 3]])
通过 newaxis 的行向量:
a[np.newaxis, :]
输出为:
array([[1, 2, 3]])
通过 reshape 的列向量:
a.reshape((3, 1))
输出为:
array([[1],
[2],
[3]])
通过 newaxis 的列向量:
a[:, np.newaxis]
输出为:
array([[1],
[2],
[3]])
本课程的其余部分将详细介绍这种类型的转换。
联接和拆分数组
数据科学中另一种常见的数据操作需求是组合多个数据集。 了解如何通过 NumPy 数组执行此任务,可帮助我们在下一节(第 4 节)中使用更复杂的数据结构执行相同操作。 还经常需要将单个数组拆分为多个数组。
联接数组
若要在 NumPy 中联接数组,最常使用 np.concatenate,我们将在此处介绍此方法。 如果你发现自己需要在混合维度中专门联接数组(这种情况比较罕见),请阅读有关 np.vstack、np.hstack 和 np.dstack 的文档。
np.concatenate()
np.concatenate 采用一个元组或数组的一个列表作为其第一个参数:
a = np.array([1, 2, 3])
b = np.array([3, 2, 1])
np.concatenate([a, b])
输出为:
array([1, 2, 3, 3, 2, 1])
还可以同时连接多个数组:
c = [99, 99, 99]
print(np.concatenate([a, b, c]))
输出为:
[ 1 2 3 3 2 1 99 99 99]
还可以将 np.concatenate 用于二维数组:
grid = np.array([[1, 2, 3],
[4, 5, 6]])
默认沿第一个轴连接:
np.concatenate([grid, grid])
输出为:
array([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6]])
亲自试一试
请记住,在 NumPy 中,轴从零开始编制索引。
预测 np.concatenate([grid, grid], axis=1) 将生成什么?
提示 (展开以显示)
np.concatenate([grid, grid], axis=1)
输出为:
array([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
拆分数组
若要将数组拆分为多个较小的数组,可以使用 np.split、np.hsplit、np.vsplit 和 np.dsplit 函数。 在本课程中,我们将仅介绍最常使用的函数 (np.split)。
np.split()
首先看看一维数组的情况:
a = [1, 2, 3, 99, 99, 3, 2, 1]
a1, a2, a3 = np.split(a, [3, 5])
print(a1, a2, a3)
输出为:
[1 2 3] [99 99] [3 2 1]
请注意,N 个拆分点会生成 N + 1 个子数组。 在这种情况下,函数形成了子数组 a2a[3] 和 a[4] 作为元素。
a[4] 是位置 5 前面的一个元素(记住 Python 索引是如何进行的),即元组中的第二个输入。
a1 和 a3 从原始数组 a 中获取剩余部分。
亲自试一试
grid = np.arange(16).reshape((4, 4))
grid
输出为:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
np.split(grid, [1, 2])生成什么?np.split(grid, [1, 2], axis=1)呢?
提示 (展开以显示)
对于:
np.split(grid, [1, 2])
输出为:
[array([[0, 1, 2, 3]]),
array([[4, 5, 6, 7]]),
array([[ 8, 9, 10, 11],
[12, 13, 14, 15]])]
而对于 axis=1:
np.split(grid, [1, 2], axis=1)
输出为:
[array([[ 0],
[ 4],
[ 8],
[12]]),
array([[ 1],
[ 5],
[ 9],
[13]]),
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])]
要点
操作数据集是准备数据分析的基本部分。 你在此处学习和实践的技能将成为最复杂的数据操作(你将在本课程的后续部分学习)的基础。