处理 NumPy 数组:基础知识

已完成

现在你已了解如何在 NumPy 中创建数组,你需要熟悉如何操作它们,原因有两个。 首先,你将处理 NumPy 数组,这是数据科学的探索过程的一部分。 其次,我们另一个重要的 Python 数据科学工具 pandas 是围绕 NumPy 构建的。 我们将在下一节(第 4 节)及后续部分介绍擅长处理 NumPy 数组带来的好处。 NumPy 数组是 Python pandas 库中 SeriesDataFrame 数据结构的构建基块。 它们将大量用于数据科学中。 为了帮助你熟悉数组操作,我们将介绍五个特定主题:

  • 数组特性:评估数组的大小、形状和数据类型
  • 对数组编制索引:获取和设置各个数组元素的值
  • 对数组进行切片:在较大的数组中获取和设置较小的子数组
  • 改变数组的形状:更改给定数组的形状
  • 联接和拆分数组:将多个数组组合并到一个数组中,以及将一个数组拆分成多个数组

数组特性

首先来看一些数组特性。 我们首先定义三个用随机数填充的数组:一个是一维数组,另一个是二维数组,最后一个是三维数组。 由于我们将使用 NumPy 的随机数生成器,因此我们将设置种子值,以确保每次运行此代码时获得相同的随机数组

import numpy as np
np.random.seed(0)  # Seed for reproducibility

a1 = np.random.randint(10, size=6)  # One-dimensional array
a2 = np.random.randint(10, size=(3, 4))  # Two-dimensional array
a3 = np.random.randint(10, size=(3, 4, 5))  # Three-dimensional array

每个数组都具有属性 ndim(数组的维度数)、shape(数组每个维度的大小)以及 size(数组中元素的总数):

print("a1 ndim: ", a1.ndim)
print("a1 shape:", a1.shape)
print("a1 size: ", a1.size)

输出为:

a1 ndim:  1
a1 shape: (6,)
a1 size:  6

亲自试一试

更改此代码片段中的值,以查看 a2a3 的特性:


提示(展开以显示)

对于 a2

print("a2 ndim: ", a2.ndim)
print("a2 shape:", a2.shape)
print("a2 size: ", a2.size)

输出为:

a2 ndim:  2
a2 shape: (3, 4)
a2 size:  12

对于 a3

print("a3 ndim: ", a3.ndim)
print("a3 shape:", a3.shape)
print("a3 size: ", a3.size)

输出为:

a3 ndim:  3
a3 shape: (3, 4, 5)
a3 size:  60




另一个有用的数组特性是 dtype,我们已在本节前面部分讨论过该特性,它是确定数组中数据类型的一种方法:

print("dtype:", a3.dtype)

输出为:

dtype: int64

亲自试一试

浏览其他数组的 dtype 值。

预测它们有哪些 dtype 值?


提示(展开以显示)
print("dtype:", a3.dtype)

输出为:

dtype: int64




对数组编制索引

NumPy 中的索引类似于标准 Python 中的索引列表。 事实上,一维数组中的索引与在 Python 列表中的效果完全相同。

请尝试:

a1

输出为:

array([5, 0, 3, 3, 7, 9])

然后尝试:

a1[0]

输出为:

5

下一步:

a1[4]

输出为:

7

与常规 Python 列表一样,若要从数组末尾开始编制索引,可以使用负索引。

例如:

a1[-1]

输出为:

9

以及:

a1[-2]

输出为:

7

亲自试一试

多维 NumPy 数组的工作方式类似于 Python 列表的列表吗?

尝试一些组合(如 a2[1][1]a3[0][2][1]),并查看返回的内容。


提示(展开以显示)
a2[1][1]

输出为:

6

当你使用:

a3[0][2][1]

输出为:

0




你可能已注意到,我们可以将多维数组视为列表的列表。 但要访问多维数组中的项,更常见的方法是使用以逗号分隔的索引元组。

(是的,我们意识到这些逗号分隔的元组使用方括号,而不是名称可能暗示的括号。它们仍称为元组。)

请尝试:

a2

输出为:

array([[3, 5, 2, 4],
       [7, 6, 8, 8],
       [1, 6, 7, 7]])

并使用:

a2[0, 0]

输出为:

3

接下来查看:

a2[2, 0]

输出为:

1

请尝试:

a2[2, -1]

输出为:

7

还可使用相同的以逗号分隔的索引表示法来修改值:

a2[0, 0] = 12
a2

输出为:

array([[12,  5,  2,  4],
       [ 7,  6,  8,  8],
       [ 1,  6,  7,  7]])

请记住,在定义NumPy 数组后,它们就具有固定的数据类型。 因此,如果尝试在整数数组中插入浮点数,将无提示地截断值。

a1[0] = 3.14159
a1

输出为:

array([3, 0, 3, 3, 7, 9])

亲自试一试

如果尝试将字符串插入 a1 中,会发生什么情况? 请尝试插入 '3''three' 这样的字符串。


提示(展开以显示)
a1[1] = '3'
a1

输出为:

array([3, 3, 3, 3, 7, 9])

但当你尝试:

a1[1] = 'three'
a1

输出为:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
 in 
----> 1 a1[1] = 'three'
      2 a1

ValueError: invalid literal for int() with base 10: 'three'




对数组进行切片

与使用方括号访问各个数组元素的方式类似,你还可以使用方括号来访问子数组。 为此,可使用切片表示法(用冒号 字符标记):。 NumPy 切片语法遵循标准 Python 列表的语法。 因此,若要访问数组 a 的切片,请使用此表示法:

a[start:stop:step]

如未指定这些元素中的任何一个,则它们的默认值为 start=0stop=size of dimensionstep=1。 接下来了解如何访问一个维度和多个维度中的子数组。

一维切片

如果使用此代码:

a = np.arange(10)
a

输出为:

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

可以获取前五个元素:

a[:5]

输出为:

array([0, 1, 2, 3, 4])

可以返回索引 5 之后的元素:

a[5:]

输出为:

array([5, 6, 7, 8, 9])

或中间子数组:

a[4:7]

输出为:

array([4, 5, 6])

下面介绍如何获取其他所有元素:

a[::2]

输出为:

array([0, 2, 4, 6, 8])

若要获取从索引 1 开始的其他所有元素:

a[1::2]

输出为:

array([1, 3, 5, 7, 9])

亲自试一试

如何访问数组 的最后五个元素?a 如何访问 a 的最后五个元素之外的其他所有元素? 请回顾 Python 中的列表索引。


提示(展开以显示)

请尝试:

a[-5:]

输出为:

array([5, 6, 7, 8, 9])

以及:

a[-5::2]

输出为:

array([5, 7, 9])




step 使用负值时,请谨慎操作。 如果 step 具有负值,则 startstop 的默认值会互换。 你可使用此功能来反转数组。

此代码提供已反转的所有元素:

a[::-1]

输出为:

array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

此代码将为你提供索引 5 中已反转的其他所有元素:

a[5::-2]

输出为:

array([5, 3, 1])

亲自试一试

如何创建一个切片,使其包含 a 中每三个元素的最后一个,从倒数第二个元素起降序排列?


提示(展开以显示)

请尝试:

a[-2::-3]

输出为:

array([8, 5, 2])




多维切片

多维切片使用与一维子数组相同的切片表示法,混合使用多维数组的以逗号分隔的表示法。 一些示例有助于说明这一概念:

a2

输出为:

array([[12,  5,  2,  4],
       [ 7,  6,  8,  8],
       [ 1,  6,  7,  7]])

两行,三列:

a2[:2, :3]

输出为:

array([[12,  5,  2],
       [ 7,  6,  8]])

所有行,每隔一列:

a2[:3, ::2]

输出为:

array([[12,  2],
       [ 7,  8],
       [ 1,  7]])

最后,子数组维度甚至可以一起反转:

a2[::-1, ::-1]

输出为:

array([[ 7,  7,  6,  1],
       [ 8,  8,  6,  7],
       [ 4,  2,  5, 12]])

访问数组行和列

操作数据时,通常需要访问数组中的单个行或单个列。 可以通过结合使用索引和切片来实现此目的。 具体而言,你将使用由单个冒号 (:) 标记的空切片。 同样,一些示例将有助于说明这一概念。

若要获取 x2 的第一列:

print(a2[:, 0])

输出为:

[12  7  1]

若要获取 x2 的第一行:

print(a2[0, :])

输出为:

[12  5  2  4]

若要访问行,可省略空切片以获取更简洁的语法:

print(a2[0])  # Equivalent to a2[0, :]

输出为:

[12  5  2  4]

亲自试一试

如何访问 a3 的第三列?

如何访问 a3 的第三行?


提示(展开以显示)
a3[:,:,2]

输出为:

array([[5, 3, 2, 3],
     [9, 3, 0, 8],
     [8, 9, 0, 4]])

而对于:

a3[2,:,:]

输出为:

array([[4, 9, 8, 1, 1],
     [7, 9, 9, 3, 6],
     [7, 2, 0, 3, 5],
     [9, 4, 4, 6, 4]])