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 创建一个数组,其初始内容是随机的,并且取决于内存的状态.相对于 zeros (或类似的东西)使用 empty 的原因是速度 - 只要确保之后填充每个元素即可!:

>>> # 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 行 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-like 索引顺序读/写元素,否则使用C-like顺序.(这是一个可选参数,不需要指定.)

如果你想学更多关于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 将在使用一次时将数组的维度增加一维.这意味着一维数组将变为二维数组,二维数组将变为三维数组,依此类推.

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

>>> 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,)

你可以使用以下命令在索引位置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 以及 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() .这适用于 1D 数组,2D 数组以及更高维度的数组.

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

>>> a.sum()
10

要添加 2D 数组中的行或列,您需要指定轴.

如果您从此数组开始:

>>> 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 列表的列表以创建一个 2-D 数组(或“矩阵”)以在 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

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

生成随机数#

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

使用 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 数组中唯一值的索引(数组中唯一值的第一个索引位置的数组),只需在 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]

您可以将 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 将反转输入数组的所有轴上的内容.

反转一维数组

如果您从像这样的一个一维数组开始:

>>> 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. 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 的值为三.在我们进行减法运算后,向量中的值将平方.然后 NumPy 对值进行求和,结果是该预测的误差值和模型质量的评分.

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

如何保存和加载 NumPy 对象#

本节介绍 np.save , np.savez , np.savetxt , np.load , np.loadtxt


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

.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 ,你可以指定标题,页脚,注释等.

更多信息请参考 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/