NumPy:初学者入门绝对基础#

欢迎来到 NumPy 的绝对入门指南!

NumPy(Numerical Python)是一个开源 Python 库,广泛用于科学和工程领域。NumPy 库包含多维数组数据结构(如同质 N 维 ndarray)以及在这些数据结构上高效运行的大型函数库。在 什么是 NumPy 中,了解有关 NumPy 的更多信息,如果您有任何意见或建议,请 联系我们

如何导入 NumPy#

安装 NumPy 后,可以通过以下方式将它导入 Python 代码

import numpy as np

这种广泛应用的约定允许使用简短、可识别的前缀 (np.) 来访问 NumPy 的功能,同时将 NumPy 的功能与同名其他功能区分开来。

阅读示例代码#

在整个 NumPy 文档中,您会看到如下所示的代码段

>>> a = np.array([[1, 2, 3],
...               [4, 5, 6]])
>>> a.shape
(2, 3)

>>>... 开头的文本是输入,即您在脚本或 Python 提示符下输入的代码。其他内容都是输出,即运行代码的结果。请注意,>>>... 不是代码的一部分,如果在 Python 提示符下输入,可能会导致错误。

为什么要使用 NumPy?#

Python 列表是一种优秀的通用容器。它们可以是“异构”的,这意味着它们可以包含多种类型的元素,并且在对少数元素执行个别操作时相当快速。

根据数据特征和需要执行的操作类型,其他容器可能更合适;通过利用这些特征,我们可以提高速度,减少内存消耗,并提供用于执行各种常见处理任务的高级语法。当大量“同构”(同类型)数据需要在 CPU 上处理时,NumPy 会大放异彩。

什么是“数组”?#

在计算机编程中,数组是一种用于存储和检索数据的数据结构。我们经常把数组当作网格来谈,每个单元格存储数据的一个元素。例如,如果数据的每个元素都是一个数字,我们可能会将“一维”数组视为一个列表

\[\begin{split}\begin{array}{|c||c|c|c|} \hline 1 & 5 & 2 & 0 \\ \hline \end{array}\end{split}\]

二维数组则像一张表

\[\begin{split}\begin{array}{|c||c|c|c|} \hline 1 & 5 & 2 & 0 \\ \hline 8 & 3 & 6 & 1 \\ \hline 1 & 7 & 2 & 9 \\ \hline \end{array}\end{split}\]

三维数组就像一组表,可能堆叠在一起,就像它们被打印在单独的页面上一样。在 NumPy 中,这个概念推广到任意数量的维度,因此基本数组类被称为 ndarray:它表示一个“N 维数组”。

大多数 NumPy 数组都有一些限制。例如

  • 数组的所有元素必须是同一种数据类型。

  • 一旦创建,数组的总大小不能更改。

  • 形状必须是“矩形”,而不是“锯齿形”;例如,二维数组的每一行必须具有相同数量的列。

当满足这些条件时,NumPy 会利用这些特征使数组比限制较小的数据结构更快、更省内存、更方便使用。

在本文件的其余部分中,我们将使用单词“数组”来表示 ndarray 的实例。

数组基础知识#

初始化数组的一种方式是使用 Python 序列,如列表。例如

>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a
array([1, 2, 3, 4, 5, 6])

可以通过 多种方式 访问数组元素。例如,我们可以像访问原始列表中的元素一样访问此数组中的单个元素:使用方括号中的元素的整数索引。

>>> a[0]
1

注意

与 Python 内置序列一样,NumPy 数组采用“零索引法”:使用索引 0(而非 1)可访问数组的第一个元素。

与原始列表类似,该数组是可变的。

>>> a[0] = 10
>>> a
array([10,  2,  3,  4,  5,  6])

同样类似原始列表,Python 切片符号可用于索引。

>>> a[:3]
array([10, 2, 3])

一个主要区别在于,列表的切片索引会将元素复制到一个新列表中,而对数组进行切片会返回一个视图:一个引用原始数组中数据的对象。可以使用视图对原始数组进行修改。

>>> b = a[3:]
>>> b
array([4, 5, 6])
>>> b[0] = 40
>>> a
array([ 10,  2,  3, 40,  5,  6])

请参阅 副本和视图,全面了解何时数组操作返回视图而不是副本。

可以利用嵌套的 Python 序列初始化二维以上数组

>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

在 NumPy 中,数组的一个维度有时称为“轴”。此术语可帮助消除数组维数与数组表示的数据维数之间的歧义。例如,数组 a 可以表示三个点,每个点都在一个四维空间中,但 a 只有两个“轴”。

数组与列表列表之间的另一个区别在于,可以在单个方括号内通过指定沿每个轴的索引来访问数组元素,这些方括号内用逗号分隔。例如,元素 8 在第 1 行和第 3

>>> a[1, 3]
8

注意

在数学中,按行索引先于列索引的顺序引用矩阵元素是很常见的做法。这对于二维数组而言是正确的,但更好的思维模型是将列索引放在最后,行索引放在倒数第二。这可概括为具有任意数量维度的数组。

注意

你可能会听说 0-D(零维)数组被称为“标量”,1-D(一维)数组被称为“向量”,2-D(二维)数组被称为“矩阵”,或者 N-D(N 维,其中“N”通常是大于 2 的整数)数组被称为“张量”。为清晰起见,最好不要在引用数组时使用数学术语,因为带这些名称的数学对象的行为与数组不同(例如,“矩阵”乘法与“数组”乘法有本质区别),并且科学 Python 生态系统中还有其他具有这些名称的对象(例如,PyTorch 的基本数据结构是“张量”)。

数组属性#

本节涵盖 ndimshapesize dtype数组 的属性


数组的维度数包含在 ndim 属性中。

>>> a.ndim
2

数组的形状是一个非负整数元组,用于指定沿各个维度中的元素数。

>>> a.shape
(3, 4)
>>> len(a.shape) == a.ndim
True

数组中元素的固定总数包含在 size 属性中。

>>> a.size
12
>>> import math
>>> a.size == math.prod(a.shape)
True

数组通常是“同质”的,这意味着它们只包含一种“数据类型”的元素。数据类型记录在 dtype 属性中。

>>> a.dtype
dtype('int64')  # "int" for integer, "64" for 64-bit

在这里阅读有关 array 属性的更多内容,并了解有关 array 对象的内容

如何创建一个基本数组#

本节涵盖 np.zeros()np.ones()np.empty()np.arange()np.linspace()


除了基于元素序列创建一个数组,你还可以轻松创建一个填充有 0 的数组

>>> np.zeros(2)
array([0., 0.])

或者一个填充有 1 的数组

>>> np.ones(2)
array([1., 1.])

甚至创建一个空数组!函数 empty 创建一个内容随机且取决于内存状态的数组。使用 empty 而非 zeros(或类似内容)的原因是速度——只需确保随后填充每个元素即可!

>>> # Create an empty array with 2 elements
>>> np.empty(2) 
array([3.14, 42.  ])  # may vary

你可以创建一个带有元素范围的数组

>>> np.arange(4)
array([0, 1, 2, 3])

甚至创建一个包含一系列均匀间隔的数组。为此,你需要指定第一个数字最后一个数字步长

>>> np.arange(2, 9, 2)
array([2, 4, 6, 8])

你还可以使用 np.linspace() 在指定区间内创建元素在该区间内线性变化的数组

>>> np.linspace(0, 10, num=5)
array([ 0. ,  2.5,  5. ,  7.5, 10. ])

指定数据类型

虽然默认数据类型是浮点数 (np.float64),你也可以使用 dtype 关键字明确指定想要的数据类型。

>>> x = np.ones(2, dtype=np.int64)
>>> x
array([1, 1])

点击此处,详细了解如何创建数组

添加、删除和排列元素#

本节涵盖了 np.sort()np.concatenate()


借助 np.sort(),对数组进行排序很简单。在调用该函数时,可以指定轴、类型和顺序。

如果你从这个数组开始

>>> arr = np.array([2, 1, 5, 3, 7, 4, 6, 8])

则可以使用以下方法快速升序排列数字

>>> np.sort(arr)
array([1, 2, 3, 4, 5, 6, 7, 8])

除了可以返回排序后的数组副本的 sort 之外,还可以使用

  • argsort,这是一个沿指定轴进行间接排序的方法,

  • lexsort,它是一个基于多个键进行间接稳定排序的方法,

  • searchsorted,它会查找排好序的数组中的元素,以及

  • partition,这是部分排序。

要详细了解如何对数组进行排序,请参阅:sort

如果你从这些数组开始

>>> a = np.array([1, 2, 3, 4])
>>> b = np.array([5, 6, 7, 8])

则可以使用 np.concatenate() 将它们连接起来。

>>> np.concatenate((a, b))
array([1, 2, 3, 4, 5, 6, 7, 8])

或者,如果你从这些数组开始

>>> x = np.array([[1, 2], [3, 4]])
>>> y = np.array([[5, 6]])

则可以使用以下方法将它们连接起来

>>> np.concatenate((x, y), axis=0)
array([[1, 2],
       [3, 4],
       [5, 6]])

要从数组中删除元素,一种简单的方法是使用索引选择要保留的元素。

要详细了解连接,请参阅:concatenate

如何得知数组的形状和大小?#

本节涵盖了 ndarray.ndimndarray.sizendarray.shape


ndarray.ndim 将告诉你数组的轴数或维数。

ndarray.size 将告诉你数组的总元素数。这是数组形状元素的乘积。

ndarray.shape 将显示一个整数元组,该元组表示存储在数组的各个维度的元素数量。例如,如果你有一个包含 2 行和 3 列的 2 维数组,则数组的形状为 (2, 3)

例如,如果你创建此数组

>>> array_example = np.array([[[0, 1, 2, 3],
...                            [4, 5, 6, 7]],
...
...                           [[0, 1, 2, 3],
...                            [4, 5, 6, 7]],
...
...                           [[0 ,1 ,2, 3],
...                            [4, 5, 6, 7]]])

要查找数组的维度数,请运行

>>> array_example.ndim
3

要查找数组中的元素总数,请运行

>>> array_example.size
24

要查找数组的形状,请运行

>>> array_example.shape
(3, 2, 4)

你能重新整理一个数组吗?#

本节涵盖 arr.reshape()


对!

使用 arr.reshape() 会为数组提供新形状,而不会更改数据。请记住在使用 reshape 方法时,想要生成的数组需要拥有与原始数组相同数量的元素。如果你从包含 12 个元素的数组开始,你需要确保新数组的元素总数也为 12。

如果你从这个数组开始

>>> a = np.arange(6)
>>> print(a)
[0 1 2 3 4 5]

你可以使用 reshape() 来重新整理数组。例如,你可以将此数组重新整理为一个包含三行两列的数组

>>> b = a.reshape(3, 2)
>>> print(b)
[[0 1]
 [2 3]
 [4 5]]

使用 np.reshape 可以指定一些可选参数

>>> np.reshape(a, shape=(1, 6), order='C')
array([[0, 1, 2, 3, 4, 5]])

a 是要重新整理的数组。

newshape 是想要的新形状。你可以指定一个整数或一个整数元组。如果你指定一个整数,则结果将是具有该长度的数组。该形状应该与原始形状兼容。

order: C 意味着使用类似 C 语言的索引顺序读写元素,F 意味着使用类似 Fortran 语言的索引顺序读写元素,A 意味着在内存中连续地使用类似 Fortran 语言的索引顺序读写元素,否则使用类似 C 语言的索引顺序。(这是一个可选参数,不需要指定。)

如果您想进一步了解 C 语言和 Fortran 顺序,您可以在此处阅读有关 NumPy 数组内部组织的更多内容。从本质上来说,C 语言和 Fortran 顺序与索引如何对应数组在内存中存储的顺序有关。在 Fortran 语言中,在内存中存储二维数组的元素时,第一个索引变化最快。随着第一个索引变化移动到下一行,矩阵一次存储一列。因此,Fortran 语言被认为是列优先语言。另一方面,在 C 语言中,最后一个索引变化最快。矩阵按行存储,使其成为行优先语言。您对 C 语言或 Fortran 语言的操作取决于保留索引约定或不重新排序数据是否更重要。

在此处了解有关形状操作的更多内容.

如何将一维数组转换为二维数组(如何向数组添加新轴)#

本节包括 np.newaxisnp.expand_dims


您可以使用 np.newaxisnp.expand_dims 来增加现有数组的维度。

使用 np.newaxis 一次将增加数组一个维度。这意味着一维数组将变为二维数组,二维数组将变为三维数组,依此类推。

例如,如果您从该数组开始

>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a.shape
(6,)

您可以使用 np.newaxis 添加一个新轴

>>> a2 = a[np.newaxis, :]
>>> a2.shape
(1, 6)

您可以使用 np.newaxis 将一维数组明确转换为行向量或列向量。例如,您可以通过沿第一个维度插入轴将一维数组转换为行向量

>>> row_vector = a[np.newaxis, :]
>>> row_vector.shape
(1, 6)

或者,对于列向量,您可以沿第二个维度插入一个轴

>>> col_vector = a[:, np.newaxis]
>>> col_vector.shape
(6, 1)

您还可以使用 np.expand_dims 在指定位置插入新轴来扩展数组。

例如,如果您从该数组开始

>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a.shape
(6,)

您可以使用 np.expand_dims 在索引位置 1 添加一个轴,如下所示

>>> b = np.expand_dims(a, axis=1)
>>> b.shape
(6, 1)

您可以通过以下方式在索引位置 0 添加一个轴

>>> c = np.expand_dims(a, axis=0)
>>> c.shape
(1, 6)

有关 newaxisexpand_dims 的更多信息,请访问 expand_dims

索引和切片#

你可以按在 Python 列表中切片的方式对 NumPy 数组进行索引和切片。

>>> data = np.array([1, 2, 3])

>>> data[1]
2
>>> data[0:2]
array([1, 2])
>>> data[1:]
array([2, 3])
>>> data[-2:]
array([2, 3])

你可以这样可视化

../_images/np_indexing.png

你可能希望获取数组的一部分或特定数组元素以用于进一步分析或其他操作。为此,你需要对数组进行子集操作、切片和/或索引。

如果要从数组中选择满足特定条件的值,则使用 NumPy 非常简单。

例如,如果您从该数组开始

>>> a = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

你可以轻松打印出数组中小于 5 的所有值。

>>> print(a[a < 5])
[1 2 3 4]

你也可以选择例如大于或等于 5 的数字,并使用该条件对数组进行索引。

>>> five_up = (a >= 5)
>>> print(a[five_up])
[ 5  6  7  8  9 10 11 12]

你可以选择可被 2 整除的元素

>>> divisible_by_2 = a[a%2==0]
>>> print(divisible_by_2)
[ 2  4  6  8 10 12]

或者你可以使用 &| 运算符来选择满足两个条件的元素

>>> c = a[(a > 2) & (a < 11)]
>>> print(c)
[ 3  4  5  6  7  8  9 10]

你还可以利用逻辑运算符 &| 来返回布尔值,指定数组中的值是否满足特定条件。这对于包含名称或其他分类值在内的数组非常有用。

>>> five_up = (a > 5) | (a == 5)
>>> print(five_up)
[[False False False False]
 [ True  True  True  True]
 [ True  True  True True]]

你还可以使用 np.nonzero() 从数组中选择元素或索引。

从这个数组开始

>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

你可以使用 np.nonzero() 来打印例如小于 5 的元素的索引

>>> b = np.nonzero(a < 5)
>>> print(b)
(array([0, 0, 0, 0]), array([0, 1, 2, 3]))

在此示例中,返回了一个元组数组:每个维度一个数组。第一个数组表示找到这些值的行列索引,第二个数组表示找到值的列索引。

如果要生成元素存在的坐标列表,则可以对这些数组进行压缩,遍历坐标列表,然后打印。例如

>>> list_of_coordinates= list(zip(b[0], b[1]))

>>> for coord in list_of_coordinates:
...     print(coord)
(np.int64(0), np.int64(0))
(np.int64(0), np.int64(1))
(np.int64(0), np.int64(2))
(np.int64(0), np.int64(3))

你还可以使用 np.nonzero() 使用下列命令打印数组中小于 5 的元素

>>> print(a[b])
[1 2 3 4]

如果在数组中不存在你正在寻找的元素,则返回的索引数组将为空。例如

>>> not_there = np.nonzero(a == 42)
>>> print(not_there)
(array([], dtype=int64), array([], dtype=int64))

详细了解 此处 的索引和切片,以及 此处

在以下位置阅读有关使用 nonzero 函数的更多内容: nonzero

如何从现有数据创建数组#

本节涵盖 切片 索引np.vstack()np.hstack()np.hsplit().view()copy()


您可以轻松地根据现有数组的一部分创建新的数组。

假设您有这个数组

>>> a = np.array([1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

您可以随时通过指定在数组何处切片来根据数组的部分创建新数组。

>>> arr1 = a[3:8]
>>> arr1
array([4, 5, 6, 7, 8])

此处,您从索引位置 3 到索引位置 8 获得了数组的一部分。

您还可以垂直或水平堆叠两个现有数组。假设您有两个数组,a1a2

>>> a1 = np.array([[1, 1],
...                [2, 2]])

>>> a2 = np.array([[3, 3],
...                [4, 4]])

您可以使用 vstack 垂直堆叠它们

>>> np.vstack((a1, a2))
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

或使用 hstack 水平堆叠它们

>>> np.hstack((a1, a2))
array([[1, 1, 3, 3],
       [2, 2, 4, 4]])

您可以使用 hsplit 将数组拆分为几个较小的数组。您可以指定要返回的形状相同的数组数,也可以指定分隔要发生在之后的列。

假设您有这个数组

>>> x = np.arange(1, 25).reshape(2, 12)
>>> x
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]])

如果您想将此数组拆分为三个形状相同的数组,将运行

>>> np.hsplit(x, 3)
  [array([[ 1,  2,  3,  4],
         [13, 14, 15, 16]]), array([[ 5,  6,  7,  8],
         [17, 18, 19, 20]]), array([[ 9, 10, 11, 12],
         [21, 22, 23, 24]])]

如果您想在第三列和第四列之后拆分您的数组,将运行

>>> np.hsplit(x, (3, 4))
  [array([[ 1,  2,  3],
         [13, 14, 15]]), array([[ 4],
         [16]]), array([[ 5,  6,  7,  8,  9, 10, 11, 12],
         [17, 18, 19, 20, 21, 22, 23, 24]])]

在此处了解有关堆叠和拆分数组的更多信息.

您可以使用 view 方法创建一个新的数组对象,它查看与原始数组相同的数据(浅表副本)。

视图是 NumPy 的一个重要概念!只要有可能,NumPy 函数以及诸如索引和切片之类的运算都将返回视图。这可以节省内存并加快速度(不必复制数据)。然而需要注意这一点 - 修改视图中的数据也会修改原始数组!

假设您创建了此数组

>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

现在,我们通过对 a 进行切片并修改 b1 的第一个元素,从而创建一个数组 b1。这也会修改 a 中相应的元素!

>>> b1 = a[0, :]
>>> b1
array([1, 2, 3, 4])
>>> b1[0] = 99
>>> b1
array([99,  2,  3,  4])
>>> a
array([[99,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

使用 copy 方法会对数组及其数据进行完全复制(即“深度复制”)。要在你的数组上使用该方法,你可以运行

>>> b2 = a.copy()

在此处了解有关复制和视图的更多信息.

基本数组运算#

本部分涵盖加法、减法、乘法、除法等


创建好数组后,你可以开始使用它们。例如,假设你创建了两个数组,一个名为“data”,另一个名为“ones”

../_images/np_array_dataones.png

你可以使用加号将这些数组加在一起。

>>> data = np.array([1, 2])
>>> ones = np.ones(2, dtype=int)
>>> data + ones
array([2, 3])
../_images/np_data_plus_ones.png

当然,你还可以做更多的事情,而不仅仅是加法!

>>> data - ones
array([0, 1])
>>> data * data
array([1, 4])
>>> data / data
array([1., 1.])
../_images/np_sub_mult_divide.png

在 NumPy 中,基本运算非常简单。如果你想找到数组中元素的总和,可以使用 sum()。这适用于一维数组、二维数组和维度更高的数组。

>>> a = np.array([1, 2, 3, 4])

>>> a.sum()
10

要添加二维数组中的行或列,你需要指定轴。

如果你从这个数组开始

>>> b = np.array([[1, 1], [2, 2]])

你可以通过以下方式对行的轴进行求和:

>>> b.sum(axis=0)
array([3, 3])

你可以通过以下方式对列的轴进行求和:

>>> b.sum(axis=1)
array([2, 4])

在此处了解有关基本运算的更多信息.

广播#

有时,你可能希望在数组和单个数字(也称为“向量与标量的运算”)或两个不同大小的数组之间执行运算。例如,你的数组(我们称之为“data”)可能包含英里制下的距离信息,但你希望将这些信息转换为公里制。你可以使用以下方式执行此运算

>>> data = np.array([1.0, 2.0])
>>> data * 1.6
array([1.6, 3.2])
../_images/np_multiply_broadcasting.png

NumPy 明白此乘法应针对每个单元格执行。该概念称为**广播**。广播是一种机制,它允许 NumPy 对不同形状的数组执行运算。你的数组的维度必须兼容,例如,当两个数组的维度相同时或者其中一个数组的维度为 1 时。如果维度不兼容,则会收到 ValueError

在此处了解有关广播的更多信息.

更有用的数组运算#

本部分涵盖最大值、最小值、总和、均值、乘积、标准差等


NumPy 还可以执行聚合函数。除了minmaxsum以外,你还可以轻松运行mean以获取平均值、prod以获取将元素相乘的结果、std以获取标准差等。

>>> data.max()
2.0
>>> data.min()
1.0
>>> data.sum()
3.0
../_images/np_aggregation.png

我们从称为“a”的这个数组开始

>>> a = np.array([[0.45053314, 0.17296777, 0.34376245, 0.5510652],
...               [0.54627315, 0.05093587, 0.40067661, 0.55645993],
...               [0.12697628, 0.82485143, 0.26590556, 0.56917101]])

通常会想要沿着行或列进行聚合。默认情况下,每个 NumPy 聚合函数都将返回整个数组的聚合。要查找数组中元素的总和或最小值,请运行

>>> a.sum()
4.8595784

>>> a.min()
0.05093587

可以指定在哪个轴上计算聚合函数。例如,你可以通过指定axis=0来查找每一列中的最小值。

>>> a.min(axis=0)
array([0.12697628, 0.05093587, 0.26590556, 0.5510652 ])

上面列出的四个值对应于数组中的列数。对于四列数组,你将获得四个值作为结果。

在此处阅读有关数组方法的更多信息

创建矩阵#

你可以传递 Python 列表的列表,以创建一个二维数组(或“矩阵”)来在 NumPy 中表示它们。

>>> data = np.array([[1, 2], [3, 4], [5, 6]])
>>> data
array([[1, 2],
       [3, 4],
       [5, 6]])
../_images/np_create_matrix.png

在处理矩阵时,索引和切片操作非常有用

>>> data[0, 1]
2
>>> data[1:3]
array([[3, 4],
       [5, 6]])
>>> data[0:2, 0]
array([1, 3])
../_images/np_matrix_indexing.png

你可以像聚合向量一样聚合矩阵

>>> data.max()
6
>>> data.min()
1
>>> data.sum()
21
../_images/np_matrix_aggregation.png

你可以聚合矩阵中的所有值,并且可以使用axis参数对它们跨列或跨行进行聚合。为了说明这一点,我们来看一下经过些许修改的数据集

>>> data = np.array([[1, 2], [5, 3], [4, 6]])
>>> data
array([[1, 2],
       [5, 3],
       [4, 6]])
>>> data.max(axis=0)
array([5, 6])
>>> data.max(axis=1)
array([2, 5, 6])
../_images/np_matrix_aggregation_row.png

创建矩阵后,如果拥有两个大小相同的矩阵,则可以使用算术运算符为其增加和相乘。

>>> data = np.array([[1, 2], [3, 4]])
>>> ones = np.array([[1, 1], [1, 1]])
>>> data + ones
array([[2, 3],
       [4, 5]])
../_images/np_matrix_arithmetic.png

可以在不同大小的矩阵上执行这些算术运算,但前提是其中一个矩阵只有一列或一行。在这种情况下,NumPy 将对其运算使用广播规则。

>>> data = np.array([[1, 2], [3, 4], [5, 6]])
>>> ones_row = np.array([[1, 1]])
>>> data + ones_row
array([[2, 3],
       [4, 5],
       [6, 7]])
../_images/np_matrix_broadcasting.png

请注意,当 NumPy 打印 N 维数组时,将最快地遍历最后一个轴,而第一个轴最慢。例如

>>> np.ones((4, 3, 2))
array([[[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]]])

在很多时候,我们要让 NumPy 初始化数组的值。NumPy 提供 ones()zeros()等函数,以及random.Generator类来进行随机数生成。你要做的就是传入它要生成的元素数量

>>> np.ones(3)
array([1., 1., 1.])
>>> np.zeros(3)
array([0., 0., 0.])
>>> rng = np.random.default_rng()  # the simplest way to generate random numbers
>>> rng.random(3) 
array([0.63696169, 0.26978671, 0.04097352])
../_images/np_ones_zeros_random.png

如果您提供描述矩阵维数的元组,还可以使用 ones()zeros()random() 来创建 2D 数组

>>> np.ones((3, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((3, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.]])
>>> rng.random((3, 2)) 
array([[0.01652764, 0.81327024],
       [0.91275558, 0.60663578],
       [0.72949656, 0.54362499]])  # may vary
../_images/np_ones_zeros_matrix.png

数组创建例程了解更多关于创建数组的信息,数组可通过 01、其他值或未初始化来填充。

生成随机数#

随机数生成的使用是许多数值和机器学习算法的配置和评估的重要组成部分。无论您是否需要在人工神经网络中随机初始化权重、将数据拆分为随机集合还是随机打乱数据集,能够生成随机数(实际上是可重复伪随机数)都是必不可少的。

使用 Generator.integers,您可以生成从低随机整数(记住,这是 NumPy 包含的)到高随机整数(带排他性)。您可以把 endpoint=True 设置为使高数字包含在内。

您可以利用 0 到 4 之间的随机整数生成一个 2 x 4 数组

>>> rng.integers(5, size=(2, 4)) 
array([[2, 1, 1, 0],
       [0, 0, 0, 4]])  # may vary

了解更多关于随机数字生成的信息.

如何获得唯一项和次数#

本节涵盖 np.unique()


您可以使用 np.unique 轻松地找到数组中的唯一元素。

例如,如果您从该数组开始

>>> a = np.array([11, 11, 12, 13, 14, 15, 16, 17, 12, 13, 11, 14, 18, 19, 20])

您可以使用 np.unique 来打印数组中的唯一值

>>> unique_values = np.unique(a)
>>> print(unique_values)
[11 12 13 14 15 16 17 18 19 20]

若要获取 NumPy 数组中唯一值的索引(数组中唯一值的第一个索引位置的数组),只需在 np.unique() 中传递 return_index 参数以及您的数组。

>>> unique_values, indices_list = np.unique(a, return_index=True)
>>> print(indices_list)
[ 0  2  3  4  5  6  7 12 13 14]

您可以在 np.unique() 中传递 return_counts 参数以及您的数组,以获取 NumPy 数组中唯一值的频率计数。

>>> unique_values, occurrence_count = np.unique(a, return_counts=True)
>>> print(occurrence_count)
[3 2 2 2 1 1 1 1 1 1]

这同样适用于 2D 数组!如果您从这个数组开始

>>> a_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [1, 2, 3, 4]])

您可以使用以下方式找到唯一值

>>> unique_values = np.unique(a_2d)
>>> print(unique_values)
[ 1  2  3  4  5  6  7  8  9 10 11 12]

如果没有传递 axis 参数,则会将您的 2D 数组展开。

如果您要获取唯一行或列,请务必传递 axis 参数。若要查找唯一行,请指定 axis=0,对于列,请指定 axis=1

>>> unique_rows = np.unique(a_2d, axis=0)
>>> print(unique_rows)
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]

若要获得唯一行、索引位置和出现次数,您可以使用

>>> unique_rows, indices, occurrence_count = np.unique(
...      a_2d, axis=0, return_counts=True, return_index=True)
>>> print(unique_rows)
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
>>> print(indices)
[0 1 2]
>>> print(occurrence_count)
[2 1 1]

要详细了解如何在数组中寻找唯一元素,请参阅 unique

转置和变换矩阵#

本节涵盖 arr.reshape()arr.transpose()arr.T


一般需要转置矩阵。NumPy 数组具备允许转置矩阵的 T 属性。

../_images/np_transposing_reshaping.png

你可能还需要切换矩阵的维度。比如说,当你的模型期待的一个形状与你的数据集不同时,就需要切换维度。这时候,reshape 方法就有用了。你只需要传入所需的新矩阵维度即可。

>>> data.reshape(2, 3)
array([[1, 2, 3],
       [4, 5, 6]])
>>> data.reshape(3, 2)
array([[1, 2],
       [3, 4],
       [5, 6]])
../_images/np_reshape.png

还可以使用 .transpose() 根据指定的值倒序或更改数组的轴。

如果你从这个数组开始

>>> arr = np.arange(6).reshape((2, 3))
>>> arr
array([[0, 1, 2],
       [3, 4, 5]])

可以使用 arr.transpose() 转置数组。

>>> arr.transpose()
array([[0, 3],
       [1, 4],
       [2, 5]])

还可以使用 arr.T

>>> arr.T
array([[0, 3],
       [1, 4],
       [2, 5]])

要详细了解如何转置和变换数组,请参阅 transposereshape

如何倒转数组#

本节涵盖 np.flip()


NumPy 的 np.flip() 函数允许按轴翻转或倒转数组的内容。使用 np.flip() 时,需指定要翻转的数组和轴。如果你不指定轴,NumPy 会按输入数组的所有轴翻转内容。

倒转一维数组

如果从以下的一维数组开始

>>> arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])

可以使用此代码倒转数组

>>> reversed_arr = np.flip(arr)

如果你要打印已倒转的数组,可运行

>>> print('Reversed Array: ', reversed_arr)
Reversed Array:  [8 7 6 5 4 3 2 1]

倒转二维数组

二维数组的处理方式很类似。

如果你从这个数组开始

>>> arr_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

可以使用以下代码倒转所有行和所有列中的内容

>>> reversed_arr = np.flip(arr_2d)
>>> print(reversed_arr)
[[12 11 10  9]
 [ 8  7  6  5]
 [ 4  3  2  1]]

可以使用以下代码轻松仅倒转

>>> reversed_arr_rows = np.flip(arr_2d, axis=0)
>>> print(reversed_arr_rows)
[[ 9 10 11 12]
 [ 5  6  7  8]
 [ 1  2  3  4]]

或者仅倒转

>>> reversed_arr_columns = np.flip(arr_2d, axis=1)
>>> print(reversed_arr_columns)
[[ 4  3  2  1]
 [ 8  7  6  5]
 [12 11 10  9]]

您还可以仅反转一列或一行的内容。例如,可以反转索引位置 1 处的行的内容(第二行)

>>> arr_2d[1] = np.flip(arr_2d[1])
>>> print(arr_2d)
[[ 1  2  3  4]
 [ 8  7  6  5]
 [ 9 10 11 12]]

您还可以反转索引位置 1 处的列(第二列)

>>> arr_2d[:,1] = np.flip(arr_2d[:,1])
>>> print(arr_2d)
[[ 1 10  3  4]
 [ 8  7  6  5]
 [ 9  2 11 12]]

flip 了解更多关于反转数组的内容。

重塑和展平多维数组#

此部分涵盖 .flatten(), ravel()


有两种流行的展平数组的方法:.flatten().ravel()。两者的主要区别在于,使用 ravel() 创建的新数组实际上是对父数组的引用(即,“视图”)。这意味着对新数组的任何更改都会同样影响父数组。由于 ravel 不会创建副本,因此它具有很高的内存效率。

如果你从这个数组开始

>>> x = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

您可以使用 flatten 将数组展平为一维数组。

>>> x.flatten()
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

当您使用 flatten 时,对新数组的更改不会更改父数组。

例如

>>> a1 = x.flatten()
>>> a1[0] = 99
>>> print(x)  # Original array
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
>>> print(a1)  # New array
[99  2  3  4  5  6  7  8  9 10 11 12]

但是,当您使用 ravel 时,对新数组所做的更改会影响父数组。

例如

>>> a2 = x.ravel()
>>> a2[0] = 98
>>> print(x)  # Original array
[[98  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
>>> print(a2)  # New array
[98  2  3  4  5  6  7  8  9 10 11 12]

ndarray.flatten 了解更多关于 flatten 的信息,在 ravel 了解更多关于 ravel 的信息。

如何访问文档字符串了解更多信息#

此部分涵盖 help(), ?, ??


对于数据科学生态系统,Python 和 NumPy 的构建以用户为中心。其中一个最佳示例便是内置对文档的访问权限。每个对象均包含对字符串的引用,称为文档字符串。在大多数情况下,此文档字符串包含对该对象及其使用方法的简洁快速摘要。Python 有一个内置的 help() 函数,它可以帮助你访问此信息。这意味着几乎任何时候当你需要更多信息时,你都可以使用 help() 来快速找到所需的信息。

例如

>>> help(max)
Help on built-in function max in module builtins:

max(...)
    max(iterable, *[, default=obj, key=func]) -> value
    max(arg1, arg2, *args, *[, key=func]) -> value

    With a single iterable argument, return its biggest item. The
    default keyword-only argument specifies an object to return if
    the provided iterable is empty.
    With two or more arguments, return the largest argument.

因为获取更多信息非常有用,所以 IPython 将 ? 字符用作此类文档和其他相关信息的访问快捷方式。IPython 是一个命令界面,用于进行多语言交互式计算。 你可以在此处找到 IPython 的更多信息

例如

In [0]: max?
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value

With a single iterable argument, return its biggest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the largest argument.
Type:      builtin_function_or_method

你甚至可以将这种方式应用于对象的方法和对象本身。

假设您创建了此数组

>>> a = np.array([1, 2, 3, 4, 5, 6])

然后你可以获取大量有用的信息(首先是 a 本身的详细信息,其次是 a 为其实例的 ndarray 的文档字符串)

In [1]: a?
Type:            ndarray
String form:     [1 2 3 4 5 6]
Length:          6
File:            ~/anaconda3/lib/python3.9/site-packages/numpy/__init__.py
Docstring:       <no docstring>
Class docstring:
ndarray(shape, dtype=float, buffer=None, offset=0,
        strides=None, order=None)

An array object represents a multidimensional, homogeneous array
of fixed-size items.  An associated data-type object describes the
format of each element in the array (its byte-order, how many bytes it
occupies in memory, whether it is an integer, a floating point number,
or something else, etc.)

Arrays should be constructed using `array`, `zeros` or `empty` (refer
to the See Also section below).  The parameters given here refer to
a low-level method (`ndarray(...)`) for instantiating an array.

For more information, refer to the `numpy` module and examine the
methods and attributes of an array.

Parameters
----------
(for the __new__ method; see Notes below)

shape : tuple of ints
        Shape of created array.
...

其在对你创建的函数和其他对象也适用。只需记住使用字符串字面量(""" """''' ''')在你的函数中添加一个文档字符串。

举例而言,如果你创建此函数

>>> def double(a):
...   '''Return a * 2'''
...   return a * 2

你可以获取有关此函数的信息

In [2]: double?
Signature: double(a)
Docstring: Return a * 2
File:      ~/Desktop/<ipython-input-23-b5adf20be596>
Type:      function

通过阅读你所关注对象的源代码,你可以获得更深入的信息。使用双问号 (??) 可让你访问源代码。

例如

In [3]: double??
Signature: double(a)
Source:
def double(a):
    '''Return a * 2'''
    return a * 2
File:      ~/Desktop/<ipython-input-23-b5adf20be596>
Type:      function

如果所涉对象是用 Python 以外的语言编译的,使用 ?? 将返回与 ? 相同的信息。你将在许多内置对象和类型中发现这一点,例如

In [4]: len?
Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type:      builtin_function_or_method

In [5]: len??
Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type:      builtin_function_or_method

具有相同的输出,因为它们是用 Python 以外的编程语言编译的。

使用数学公式#

轻松地在数组上实现数学公式是 NumPy 在科学 Python 社区得到广泛应用的因素之一。

例如,以下是均方误差公式(监督机器学习模型中处理回归的核心公式)

../_images/np_MSE_formula.png

在 NumPy 中实现此公式很简单、直接

../_images/np_MSE_implementation.png

此功能如此强大的原因在于,predictionslabels 可以包含一个或一千个值。它们只需要大小相同即可。

你可以这样可视化

../_images/np_mse_viz1.png

在本例中,prediction 和 label 向量都包含三个值,这意味着 n 的值为三。在执行减法操作后,向量中的值会平方。然后,NumPy 对这些值求和,而您的结果就是该预测的误差值和模型质量得分。

../_images/np_mse_viz2.png ../_images/np_MSE_explanation2.png

如何保存和加载 NumPy 对象#

本部分涵盖 np.savenp.saveznp.savetxtnp.loadnp.loadtxt


您在某个时候会希望将数组保存在磁盘上,并在不需要重新运行代码的情况下将其加载回来。幸运的是,可以通过多种方式使用 NumPy 来保存和加载对象。可以使用 loadtxtsavetxt 函数将 ndarray 对象保存到磁盘文件中并从磁盘文件中加载,这些函数处理普通文本文件;使用 loadsave 函数处理带有 .npy 文件扩展名的 NumPy 二进制文件;使用 savez 函数处理带有 .npz 文件扩展名的 NumPy 文件。

.npy.npz 文件存储数据、形状、dtype 和重建 ndarray 所需的其他信息,以便即使在具有不同架构的另一台机器上,也可以正确地检索该数组。

如果您想存储单个 ndarray 对象,请使用 np.save 将其存储为 .npy 文件。如果您想在单个文件中存储多个 ndarray 对象,请使用 np.savez 将其存储为 .npz 文件。您还可以使用 savez_compressed 以压缩的 npz 格式将多个数组保存到单个文件中。

使用 np.save() 保存和加载数组很容易。只要确保指定要保存的数组和文件名即可。例如,如果您创建了此数组

>>> a = np.array([1, 2, 3, 4, 5, 6])

您可以使用以下命令将其保存为“filename.npy”

>>> np.save('filename', a)

您可以使用 np.load() 重建您的数组。

>>> b = np.load('filename.npy')

如果你想检查你的数组,你可以运行

>>> print(b)
[1 2 3 4 5 6]

你可以将 NumPy 数组作为明文文件保存,例如,使用 np.savetxt 保存为 .csv.txt 文件。

例如,如果你创建此数组

>>> csv_arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])

你可以轻松地将其另存为 .csv 文件,采用以下方式,文件名指定为 “new_file.csv”

>>> np.savetxt('new_file.csv', csv_arr)

你可以快速轻松地加载保存的文本文件,所用方法是 loadtxt()

>>> np.loadtxt('new_file.csv')
array([1., 2., 3., 4., 5., 6., 7., 8.])

函数 savetxt()loadtxt() 接受其他可选参数,例如头信息、页脚和分隔符。尽管文本文件可能便于共享,但 .npy 和 .npz 文件更小,而且读取速度更快。如果你需要更复杂地处理你的文本文件(例如,如果你需要运用包含缺失值的各行),你应该使用 genfromtxt 函数。

使用 savetxt,你可以指定页眉、页脚、批注等等。

在此处了解有关 输入和输出例程 的更多信息。

导入和导出 CSV#

从包含现有信息的 CSV 中读入信息非常简单。执行此操作的最佳、最简单方法是使用 Pandas

>>> import pandas as pd

>>> # If all of your columns are the same type:
>>> x = pd.read_csv('music.csv', header=0).values
>>> print(x)
[['Billie Holiday' 'Jazz' 1300000 27000000]
 ['Jimmie Hendrix' 'Rock' 2700000 70000000]
 ['Miles Davis' 'Jazz' 1500000 48000000]
 ['SIA' 'Pop' 2000000 74000000]]

>>> # You can also simply select the columns you need:
>>> x = pd.read_csv('music.csv', usecols=['Artist', 'Plays']).values
>>> print(x)
[['Billie Holiday' 27000000]
 ['Jimmie Hendrix' 70000000]
 ['Miles Davis' 48000000]
 ['SIA' 74000000]]
../_images/np_pandas.png

同样,使用 Pandas 可以轻松导出你的数组。如果你刚接触 NumPy,你可能希望通过数组中的值创建一个 Pandas 数据框,然后使用 Pandas 将数据框写到 CSV 文件中。

如果你创建了该数组“a”

>>> a = np.array([[-2.58289208,  0.43014843, -1.24082018, 1.59572603],
...               [ 0.99027828, 1.17150989,  0.94125714, -0.14692469],
...               [ 0.76989341,  0.81299683, -0.95068423, 0.11769564],
...               [ 0.20484034,  0.34784527,  1.96979195, 0.51992837]])

你可以创建一个 Pandas 数据框

>>> df = pd.DataFrame(a)
>>> print(df)
          0         1         2         3
0 -2.582892  0.430148 -1.240820  1.595726
1  0.990278  1.171510  0.941257 -0.146925
2  0.769893  0.812997 -0.950684  0.117696
3  0.204840  0.347845  1.969792  0.519928

你可以轻松保存你的数据框,方法是

>>> df.to_csv('pd.csv')

并且用以下方法读取你的 CSV

>>> data = pd.read_csv('pd.csv')
../_images/np_readcsv.png

你也可以使用 NumPy 方法 savetxt 来保存你的数组。

>>> np.savetxt('np.csv', a, fmt='%.2f', delimiter=',', header='1,  2,  3,  4')

如果你正在使用命令行,你可以随时使用诸如以下命令来读取已保存的 CSV

$ cat np.csv
#  1,  2,  3,  4
-2.58,0.43,-1.24,1.60
0.99,1.17,0.94,-0.15
0.77,0.81,-0.95,0.12
0.20,0.35,1.97,0.52

或者,你可以随时使用文本编辑器来打开文件!

如果你有兴趣了解有关 Pandas 的更多信息,请查看 Pandas 官方文档。使用 Pandas 官方安装信息 来了解如何安装 Pandas。

使用 Matplotlib 绘制数组#

如果你需要为你的值生成图像,它使用 Matplotlib 来实现非常简单。

例如,你可能有一个类似这样的数组

>>> a = np.array([2, 1, 5, 7, 4, 6, 8, 14, 10, 9, 18, 20, 22])

如果你已经安装了 Matplotlib,你可以使用以下方法来导入它

>>> import matplotlib.pyplot as plt

# If you're using Jupyter Notebook, you may also want to run the following
# line of code to display your code in the notebook:

%matplotlib inline

你要做的所有事情就是运行你的值

>>> plt.plot(a)

# If you are running from a command line, you may need to do this:
# >>> plt.show()
../_images/matplotlib1.png

例如,你可以像这样绘制一个 1D 数组

>>> x = np.linspace(0, 5, 20)
>>> y = np.linspace(0, 10, 20)
>>> plt.plot(x, y, 'purple') # line
>>> plt.plot(x, y, 'o')      # dots
../_images/matplotlib2.png

使用 Matplotlib,你可以访问大量的可视化选项。

>>> fig = plt.figure()
>>> ax = fig.add_subplot(projection='3d')
>>> X = np.arange(-5, 5, 0.15)
>>> Y = np.arange(-5, 5, 0.15)
>>> X, Y = np.meshgrid(X, Y)
>>> R = np.sqrt(X**2 + Y**2)
>>> Z = np.sin(R)

>>> ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis')
../_images/matplotlib3.png

要阅读更多关于 Matplotlib 及其功能的信息,请参阅 官方文档。有关安装 Matplotlib 的说明,请参阅官方 安装部分


图片来源:Jay Alammar https://jalammar.github.io/