NumPy:面向初学者的绝对基础知识#

欢迎阅读NumPy的绝对初学者指南!

NumPy (Numerical Python) 是一个开源的 Python 库,广泛应用于科学和工程领域.NumPy 库包含多维数组数据结构,例如同构的 N 维 ndarray ,以及一个大型函数库,可以高效地对这些数据结构进行操作.关于 NumPy 的更多信息,请访问 What is NumPy ,如果您有任何意见或建议,请 reach out !

如何导入 NumPy#

installing 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 会利用这些特性使数组比限制较少的数据结构更快,更节省内存并且更方便使用.

在本文档的剩余部分,我们将使用"数组"一词来指代 ndarray 的实例.

数组基础#

初始化数组的一种方法是使用 Python 序列,例如列表.例如:

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

可以使用 various ways 访问数组的元素.例如,我们可以像访问原始列表中的元素一样访问此数组的单个元素:使用方括号中元素的整数索引.

>>> 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-D(零维)数组被称为"标量",1-D(一维)数组被称为"向量",2-D(二维)数组被称为"矩阵",或者 N-D(N 维,其中"N"通常是大于 2 的整数)数组被称为"张量". 为了清楚起见,最好在引用数组时避免使用数学术语,因为具有这些名称的数学对象的行为与数组不同(例如,"矩阵"乘法从根本上不同于"数组"乘法),并且科学 Python 生态系统中还有其他具有这些名称的对象(例如,PyTorch 的基本数据结构是"张量").

数组属性#

本节介绍数组的 ndim , shape , sizedtype 属性.


数组的维数包含在 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

Read more about array attributes here 并了解 array objects here .

如何创建基本数组#

本节介绍 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])

Learn more about creating arrays here

添加,删除和排序元素#

本节介绍 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.ndim , ndarray.size , ndarray.shape


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

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

ndarray.shape 将显示一个整数元组,指示沿数组的每个维度存储的元素数量.例如,如果你有一个 2-D 数组,有 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 顺序的信息,你可以 read more about the internal organization of NumPy arrays here .从本质上讲,C 和 Fortran 顺序与索引如何对应于数组在内存中存储的顺序有关.在 Fortran 中,当在内存中存储的二维数组的元素中移动时,第一个索引是变化最快的索引.当第一个索引移动到下一行时,矩阵一次存储一列.这就是为什么 Fortran 被认为是列优先语言.另一方面,在 C 中,最后一个索引变化最快.矩阵按行存储,使其成为行优先语言.你对 C 或 Fortran 所做的事情取决于保留索引约定还是不重新排序数据是否更重要.

Learn more about shape manipulation here .

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

本节涵盖 np.newaxis , np.expand_dims


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

当使用一次 np.newaxis 时,它会将数组的维度增加一个维度.这意味着 1D 数组将变成 2D 数组,2D 数组将变成 3D 数组,依此类推.

例如,如果你从这个数组开始:

>>> 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 here 了解更多关于 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])

你可以这样可视化它:

../_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))

indexing and slicing herehere 了解更多关于索引和切片的信息.

阅读更多关于使用 nonzero 函数的信息:nonzero .

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

本节介绍 slicing and indexing , 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 处,依此类推.

你还可以垂直和水平堆叠两个现有数组.假设你有两个数组, 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]])]

Learn more about stacking and splitting arrays here 了解更多关于堆叠和分割数组的信息.

你可以使用 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()

Learn more about copies and views here .

基本数组操作#

本节介绍加法,减法,乘法,除法等


创建数组后,就可以开始使用它们了. 例如,假设您创建了两个数组,一个名为"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])

Learn more about basic operations here .

广播#

有时您可能想要在数组和单个数字(也称为向量和标量之间的运算)或两个不同大小的数组之间进行运算. 例如,您的数组(我们称之为"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 .

Learn more about broadcasting here .

更多有用的数组操作#

本节介绍最大值,最小值,总和,平均值,乘积,标准差等


NumPy 还执行聚合函数. 除了 min , maxsum 之外,您还可以轻松运行 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 ])

上面列出的四个值对应于数组中的列数. 对于一个四列的数组,您将得到四个值作为结果.

阅读更多关于 array methods here .

创建矩阵#

您可以传递 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

array creation routines 中阅读更多关于创建填充 0 , 1 ,其他值或未初始化的数组的信息.

生成随机数#

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

使用 Generator.integers ,您可以生成从 low(记住 NumPy 中这是包含性的)到 high(排他性的)的随机整数. 您可以设置 endpoint=True 使 high 数字具有包含性.

您可以使用以下命令生成一个 2 x 4 的介于 0 和 4 之间的随机整数数组:

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

Read more about random number generation here .

如何获取唯一项和计数#

本节介绍 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]

这也适用于 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 将反转输入数组的所有轴的内容.

反转 1D 数组

如果您从这样的 1D 数组开始:

>>> 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]

反转 2D 数组

2D 数组的工作方式大致相同.

如果你从这个数组开始:

>>> 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 将数组展平为 1D 数组.

>>> 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.flattenflattenravelravel .

如何访问文档字符串以获取更多信息#

本节涵盖 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 是一个用于多种语言交互式计算的命令行 shell. You can find more information about IPython here .

例如:

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 本身的详细信息,然后是 ndarray 的文档字符串, a 是它的一个实例):

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

在这个例子中,预测向量和标签向量都包含三个值,这意味着 n 的值为 3.在我们执行减法后,向量中的值被平方.然后 NumPy 对值求和,你的结果是该预测的误差值和模型质量的评分.

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

如何保存和加载 NumPy 对象#

本节涵盖 np.save , np.savez , np.savetxt , np.load , np.loadtxt


在某些时候,你可能需要将数组保存到磁盘并在不重新运行代码的情况下将其加载回来.幸运的是,有几种使用 NumPy 保存和加载对象的方法.ndarray 对象可以使用处理普通文本文件的 loadtxtsavetxt 函数,处理带有 .npy 文件扩展名的 NumPy 二进制文件的 loadsave 函数以及处理带有 .npz 文件扩展名的 NumPy 文件的 savez 函数保存到磁盘文件并从磁盘文件加载.

.npy 和 .npz 文件存储数据,形状,数据类型以及重建 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 ,您可以指定 headers,footers,comments 等.

在此处了解有关 input and output routines here 的更多信息.

导入和导出 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 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')
../_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 的信息,请查看 official Pandas documentation . 了解如何使用 official Pandas installation information 安装 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 及其功能的信息,请查看 the official documentation . 有关安装 Matplotlib 的说明,请参阅官方 installation section .


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