NumPy:绝对基础入门#
欢迎阅读 NumPy 绝对初学者指南!
NumPy(Numerical Python)是一个广泛应用于科学和工程领域的开源 Python 库。NumPy 库包含多维数组数据结构,例如同质的 N 维 ndarray,以及一个能够高效处理这些数据结构的大型函数库。在 什么是 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 提示符下输入,可能会导致错误。
要运行示例中的代码,您可以将其复制并粘贴到 Python 脚本或 REPL 中,或者使用文档中各处提供的浏览器端实验性交互示例。
为什么要使用 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 会利用这些特性,使数组比限制较少的数据结构更快、内存效率更高,且使用更方便。
在本文件的其余部分中,我们将使用“数组 (array)”一词来指代 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 开始索引的”:访问数组的第一个元素使用的是索引 0,而不是 1。
与原始列表一样,数组也是可变的。
>>> a[0] = 10
>>> a
array([10, 2, 3, 4, 5, 6])
同样与原始列表一样,Python 的切片表示法也可用于索引。
>>> a[:3]
array([10, 2, 3])
一个主要的区别是,对列表进行切片索引会将元素复制到一个新列表中,但对数组进行切片会返回一个视图 (view):一个指向原始数组中数据的对象。可以使用该视图来修改原始数组。
>>> 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 中,数组的一个维度有时被称为“轴 (axis)”。这一术语有助于区分数组的维度与数组所代表的数据的维度。例如,数组 a 可以表示四个维度空间中的三个点,但 a 只有两个“轴”。
数组与列表嵌套列表之间的另一个区别是,可以通过在单个方括号内指定每个轴的索引并用逗号分隔来访问数组元素。例如,元素 8 位于第 1 行和第 3 列:
>>> a[1, 3]
8
注意
数学中习惯先引用矩阵的行索引,再引用列索引。这在二维数组中恰好适用,但一个更好的思维模型是认为列索引在最后,行索引在倒数第二。这可以推广到具有任意维度数量的数组。
注意
您可能会听说 0-D(零维)数组被称为“标量 (scalar)”,1-D(一维)数组被称为“向量 (vector)”,2-D(二维)数组被称为“矩阵 (matrix)”,或 N-D(N 维,其中“N”通常是大于 2 的整数)数组被称为“张量 (tensor)”。为了清晰起见,在提到数组时最好避免使用数学术语,因为具有这些名称的数学对象的行为与数组不同(例如,“矩阵”乘法与“数组”乘法有本质区别),而且科学 Python 生态系统中还有其他具有这些名称的对象(例如,PyTorch 的基础数据结构是“张量”)。
数组属性#
本节涵盖数组的 ndim、shape、size 和 dtype 属性。
数组的维度数量包含在 ndim 属性中。
>>> a.ndim
2
数组的形状 (shape) 是一个由非负整数组成的元组,用于指定每个维度上的元素数量。
>>> 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
在此阅读更多关于数组属性的信息,并在此了解数组对象。
如何创建基础数组#
本节涵盖 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() 对数组进行排序非常简单。您可以在调用该函数时指定轴 (axis)、类型 (kind) 和顺序 (order)。
如果您从这个数组开始:
>>> 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) 的更多信息,请参见:concatenate。
如何知道数组的形状和大小?#
本节涵盖 ndarray.ndim, ndarray.size, ndarray.shape
ndarray.ndim 将告诉您数组的轴数(维度数)。
ndarray.size 将告诉您数组的元素总数。这是数组形状各元素之积。
ndarray.shape 将显示一个整数元组,指示数组每个维度存储的元素数。例如,如果您有一个 2 行 3 列的二维数组,则数组的形状为 (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 是待重塑的数组。
shape 是您想要的新形状。您可以指定一个整数或整数元组。如果您指定一个整数,结果将是该长度的一维数组。该形状应与原始形状兼容。
order: C 意味着使用类似 C 语言的索引顺序读写元素,F 意味着使用类似 Fortran 的索引顺序读写元素,A 意味着如果 a 在内存中是 Fortran 连续的,则使用类 Fortran 索引顺序读写,否则使用类 C 顺序。(这是一个可选参数,不需要指定。)
如果您想了解有关 C 顺序和 Fortran 顺序的更多信息,可以在此处阅读有关 NumPy 数组内部组织的更多信息。本质上,C 顺序和 Fortran 顺序与索引如何对应于数组在内存中的存储顺序有关。在 Fortran 中,当遍历存储在内存中的二维数组元素时,第一个索引是变化最快的索引。随着第一个索引在变化时移动到下一行,矩阵是按列存储的。这就是为什么 Fortran 被认为是列优先 (Column-major) 语言。另一方面,在 C 语言中,最后一个索引变化最快。矩阵是按行存储的,使其成为行优先 (Row-major) 语言。对于 C 或 Fortran 的选择取决于保留索引约定还是不重新排序数据更重要。
在此了解更多有关形状操作的信息.
如何将一维数组转换为二维数组(如何向数组添加新轴)#
本节涵盖 np.newaxis, np.expand_dims
您可以使用 np.newaxis 和 np.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)
在此处了解有关 newaxis 的更多信息,并在 expand_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])
您可以这样可视化它:
您可能希望获取数组的一个部分或特定的数组元素,以便在进一步的分析或其他操作中使用。为此,您需要对数组进行子集化、切片和/或索引。
如果您想从数组中选择满足特定条件的值,使用 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]))
在这个例子中,返回了一个元组:每个维度一个。第一个数组代表找到这些值的行索引,第二个数组代表找到这些值的列索引。
如果您想生成元素存在的坐标列表,可以对数组进行 zip 操作,迭代坐标列表并打印它们。例如:
>>> 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(但不包括位置 8 本身)的部分。
提醒:数组索引从 0 开始。这意味着数组的第一个元素在索引 0,第二个元素在索引 1,依此类推。
您还可以在垂直和水平方向上堆叠两个现有数组。假设您有两个数组 a1 和 a2:
>>> 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”:
您可以使用加号将数组相加。
>>> data = np.array([1, 2])
>>> ones = np.ones(2, dtype=np.int_)
>>> data + ones
array([2, 3])
当然,您能做的远不止加法!
>>> data - ones
array([0, 1])
>>> data * data
array([1, 4])
>>> data / data
array([1., 1.])
使用 NumPy,基础操作很简单。如果您想查找数组中元素的总和,可以使用 sum()。这适用于一维数组、二维数组以及更高维度的数组。
>>> a = np.array([1, 2, 3, 4])
>>> a.sum()
10
要在二维数组中对行或列求和,您需要指定轴 (axis)。
如果您从这个数组开始:
>>> 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])
NumPy 明白乘法应该在每个单元格中发生。这个概念叫做广播 (broadcasting)。广播是一种允许 NumPy 在不同形状的数组上执行操作的机制。数组的维度必须是兼容的,例如,当两个数组的维度相等或者其中一个为 1 时。如果维度不兼容,您将得到一个 ValueError。
在此了解有关广播的更多信息.
更多有用的数组操作#
本节涵盖最大值、最小值、总和、平均值、乘积、标准差等
NumPy 还可以执行聚合函数。除了 min、max 和 sum 之外,您还可以轻松运行 mean 来获取平均值,运行 prod 来获取元素相乘的结果,运行 std 来获取标准差等。
>>> data = np.array([1, 2, 3])
>>> data.max()
3
>>> data.min()
1
>>> data.sum()
6
让我们从这个名为 “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]])
索引和切片操作在操作矩阵时非常有用:
>>> data[0, 1]
2
>>> data[1:3]
array([[3, 4],
[5, 6]])
>>> data[0:2, 0]
array([1, 3])
您可以像聚合向量一样聚合矩阵:
>>> data.max()
6
>>> data.min()
1
>>> data.sum()
21
您可以聚合矩阵中的所有值,也可以使用 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])
创建矩阵后,如果您有两个大小相同的矩阵,可以使用算术运算符对它们进行加法和乘法运算。
>>> data = np.array([[1, 2], [3, 4]])
>>> ones = np.array([[1, 1], [1, 1]])
>>> data + ones
array([[2, 3],
[4, 5]])
您可以对不同大小的矩阵执行这些算术运算,但前提是其中一个矩阵只有一列或一行。在这种情况下,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]])
请注意,当 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])
如果您给它们一个描述矩阵维度的元组,您也可以使用 ones()、zeros() 和 random() 来创建二维数组:
>>> 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
有关创建填充了 0、1、其他值或未初始化的数组的更多信息,请参见 数组创建例程。
生成随机数#
随机数生成的使用是许多数值和机器学习算法配置与评估的重要组成部分。无论您是需要随机初始化人工神经网络中的权重,将数据拆分为随机集,还是随机打乱数据集,能够生成随机数(实际上是可重复的伪随机数)都是必不可少的。
使用 Generator.integers,您可以生成从低(记住这在 NumPy 中是包含的)到高(排除)的随机整数。您可以设置 endpoint=True 来包含高位数字。
您可以使用以下代码生成一个 2 x 4 的 0 到 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]
这也适用于二维数组!如果您从这个数组开始:
>>> 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 参数,您的二维数组将被展平。
如果您想获取唯一的行或列,请务必传递 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,允许您转置矩阵。
您可能还需要切换矩阵的维度。例如,当您的模型期望特定的输入形状,而该形状与您的数据集不同时,就会发生这种情况。这就是 reshape 方法派上用场的地方。您只需要传入矩阵所需的新维度即可。
>>> data.reshape(2, 3)
array([[1, 2, 3],
[4, 5, 6]])
>>> data.reshape(3, 2)
array([[1, 2],
[3, 4],
[5, 6]])
您还可以使用 .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]])
要了解有关转置和重塑数组的更多信息,请参见 transpose 和 reshape。
如何反转数组#
本节涵盖 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 的设计充分考虑了用户。最好的例子之一就是内置的文档访问功能。每个对象都包含一个对字符串的引用,该字符串被称为文档字符串 (docstring)。在大多数情况下,此文档字符串包含该对象及其使用方法的快速简明摘要。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 是一个用于多种语言交互式计算的命令行 shell。您可以在此处找到有关 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:
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/
Type: function
您可以通过阅读感兴趣对象的源代码来达到另一层信息深度。使用双问号 (??) 允许您访问源代码。
例如:
In [3]: double??
Signature: double(a)
Source:
def double(a):
'''Return a * 2'''
return a * 2
File: ~/Desktop/
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 社区中应用如此广泛的原因之一。
例如,这是均方误差公式(处理回归的监督学习模型中使用的核心公式):
在 NumPy 中实现此公式既简单又直接:
使这项工作如此出色的是 predictions 和 labels 可以包含一个或一千个值。它们只需要大小相同即可。
您可以这样可视化它:
在这个例子中,预测向量和标签向量都包含三个值,这意味着 n 的值为 3。在我们执行减法后,向量中的值被平方。然后 NumPy 对这些值求和,您的结果就是该预测的误差值以及模型质量的评分。
如何保存和加载 NumPy 对象#
本节涵盖 np.save, np.savez, np.savetxt, np.load, np.loadtxt
在某些时候,您会希望将数组保存到磁盘并重新加载,而无需重新运行代码。幸运的是,NumPy 有几种保存和加载对象的方法。ndarray 对象可以通过处理普通文本文件的 loadtxt 和 savetxt 函数保存到磁盘文件并从中加载,通过处理具有 .npy 文件扩展名的 NumPy 二进制文件的 load 和 save 函数保存和加载,以及通过处理具有 .npz 文件扩展名的 NumPy 文件的 savez 函数保存和加载。
.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]
您可以使用 np.savetxt 将 NumPy 数组保存为纯文本文件,如 .csv 或 .txt 文件。
例如,如果您创建此数组:
>>> csv_arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
您可以像这样轻松地将其保存为名为 “new_file.csv” 的 .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() 函数接受额外的可选参数,例如页眉 (header)、页脚 (footer) 和分隔符 (delimiter)。虽然文本文件可能更容易共享,但 .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]]
同样,使用 Pandas 导出数组也很简单。如果您是 NumPy 新手,您可能希望根据数组中的值创建一个 Pandas dataframe,然后使用 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 dataframe:
>>> 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
您可以轻松保存 dataframe:
>>> df.to_csv('pd.csv')
并读取您的 CSV:
>>> data = pd.read_csv('pd.csv')
您也可以使用 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()
例如,您可以像这样绘制一维数组:
>>> x = np.linspace(0, 5, 20)
>>> y = np.linspace(0, 10, 20)
>>> plt.plot(x, y, 'purple') # line
>>> plt.plot(x, y, 'o') # dots
通过 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')
要阅读有关 Matplotlib 及其功能的更多信息,请查看官方文档。有关安装 Matplotlib 的说明,请参见官方安装部分。
图片来源:Jay Alammar https://jalammar.github.io/