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 提示符中输入可能会导致错误。
要运行示例中的代码,您可以将其复制并粘贴到 Python 脚本或 REPL 中,或者使用文档中不同位置提供的实验性交互式浏览器示例。
为什么要使用 NumPy?#
Python 列表是优秀、通用的容器。它们可以是“异质的”,这意味着它们可以包含各种类型的元素,并且在对少量元素执行单个操作时速度非常快。
根据数据特性和需要执行的操作类型,其他容器可能更合适;通过利用这些特性,我们可以提高速度、减少内存消耗,并为执行各种常见处理任务提供高级语法。当需要处理大量“同质”(相同类型)数据并在 CPU 上运行时,NumPy 表现出色。
什么是“数组”?#
在计算机编程中,数组是一种用于存储和检索数据的结构。我们经常将数组类比于空间中的网格,每个单元格存储数据的一个元素。例如,如果数据的每个元素都是一个数字,我们可以将“一维”数组可视化为一个列表
二维数组就像一个表格
三维数组就像一组表格,可能像打印在不同页面上一样堆叠。在 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 索引”的:第一个数组元素使用索引 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 维”(零维)数组称为“标量”,“1 维”(一维)数组称为“向量”,“2 维”(二维)数组称为“矩阵”,或者 N 维(N-dimensional,其中“N”通常是大于 2 的整数)数组称为“张量”。为清晰起见,最好避免使用数学术语来指代数组,因为具有这些名称的数学对象与数组的行为不同(例如,“矩阵”乘法与“数组”乘法从根本上不同),并且科学 Python 生态系统中存在其他具有这些名称的对象(例如 PyTorch 的基本数据结构是“张量”)。
数组属性#
本节介绍数组的 ndim, shape, size, 和 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
在此处阅读更多关于数组属性的信息,并了解 数组对象的信息。
如何创建基本数组#
本节介绍 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 的内容,请参阅: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 被认为是列主序语言。另一方面,在 C 中,最后一个索引变化最快。矩阵是按行存储的,使其成为行主序语言。对于 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)
您可以通过在第一个维度上插入轴来显式将一维数组转换为行向量或列向量
>>> 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=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
要对二维数组的行或列求和,您需要指定轴。
如果您从这个数组开始
>>> 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 知道乘法应该发生在每个单元格上。这个概念叫做 **广播**。广播是一种允许 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 ])
上面列出的四个值对应于你的数组中的列数。对于一个四列的数组,你将得到四个值作为结果。
在此处阅读更多关于 数组方法 的信息。
创建矩阵#
你可以传递嵌套列表来创建二维数组(或“矩阵),以便在 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 来使高位数字也包含在内。
你可以使用以下方法生成一个 2x4 的随机整数数组,范围在 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 数组中唯一值的索引(唯一值在数组中的第一个索引位置的数组),只需将 return_index 参数和你的数组一起传递给 np.unique()。
>>> unique_values, indices_list = np.unique(a, return_index=True)
>>> print(indices_list)
[ 0 2 3 4 5 6 7 12 13 14]
你可以将 return_counts 参数和你的数组一起传递给 np.unique(),以获取 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]])
如何反转数组#
本节介绍 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]
在此处阅读更多关于 flatten 的信息,请参阅 ndarray.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: <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 社区中得到广泛应用的原因之一。
例如,这是均方误差公式(监督机器学习模型中用于回归的核心公式):
在 NumPy 中实现这个公式非常简单明了:
之所以能如此良好地工作,是因为 predictions 和 labels 可以包含一个或一千个值。它们只需要大小相同。
您可以这样可视化
在此示例中,预测和标签向量都包含三个值,这意味着 n 的值为三。进行减法运算后,向量中的值会被平方。然后 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 将 DataFrame 写入 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/