NumPy-1-26-中文文档-一-

NumPy 1.26 中文文档(一)

原文:numpy.org/doc/

NumPy 用户指南

原文:numpy.org/doc/1.26/user/index.html

本指南是一个概述,解释了重要特性;细节请参阅 NumPy 参考文档。

入门指南

  • 什么是 NumPy?

  • 安装

  • NumPy 快速入门

  • NumPy:初学者的绝对基础

基础知识和用法

  • NumPy 基础知识

    • 数组创建

    • ndarrays 进行索引

    • 使用 NumPy 进行 I/O

    • 数据类型

    • 广播

    • 复制和视图

    • 结构化数组

    • 通用函数 (ufunc) 基础

  • 适用于 MATLAB 用户的 NumPy

  • NumPy 教程

  • NumPy 实用技巧

高级用法和互操作性

  • 从源代码构建

  • 使用 NumPy C-API

  • F2PY 用户指南和参考手册

  • 开发者幕后文档

  • 与 NumPy 的互操作性

开始入门

什么是 NumPy?

原文:numpy.org/doc/1.26/user/whatisnumpy.html

NumPy 是 Python 中科学计算的基础包。 这是一个提供多维数组对象、各种派生对象(如掩码数组和矩阵)以及一系列用于数组快速操作的例程的 Python 库,包括数学、逻辑、形状操作、排序、选择、I/O、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等。

NumPy 包的核心是ndarray对象。这个对象封装了* n *维同种数据类型的数组,许多操作是通过编译的代码执行以提高性能。 NumPy 数组和标准 Python 序列之间有几个重要区别:

  • NumPy 数组在创建时具有固定大小,不像 Python 列表(可以动态增长)。 更改ndarray的大小将创建一个新数组并删除原始数组。

  • NumPy 数组中的元素都必须是相同的数据类型,因此在内存中大小相同。 例外:可以有(Python,包括 NumPy)对象数组,从而允许具有不同大小元素的数组。

  • NumPy 数组可以在大量数据上执行高级数学和其他类型的操作。 通常,这些操作比使用 Python 的内置序列可能更高效,而且代码较少。

  • 越来越多的基于科学和数学的 Python 包正在使用 NumPy 数组; 尽管这些包通常支持 Python 序列输入,但在处理之前会将这些输入转换为 NumPy 数组,并且它们通常输出 NumPy 数组。 换句话说,为了有效地使用当今大部分科学/数学基于 Python 的软件,仅仅了解如何使用 Python 的内置序列类型是不够的 - 还需要知道如何使用 NumPy 数组。

关于序列大小和速度的观点在科学计算中尤为重要。举一个简单的例子,考虑将 1-D 序列中的每个元素与另一个相同长度的序列中的相应元素相乘的情况。如果数据存储在两个 Python 列表ab中,我们可以遍历每个元素:

c = []
for i in range(len(a)):
    c.append(a[i]*b[i]) 

这可以得出正确的答案,但如果ab中各包含数百万个数字,那么我们将为在 Python 中循环的低效率付出代价。 我们可以在 C 中写得更快完成相同的任务(出于清晰起见,我们忽略变量声明、初始化、内存分配等)

for (i = 0; i < rows; i++) {
  c[i] = a[i]*b[i];
} 

这节省了解释 Python 代码并操纵 Python 对象所涉及的所有开销,但代价是放弃了使用 Python 编程获得的好处。此外,随着数据维度的增加,所需的编码工作也会增加。例如,在 2-D 数组的情况下,之前缩写为 C 代码的代码会扩展到

for (i = 0; i < rows; i++) {
  for (j = 0; j < columns; j++) {
    c[i][j] = a[i][j]*b[i][j];
  }
} 

NumPy 让我们兼具两种优势:当涉及ndarray时,逐点操作是“默认模式”,但逐点操作由预编译的 C 代码迅速执行。在 NumPy 中

c = a * b 

做了之前的示例所做的事情,以接近 C 速度进行,但具有基于 Python 的代码简洁性。事实上,NumPy 的习语甚至更简单!这个最后的示例展示了 NumPy 许多强大功能的基础:矢量化和广播。

为什么 NumPy 如此快速?

矢量化描述代码中的任何显式循环、索引等都不存在——当然,这些事情当然存在,只是以优化的、预编译的 C 代码“在幕后”方式进行。矢量化代码有许多优势,其中包括:

  • 矢量化代码更简洁,更易读

  • 更少的代码行通常意味着更少的错误

  • 代码更接近标准数学符号表示法(通常使数学构造的正确编码更容易)

  • 矢量化导致了更“Pythonic”的代码。没有矢量化,我们的代码将充斥着效率低下且难以阅读的for循环。

广播是用来描述操作的隐式逐点行为的术语;一般来说,在 NumPy 中,所有操作(不仅仅是算术操作,还有逻辑、位运算、函数等)都以这种隐式逐点方式行为,即它们进行广播。此外,在上面的示例中,ab 可能是相同形状的多维数组,或者是标量和数组,甚至是两个形状不同的数组,只要较小的数组可以“扩展”到大数组的形状,使得结果的广播是明确的。有关广播的详细“规则”请参见 Broadcasting。

谁还在使用 NumPy?

NumPy 充分支持面向对象的方法,再次从 ndarray 开始。例如,ndarray 是一个类,拥有 numerous 方法和属性。它的许多方法与 NumPy 最外层命名空间中的函数相对应,允许程序员使用他们偏好的任何范例编码。这种灵活性使得 NumPy 数组方言和 NumPy ndarray 类成为 Python 中用于多维数据交换的事实上语言。

为什么 NumPy 如此快速?

矢量化描述代码中的任何显式循环、索引等都不存在——当然,这些事情当然存在,只是以优化的、预编译的 C 代码“在幕后”方式进行。矢量化代码有许多优势,其中包括:

  • 向量化的代码更简洁,更易读。

  • 更少的代码行通常意味着更少的错误。

  • 代码更接近标准数学表示法(通常更容易正确编写数学结构)。

  • 向量化导致更“Pythonic”的代码。没有向量化,我们的代码将充满效率低下且难以阅读的for循环。

广播是用来描述操作的隐式逐元素行为的术语;一般来说,在 NumPy 中所有操作,不仅仅是算术操作,还包括逻辑、位操作、函数等等,都以这种隐式逐元素的方式进行,即它们进行广播。此外,在上面的示例中,ab 可以是相同形状的多维数组,或者是标量和数组,甚至是两个形状不同的数组,只要较小的数组可以“扩展”到较大数组的形状,使得结果的广播不会产生歧义即可。有关广播的详细“规则”,请参阅广播。

谁还在使用 NumPy?

NumPy 完全支持面向对象的方法,再次以 ndarray 为例。例如,ndarray 是一个类,拥有许多方法和属性。其中许多方法在 NumPy 最外层的命名空间中都有相应的函数,使程序员可以按照他们喜欢的范式编码。这种灵活性使得 NumPy 数组方言和 NumPy ndarray 类成为 Python 中多维数据交换的事实标准语言。

NumPy 快速开始

原文:numpy.org/doc/1.26/user/quickstart.html

先决条件

你需要了解一点 Python。需要恢复记忆的话,参见Python 教程

要运行这些示例,除了 NumPy 外,你还需要安装matplotlib

学习者概况

这是 NumPy 中数组的快速概述。它演示了如何表示和操作 n 维((n>=2))数组。特别是,如果你不知道如何在 n 维数组上应用常见的函数(而不使用 for 循环),或者想理解关于 n 维数组的轴和形状属性,这篇文章可能会有所帮助。

学习目标

阅读完之后,你应该能够:

  • 了解在 NumPy 中一维、二维和 n 维数组之间的区别;

  • 了解如何在 n 维数组上应用一些线性代数操作,而不使用 for 循环;

  • 了解 n 维数组的轴和形状属性。

基础知识

NumPy 的主要对象是同质的多维数组。它是一张元素表(通常是数字),全部是相同类型的,通过非负整数的元组索引。在 NumPy 中,维度被称为

例如,三维空间中一个点的坐标的数组[1, 2, 1]有一个轴。该轴有 3 个元素,因此我们说它的长度是 3。在下面的例子中,数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。

[[1., 0., 0.],
 [0., 1., 2.]] 

NumPy 的数组类称为ndarray。它也被别名array所知。注意,numpy.array并不等同于标准 Python 库的array.array类,后者只处理一维数组并提供较少的功能。ndarray对象的更重要的属性有:

ndarray.ndim

数组的轴(维度)数量。

ndarray.shape

数组的维度。这是一个整数元组,指示每个维度上数组的大小。对于一个有n行和m列的矩阵,shape将是(n,m)。因此shape元组的长度即为轴的数量,即ndim

ndarray.size

数组中元素的总个数。这等于shape元素的乘积。

ndarray.dtype

描述数组中元素类型的对象。可以使用标准的 Python 类型创建或指定 dtype。另外,NumPy 提供了自己的类型。numpy.int32numpy.int16numpy.float64是一些例子。

ndarray.itemsize

数组中每个元素的字节大小。例如,一个float64类型的元素数组的itemsize是 8(=64/8),而一个complex32类型的元素数组的itemsize是 4(=32/8)。它等同于ndarray.dtype.itemsize

ndarray.data

包含数组实际元素的缓冲区。通常,我们不需要使用这个属性,因为我们将使用索引设施来访问数组中的元素。

例子

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
 [ 5,  6,  7,  8,  9],
 [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'> 

数组创建

有几种创建数组的方式。

例如,您可以使用array函数从常规 Python 列表或元组创建数组。结果数组的类型是从序列中的元素的类型推断出来的。

>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64') 

经常出错的一个错误是调用array时提供多个参数,而不是提供单个序列作为参数。

>>> a = np.array(1, 2, 3, 4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1, 2, 3, 4])  # RIGHT 

array将序列的序列转换为二维数组,序列的序列的序列转换为三维数组,依此类推。

>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2\. , 3\. ],
 [4\. , 5\. , 6\. ]]) 

数组的类型也可以在创建时明确指定:

>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
 [3.+0.j, 4.+0.j]]) 

通常,数组的元素最初是未知的,但其大小已知。因此,NumPy 提供了几个函数来创建带有初始占位内容的数组。这些函数最小化了增长数组的必要性,这是一项昂贵的操作。

函数zeros创建一个全是零的数组,函数ones创建一个全是一的数组,函数empty创建一个初始内容是随机的依赖于内存状态的数组。默认情况下,创建的数组的 dtype 是float64,但可以通过关键字参数dtype指定。

>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
 [0., 0., 0., 0.],
 [0., 0., 0., 0.]])
>>> np.ones((2, 3, 4), dtype=np.int16)
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]]], dtype=int16)
>>> np.empty((2, 3)) 
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
 [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) 

要创建数字序列,NumPy 提供了arange函数,它类似于 Python 内置的range,但返回一个数组。

>>> np.arange(10, 30, 5)
array([10, 15, 20, 25])
>>> np.arange(0, 2, 0.3)  # it accepts float arguments
array([0\. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) 

arange与浮点参数一起使用时,由于有限的浮点精度,通常无法预测所得元素的数量。因此,通常最好使用函数linspace,该函数接受我们想要的元素数量作为参数,而不是步长:

>>> from numpy import pi
>>> np.linspace(0, 2, 9)                   # 9 numbers from 0 to 2
array([0\.  , 0.25, 0.5 , 0.75, 1\.  , 1.25, 1.5 , 1.75, 2\.  ])
>>> x = np.linspace(0, 2 * pi, 100)        # useful to evaluate function at lots of points
>>> f = np.sin(x) 

参见

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.Generator.rand, numpy.random.Generator.randn, fromfunction, fromfile

打印数组

当您打印一个数组时,NumPy 以类似嵌套列表的方式显示它,但布局如下:

  • 最后一个轴从左到右打印,

  • 倒数第二个从上到下打印,

  • 其余部分也是从上到下打印的,每个切片之间用空行分隔。

一维数组被打印为行,二维数组被打印为矩阵,三维数组被打印为矩阵的列表。

>>> a = np.arange(6)                    # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4, 3)     # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2, 3, 4)  # 3d array
>>> print(c)
[[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

 [[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]] 

请参见下文以了解reshape的更多详细信息。

如果数组太大无法打印,NumPy 将自动跳过数组的中间部分,并只打印角落:

>>> print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]] 

要禁用这种行为,并强制 NumPy 打印整个数组,你可以使用set_printoptions来更改打印选项。

>>> np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported 

基本操作

数组上的算术运算符是逐元素应用的。创建一个新数组,并用结果填充。

>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> c = a - b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a < 35
array([ True,  True, False, False]) 

与许多矩阵语言不同,在 NumPy 数组中,乘法运算符*是逐元素操作的。矩阵乘积可以使用@运算符(在 Python >=3.5 中)或dot函数或方法来执行:

>>> A = np.array([[1, 1],
...               [0, 1]])
>>> B = np.array([[2, 0],
...               [3, 4]])
>>> A * B     # elementwise product
array([[2, 0],
 [0, 4]])
>>> A @ B     # matrix product
array([[5, 4],
 [3, 4]])
>>> A.dot(B)  # another matrix product
array([[5, 4],
 [3, 4]]) 

一些操作,比如+=*=,是就地修改现有数组而不是创建新数组。

>>> rg = np.random.default_rng(1)  # create instance of default random number generator
>>> a = np.ones((2, 3), dtype=int)
>>> b = rg.random((2, 3))
>>> a *= 3
>>> a
array([[3, 3, 3],
 [3, 3, 3]])
>>> b += a
>>> b
array([[3.51182162, 3.9504637 , 3.14415961],
 [3.94864945, 3.31183145, 3.42332645]])
>>> a += b  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind' 

当对不同类型的数组进行操作时,结果数组的类型对应于更一般或更精确的类型(这种行为称为向上转型)。

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0, pi, 3)
>>> b.dtype.name
'float64'
>>> c = a + b
>>> c
array([1\.        , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c * 1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
 -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128' 

许多一元操作,如计算数组中所有元素的和,都作为ndarray类的方法实现。

>>> a = rg.random((2, 3))
>>> a
array([[0.82770259, 0.40919914, 0.54959369],
 [0.02755911, 0.75351311, 0.53814331]])
>>> a.sum()
3.1057109529998157
>>> a.min()
0.027559113243068367
>>> a.max()
0.8277025938204418 

默认情况下,这些操作将应用于数组,就像它是数字的列表一样,而不考虑其形状。然而,通过指定axis参数,你可以沿着指定的轴对数组应用操作:

>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)     # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)     # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)  # cumulative sum along each row
array([[ 0,  1,  3,  6],
 [ 4,  9, 15, 22],
 [ 8, 17, 27, 38]]) 

通用函数

NumPy 提供了熟悉的数学函数,如 sin,cos 和 exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数对数组进行逐元素操作,产生一个数组作为输出。

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1\.        , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0\.        , 1\.        , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([2., 0., 6.]) 

请参见下文

allanyapply_along_axisargmaxargminargsortaveragebincountceilclipconjcorrcoefcovcrosscumprodcumsumdiffdotfloorinnerinvertlexsortmaxmaximummeanmedianminminimumnonzeroouterprodreroundsortstdsumtracetransposevarvdotvectorizewhere

索引、切片和迭代

一维数组可以像列表和其他 Python 序列一样进行索引、切片和迭代。

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> # equivalent to a[0:6:2] = 1000;
>>> # from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 1000
>>> a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>> a[::-1]  # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>> for i in a:
...     print(i**(1 / 3.))
...
9.999999999999998  # may vary
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998 

多维数组可以有每个轴的一个索引。这些索引以逗号分隔的元组给出:

>>> def f(x, y):
...     return 10 * x + y
...
>>> b = np.fromfunction(f, (5, 4), dtype=int)
>>> b
array([[ 0,  1,  2,  3],
 [10, 11, 12, 13],
 [20, 21, 22, 23],
 [30, 31, 32, 33],
 [40, 41, 42, 43]])
>>> b[2, 3]
23
>>> b[0:5, 1]  # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[:, 1]    # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, :]  # each column in the second and third row of b
array([[10, 11, 12, 13],
 [20, 21, 22, 23]]) 

如果提供的索引少于轴的数量,则缺失的索引被视为完整的切片:

>>> b[-1]   # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43]) 

方括号中的表达式b[i]被视为一个i,后面跟着需要表示剩余轴的冒号实例。NumPy 还允许您使用点表示法b[i, ...]来编写它。

...)表示需要生成完整索引元组的多少个冒号。例如,如果x是一个具有 5 个轴的数组,则

  • x[1, 2, ...] 相当于 x[1, 2, :, :, :]

  • x[..., 3] 相当于 x[:, :, :, :, 3]

  • x[4, ..., 5, :] 相当于 x[4, :, :, 5, :]

>>> c = np.array([[[  0,  1,  2],  # a 3D array (two stacked 2D arrays)
...                [ 10, 12, 13]],
...               [[100, 101, 102],
...                [110, 112, 113]]])
>>> c.shape
(2, 2, 3)
>>> c[1, ...]  # same as c[1, :, :] or c[1]
array([[100, 101, 102],
 [110, 112, 113]])
>>> c[..., 2]  # same as c[:, :, 2]
array([[  2,  13],
 [102, 113]]) 

对多维数组进行迭代是针对第一个轴进行的:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43] 

但是,如果想对数组中的每个元素执行操作,可以使用flat属性,该属性是对数组的所有元素的迭代器

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43 

参见

ndarrays 的索引、索引例程(参考)、newaxisndenumerateindices ## 形状操作

改变数组的形状

数组的形状由沿每个轴的元素数量确定:

>>> a = np.floor(10 * rg.random((3, 4)))
>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.shape
(3, 4) 

数组的形状可以用各种命令进行更改。请注意,以下三个命令都会返回一个修改后的数组,但不会更改原始数组:

>>> a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>> a.reshape(6, 2)  # returns the array with a modified shape
array([[3., 7.],
 [3., 4.],
 [1., 4.],
 [2., 2.],
 [7., 2.],
 [4., 9.]])
>>> a.T  # returns the array, transposed
array([[3., 1., 7.],
 [7., 4., 2.],
 [3., 2., 4.],
 [4., 2., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4) 

ravel结果数组中的元素顺序通常是“C 风格”,即,最右边的索引“变化最快”,因此a[0, 0]之后的元素是a[0, 1]。如果数组被重塑为其他形状,那么数组再次被视为“C 风格”。NumPy 通常创建以这种顺序存储的数组,因此ravel通常不需要复制其参数,但如果数组是通过对另一个数组取片或使用不寻常的选项创建的,则可能需要复制。函数ravelreshape还可以使用可选参数指示使用 FORTRAN 风格的数组,其中最左边的索引变化最快。

reshape函数返回其具有修改形状的参数,而ndarray.resize方法修改数组本身:

>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.resize((2, 6))
>>> a
array([[3., 7., 3., 4., 1., 4.],
 [2., 2., 7., 2., 4., 9.]]) 

如果在重塑操作中给定维度为-1,则其他维度将自动计算:

>>> a.reshape(3, -1)
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]]) 

另请参阅

ndarray.shape, reshape, resize, ravel

将不同数组堆叠在一起

可以沿不同轴堆叠几个数组:

>>> a = np.floor(10 * rg.random((2, 2)))
>>> a
array([[9., 7.],
 [5., 2.]])
>>> b = np.floor(10 * rg.random((2, 2)))
>>> b
array([[1., 9.],
 [5., 1.]])
>>> np.vstack((a, b))
array([[9., 7.],
 [5., 2.],
 [1., 9.],
 [5., 1.]])
>>> np.hstack((a, b))
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]]) 

函数 column_stack 将 1D 数组作为列堆叠到 2D 数组中。它仅对于 2D 数组等效于 hstack:

>>> from numpy import newaxis
>>> np.column_stack((a, b))  # with 2D arrays
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]])
>>> a = np.array([4., 2.])
>>> b = np.array([3., 8.])
>>> np.column_stack((a, b))  # returns a 2D array
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a, b))        # the result is different
array([4., 2., 3., 8.])
>>> a[:, newaxis]  # view `a` as a 2D column vector
array([[4.],
 [2.]])
>>> np.column_stack((a[:, newaxis], b[:, newaxis]))
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a[:, newaxis], b[:, newaxis]))  # the result is the same
array([[4., 3.],
 [2., 8.]]) 

另一方面,函数 row_stack 对于任何输入数组都相当于 vstack。事实上,row_stackvstack 的别名:

>>> np.column_stack is np.hstack
False
>>> np.row_stack is np.vstack
True 

一般来说,对于超过两个维度的数组,hstack 沿第二个轴堆叠,vstack 沿第一个轴堆叠,而 concatenate 允许一个可选参数,用于指定连接应该发生的轴的编号。

注意

在复杂情况下,r_c_ 对于通过在一个轴上堆叠数字创建数组非常有用。它们允许使用范围文字 :

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

当用数组作为参数时,r_c_ 在默认行为上类似于 vstackhstack,但允许一个可选参数,用于指定沿哪个轴进行连接。

另请参阅

hstack, vstack, column_stack, concatenate, c_, r_

将一个数组拆分为几个较小的数组

使用hsplit,您可以沿着数组的水平轴分割数组,可以通过指定要返回的等形数组的数量,也可以通过指定在哪些列之后进行分割:

>>> a = np.floor(10 * rg.random((2, 12)))
>>> a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
 [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
>>> # Split `a` into 3
>>> np.hsplit(a, 3)
[array([[6., 7., 6., 9.],
 [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
 [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
 [1., 8., 1., 0.]])]
>>> # Split `a` after the third and the fourth column
>>> np.hsplit(a, (3, 4))
[array([[6., 7., 6.],
 [8., 5., 5.]]), array([[9.],
 [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
 [1., 8., 6., 7., 1., 8., 1., 0.]])] 

vsplit 沿垂直轴拆分,而 array_split 允许指定沿哪个轴进行拆分。## 复制和视图

在操作和操作数组时,有时会将它们的数据复制到新数组中,有时则不会。这常常会让初学者感到困惑。有三种情况:

没有任何复制

简单的赋值不会复制对象或其数据。

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True 

Python 以引用的方式传递可变对象,因此函数调用不会复制。

>>> def f(x):
...     print(id(x))
...
>>> id(a)  # id is a unique identifier of an object 
148293216  # may vary
>>> f(a)   
148293216  # may vary 

查看或浅拷贝

不同的数组对象可以共享相同的数据。view 方法创建一个查看相同数据的新数组对象。

>>> c = a.view()
>>> c is a
False
>>> c.base is a            # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))  # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234         # a's data changes
>>> a
array([[   0,    1,    2,    3],
 [1234,    5,    6,    7],
 [   8,    9,   10,   11]]) 

对数组进行切片会返回它的视图:

>>> s = a[:, 1:3]
>>> s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

深复制

copy 方法将数组及其数据完全复制一份。

>>> d = a.copy()  # a new array object with new data is created
>>> d is a
False
>>> d.base is a  # d doesn't share anything with a
False
>>> d[0, 0] = 9999
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

有时在切片之后如果不再需要原始数组,则应调用 copy。例如,假设 a 是一个巨大的中间结果,而最终结果 b 只包含 a 的一小部分,则在使用切片构造 b 时应进行深复制:

>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a  # the memory of ``a`` can be released. 

如果使用b = a[:100],则ab引用,并且即使执行del aa也会在内存中持续存在。

函数和方法概述

这是一些有用的 NumPy 函数和方法名称的列表,按类别排序。请参阅 例程 获取完整列表。

数组创建

arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r_, zeros, zeros_like

转换

ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

操作

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

问题

all, any, nonzero, where

排序

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作

choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

基本统计

cov, mean, std, var

基本线性代数

cross, dot, outer, linalg.svd, vdot

更不基础的

广播规则

广播允许通用函数以有意义的方式处理形状不完全相同的输入。

广播的第一规则是,如果所有输入数组的维度数不相同,那么“1”将被重复地前置到较小数组的形状上,直到所有数组具有相同的维度数。

广播的第二规则确保了沿着特定维度大小为 1 的数组的行为,就好像它们在该维度上的最大形状的数组的大小一样。假定“广播”数组沿着那个维度的数组元素的值是相同的。

应用广播规则后,所有数组的大小必须匹配。更多细节可以在 Broadcasting 中找到。

高级索引和索引技巧

NumPy 提供比普通 Python 序列更多的索引工具。除了之前我们所见到的按整数和切片进行索引之外,数组还可以通过整数数组和布尔数组进行索引。

使用索引数组进行索引

>>> a = np.arange(12)**2  # the first 12 square numbers
>>> i = np.array([1, 1, 3, 8, 5])  # an array of indices
>>> a[i]  # the elements of `a` at the positions `i`
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array([[3, 4], [9, 7]])  # a bidimensional array of indices
>>> a[j]  # the same shape as `j`
array([[ 9, 16],
 [81, 49]]) 

当索引数组a是多维的时,单个索引数组会引用a的第一个维度。以下示例通过使用调色板,将标签图像转换为彩色图像展示了这种行为。

>>> palette = np.array([[0, 0, 0],         # black
...                     [255, 0, 0],       # red
...                     [0, 255, 0],       # green
...                     [0, 0, 255],       # blue
...                     [255, 255, 255]])  # white
>>> image = np.array([[0, 1, 2, 0],  # each value corresponds to a color in the palette
...                   [0, 3, 4, 0]])
>>> palette[image]  # the (2, 4, 3) color image
array([[[  0,   0,   0],
 [255,   0,   0],
 [  0, 255,   0],
 [  0,   0,   0]],

 [[  0,   0,   0],
 [  0,   0, 255],
 [255, 255, 255],
 [  0,   0,   0]]]) 

我们还可以为多个维度提供索引。每个维度的索引数组必须具有相同的形状。

>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>> i = np.array([[0, 1],  # indices for the first dim of `a`
...               [1, 2]])
>>> j = np.array([[2, 1],  # indices for the second dim
...               [3, 3]])
>>>
>>> a[i, j]  # i and j must have equal shape
array([[ 2,  5],
 [ 7, 11]])
>>>
>>> a[i, 2]
array([[ 2,  6],
 [ 6, 10]])
>>>
>>> a[:, j]
array([[[ 2,  1],
 [ 3,  3]],

 [[ 6,  5],
 [ 7,  7]],

 [[10,  9],
 [11, 11]]]) 

在 Python 中,arr[i, j]arr[(i, j)]完全相同 - 因此我们可以将ij放入一个tuple,然后使用该索引。

>>> l = (i, j)
>>> # equivalent to a[i, j]
>>> a[l]
array([[ 2,  5],
 [ 7, 11]]) 

然而,我们不能将ij放入一个数组中来做到这一点,因为这个数组会被解释为索引a的第一个维度。

>>> s = np.array([i, j])
>>> # not what we want
>>> a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3
>>> # same as `a[i, j]`
>>> a[tuple(s)]
array([[ 2,  5],
 [ 7, 11]]) 

使用数组进行索引的另一个常见用途是搜索时序系列的最大值:

>>> time = np.linspace(20, 145, 5)  # time scale
>>> data = np.sin(np.arange(20)).reshape(5, 4)  # 4 time-dependent series
>>> time
array([ 20\.  ,  51.25,  82.5 , 113.75, 145\.  ])
>>> data
array([[ 0\.        ,  0.84147098,  0.90929743,  0.14112001],
 [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
 [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
 [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
 [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
>>> # index of the maxima for each series
>>> ind = data.argmax(axis=0)
>>> ind
array([2, 0, 3, 1])
>>> # times corresponding to the maxima
>>> time_max = time[ind]
>>>
>>> data_max = data[ind, range(data.shape[1])]  # => data[ind[0], 0], data[ind[1], 1]...
>>> time_max
array([ 82.5 ,  20\.  , 113.75,  51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>> np.all(data_max == data.max(axis=0))
True 

你还可以使用数组作为目标进行编制索引:

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

然而,当索引列表包含重复项时,赋值会多次进行,留下最后一个值:

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

虽然这是足够合理的,但要注意如果你想使用 Python 的+=构造,因为它可能不会如你所期望地运行:

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

尽管索引列表中 0 出现了两次,但第 0 个元素只增加了一次。这是因为 Python 要求a += 1等价于a = a + 1

使用布尔数组进行索引

当我们使用(整数)索引数组对数组进行索引时,我们提供要选择的索引列表。使用布尔索引时,方法不同;我们明确选择数组中的哪些项和哪些项不要。

对于布尔索引,最自然的想法之一是使用与原始数组相同形状的布尔数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b = a > 4
>>> b  # `b` is a boolean with `a`'s shape
array([[False, False, False, False],
 [False,  True,  True,  True],
 [ True,  True,  True,  True]])
>>> a[b]  # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11]) 

这个特性在赋值中非常有用:

>>> a[b] = 0  # All elements of `a` higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
 [4, 0, 0, 0],
 [0, 0, 0, 0]]) 

你可以查看下面的例子,看如何使用布尔索引生成Mandelbrot 集合的图像:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot(h, w, maxit=20, r=2):
...  """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     x = np.linspace(-2.5, 1.5, 4*h+1)
...     y = np.linspace(-1.5, 1.5, 3*w+1)
...     A, B = np.meshgrid(x, y)
...     C = A + B*1j
...     z = np.zeros_like(C)
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + C
...         diverge = abs(z) > r                    # who is diverging
...         div_now = diverge & (divtime == maxit)  # who is diverging now
...         divtime[div_now] = i                    # note when
...         z[diverge] = r                          # avoid diverging too much
...
...     return divtime
>>> plt.clf()
>>> plt.imshow(mandelbrot(400, 400)) 

../_images/quickstart-1.png

使用布尔值进行索引的第二种方式更类似于整数索引;对数组的每个维度,我们提供一个选择我们想要的切片的 1D 布尔数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b1 = np.array([False, True, True])         # first dim selection
>>> b2 = np.array([True, False, True, False])  # second dim selection
>>>
>>> a[b1, :]                                   # selecting rows
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                      # same thing
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[:, b2]                                   # selecting columns
array([[ 0,  2],
 [ 4,  6],
 [ 8, 10]])
>>>
>>> a[b1, b2]                                  # a weird thing to do
array([ 4, 10]) 

注意 1D 布尔数组的长度必须与您要切片的维度(或轴)的长度相一致。在前面的例子中,b1的长度为 3(a中的 数),而b2(长度为 4)适合于索引a的第 2 轴(列)。

ix_() 函数

ix_函数可用于组合不同的向量,以便为每个 n-uplet(n 元组)的结果。例如,如果想要计算从向量 a、b 和 c 中取出的所有三元组的 a+b*c:

>>> a = np.array([2, 3, 4, 5])
>>> b = np.array([8, 5, 4])
>>> c = np.array([5, 4, 6, 8, 3])
>>> ax, bx, cx = np.ix_(a, b, c)
>>> ax
array([[[2]],

 [[3]],

 [[4]],

 [[5]]])
>>> bx
array([[[8],
 [5],
 [4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax + bx * cx
>>> result
array([[[42, 34, 50, 66, 26],
 [27, 22, 32, 42, 17],
 [22, 18, 26, 34, 14]],

 [[43, 35, 51, 67, 27],
 [28, 23, 33, 43, 18],
 [23, 19, 27, 35, 15]],

 [[44, 36, 52, 68, 28],
 [29, 24, 34, 44, 19],
 [24, 20, 28, 36, 16]],

 [[45, 37, 53, 69, 29],
 [30, 25, 35, 45, 20],
 [25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17 

你也可以如下实现 reduce:

>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r, v)
...    return r 

然后使用它:

>>> ufunc_reduce(np.add, a, b, c)
array([[[15, 14, 16, 18, 13],
 [12, 11, 13, 15, 10],
 [11, 10, 12, 14,  9]],

 [[16, 15, 17, 19, 14],
 [13, 12, 14, 16, 11],
 [12, 11, 13, 15, 10]],

 [[17, 16, 18, 20, 15],
 [14, 13, 15, 17, 12],
 [13, 12, 14, 16, 11]],

 [[18, 17, 19, 21, 16],
 [15, 14, 16, 18, 13],
 [14, 13, 15, 17, 12]]]) 

与普通的 ufunc.reduce 相比,这个 reduce 版本的优势在于它利用广播规则来避免创建一个大小等于输出乘以向量数量的参数数组。

使用字符串进行索引

参见结构化数组。

技巧和提示

这里我们列出一些简短且有用的提示。

“自动”重塑

要改变数组的维度,你可以省略其中一个尺寸,这样它将自动推导出来:

>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3))  # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
array([[[ 0,  1,  2],
 [ 3,  4,  5],
 [ 6,  7,  8],
 [ 9, 10, 11],
 [12, 13, 14]],

 [[15, 16, 17],
 [18, 19, 20],
 [21, 22, 23],
 [24, 25, 26],
 [27, 28, 29]]]) 

向量堆叠

我们如何从一系列等大小的行向量构造一个二维数组?在 MATLAB 中,这相当容易:如果 xy 是长度相同的两个向量,你只需要 m=[x;y]。在 NumPy 中,这可以通过函数 column_stackdstackhstackvstack 来实现,具体取决于堆叠的维度。例如:

>>> x = np.arange(0, 10, 2)
>>> y = np.arange(5)
>>> m = np.vstack([x, y])
>>> m
array([[0, 2, 4, 6, 8],
 [0, 1, 2, 3, 4]])
>>> xy = np.hstack([x, y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4]) 

那些超过两个维度的函数背后的逻辑可能会有些奇怪。

也请参见

NumPy 适用于 MATLAB 用户

直方图

应用于数组的 NumPy histogram 函数返回一对向量:数组的直方图和一个箱边的向量。注意:matplotlib 也有一个构建直方图的函数(称为 hist,类似于 Matlab),它与 NumPy 中的函数不同。主要区别在于 pylab.hist 会自动绘制直方图,而 numpy.histogram 只会生成数据。

>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5² and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu, sigma, 10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=True)       # matplotlib version (plot)
(array...)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True)  # NumPy version (no plot)
>>> plt.plot(.5 * (bins[1:] + bins[:-1]), n) 

../_images/quickstart-2.png

使用 Matplotlib >=3.4,你也可以使用 plt.stairs(n, bins)

进一步阅读

先决条件

你需要了解一些 Python。需要复习的话,请参见Python 教程

要运行示例,除了 NumPy 外,你还需要安装 matplotlib

学习者档案

这是 NumPy 中数组的快速概述。它演示了如何表示和操作 n 维((n>=2)))数组。特别是,如果你不知道如何将常见函数应用于 n 维数组(不使用 for 循环),或者如果你想了解 n 维数组的轴和形状属性,这篇文章可能会有所帮助。

学习目标

阅读后,你应该能够:

  • 了解 NumPy 中一维、二维和 n 维数组之间的区别;

  • 了解如何将一些线性代数操作应用于 n 维数组,而不使用 for 循环;

  • 了解 n 维数组的轴和形状属性。

基础知识

NumPy 的主要对象是同构的多维数组。它是一个元素表(通常是数字),所有元素的类型相同,由非负整数的元组索引。在 NumPy 中,维度称为

例如,三维空间中一点的坐标数组 [1, 2, 1] 具有一个轴。该轴中有 3 个元素,因此我们说它的长度为 3。在下面的示例中,数组有 2 个轴。第一个轴长度为 2,第二个轴长度为 3。

[[1., 0., 0.],
 [0., 1., 2.]] 

NumPy 的数组类称为 ndarray。它也以别名 array 而闻名。注意 numpy.array 不同于标准 Python 库中的类 array.array,后者只处理一维数组并提供较少的功能。ndarray 对象的更重要的属性有:

ndarray.ndim

数组的轴数(维数)。

ndarray.shape

数组的维度。这是一个整数元组,指示每个维度中数组的大小。对于具有 n 行和 m 列的矩阵,shape 将是 (n,m)。因此,shape 元组的长度是轴数 ndim

ndarray.size

数组的元素总数。这等于 shape 元素的乘积。

ndarray.dtype

描述数组中元素类型的对象。可以使用标准的 Python 类型创建或指定 dtype。此外,NumPy 还提供了自己的类型。numpy.int32、numpy.int16 和 numpy.float64 是一些示例。

ndarray.itemsize

数组中每个元素的字节大小。例如,类型为 float64 的元素数组的 itemsize 为 8(=64/8),而类型为 complex32 的元素数组的 itemsize 为 4(=32/8)。它相当于 ndarray.dtype.itemsize

ndarray.data

包含数组实际元素的缓冲区。通常,我们不需要使用此属性,因为我们将使用索引功能访问数组中的元素。

一个例子

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
 [ 5,  6,  7,  8,  9],
 [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'> 

数组创建

有几种创建数组的方法。

例如,可以使用 array 函数从常规的 Python 列表或元组创建数组。结果数组的类型是从序列中元素的类型推断出来的。

>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64') 

经常出现的错误是调用 array 时提供多个参数,而不是提供一个序列作为参数。

>>> a = np.array(1, 2, 3, 4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1, 2, 3, 4])  # RIGHT 

array 将序列序列转换为二维数组,序列序列序列转换为三维数组,依此类推。

>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2\. , 3\. ],
 [4\. , 5\. , 6\. ]]) 

数组的类型也可以在创建时明确指定:

>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
 [3.+0.j, 4.+0.j]]) 

通常,数组的元素最初是未知的,但其大小已知。因此,NumPy 提供了几个函数来创建带有初始占位符内容的数组。这些函数最小化了增长数组的必要性,这是一项昂贵的操作。

函数 zeros 创建一个全为零的数组,函数 ones 创建一个全为一的数组,函数 empty 创建一个初始内容是随机的数组,取决于内存状态。默认情况下,创建的数组的 dtype 是 float64,但可以通过关键字参数 dtype 指定。

>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
 [0., 0., 0., 0.],
 [0., 0., 0., 0.]])
>>> np.ones((2, 3, 4), dtype=np.int16)
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]]], dtype=int16)
>>> np.empty((2, 3)) 
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
 [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) 

要创建数字序列,NumPy 提供了arange函数,它类似于 Python 内置的range,但返回一个数组。

>>> np.arange(10, 30, 5)
array([10, 15, 20, 25])
>>> np.arange(0, 2, 0.3)  # it accepts float arguments
array([0\. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) 

arange与浮点参数一起使用时,通常无法预测获得的元素数量,这是由于有限的浮点精度。因此,通常最好使用函数linspace,它接收我们想要的元素数量作为参数,而不是步长:

>>> from numpy import pi
>>> np.linspace(0, 2, 9)                   # 9 numbers from 0 to 2
array([0\.  , 0.25, 0.5 , 0.75, 1\.  , 1.25, 1.5 , 1.75, 2\.  ])
>>> x = np.linspace(0, 2 * pi, 100)        # useful to evaluate function at lots of points
>>> f = np.sin(x) 

另请参阅

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.Generator.rand, numpy.random.Generator.randn, fromfunction, fromfile

打印数组

当您打印一个数组时,NumPy 会以嵌套列表的方式显示,但布局如下:

  • 最后一轴从左到右打印,

  • 倒数第二个从上到下打印,

  • 其余部分也会从上到下打印,每个切片之间由一个空行分隔。

一维数组然后按行打印,二维数组按矩阵打印,三维数组按矩阵列表打印。

>>> a = np.arange(6)                    # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4, 3)     # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2, 3, 4)  # 3d array
>>> print(c)
[[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

 [[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]] 

请看下文以获取有关reshape的更多详细信息。

如果数组太大无法打印,NumPy 会自动跳过数组的中心部分,只打印角落部分:

>>> print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]] 

要禁用此行为并强制 NumPy 打印整个数组,您可以使用set_printoptions更改打印选项。

>>> np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported 

基本运算

数组上的算术运算符以逐元素方式应用。将创建一个新数组并填充结果。

>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> c = a - b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a < 35
array([ True,  True, False, False]) 

与许多矩阵语言不同,在 NumPy 数组中,乘积运算符*以元素方式操作。可以使用@运算符(在 python >=3.5 中)或dot函数或方法执行矩阵乘积:

>>> A = np.array([[1, 1],
...               [0, 1]])
>>> B = np.array([[2, 0],
...               [3, 4]])
>>> A * B     # elementwise product
array([[2, 0],
 [0, 4]])
>>> A @ B     # matrix product
array([[5, 4],
 [3, 4]])
>>> A.dot(B)  # another matrix product
array([[5, 4],
 [3, 4]]) 

一些操作,例如+=*=,会就地修改现有数组,而不是创建新数组。

>>> rg = np.random.default_rng(1)  # create instance of default random number generator
>>> a = np.ones((2, 3), dtype=int)
>>> b = rg.random((2, 3))
>>> a *= 3
>>> a
array([[3, 3, 3],
 [3, 3, 3]])
>>> b += a
>>> b
array([[3.51182162, 3.9504637 , 3.14415961],
 [3.94864945, 3.31183145, 3.42332645]])
>>> a += b  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind' 

在操作不同类型的数组时,结果数组的类型对应于更一般或更精确的类型(这种行为称为向上转型)。

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0, pi, 3)
>>> b.dtype.name
'float64'
>>> c = a + b
>>> c
array([1\.        , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c * 1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
 -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128' 

许多一元操作,如计算数组中所有元素的总和,都实现为ndarray类的方法。

>>> a = rg.random((2, 3))
>>> a
array([[0.82770259, 0.40919914, 0.54959369],
 [0.02755911, 0.75351311, 0.53814331]])
>>> a.sum()
3.1057109529998157
>>> a.min()
0.027559113243068367
>>> a.max()
0.8277025938204418 

默认情况下,这些操作将应用于数组,就好像它是一个数字列表,而不考虑其形状。然而,通过指定axis参数,你可以沿着数组的指定轴应用操作:

>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)     # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)     # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)  # cumulative sum along each row
array([[ 0,  1,  3,  6],
 [ 4,  9, 15, 22],
 [ 8, 17, 27, 38]]) 

通用函数

NumPy 提供了熟悉的数学函数,如 sin、cos 和 exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数对数组进行逐元素操作,产生一个数组作为输出。

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1\.        , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0\.        , 1\.        , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([2., 0., 6.]) 

另见

all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, invert, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

索引、切片和迭代

一维数组可以像列表和其他 Python 序列一样进行索引、切片和迭代。

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> # equivalent to a[0:6:2] = 1000;
>>> # from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 1000
>>> a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>> a[::-1]  # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>> for i in a:
...     print(i**(1 / 3.))
...
9.999999999999998  # may vary
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998 

多维数组每个轴可以有一个索引。这些索引以逗号分隔的元组形式给出:

>>> def f(x, y):
...     return 10 * x + y
...
>>> b = np.fromfunction(f, (5, 4), dtype=int)
>>> b
array([[ 0,  1,  2,  3],
 [10, 11, 12, 13],
 [20, 21, 22, 23],
 [30, 31, 32, 33],
 [40, 41, 42, 43]])
>>> b[2, 3]
23
>>> b[0:5, 1]  # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[:, 1]    # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, :]  # each column in the second and third row of b
array([[10, 11, 12, 13],
 [20, 21, 22, 23]]) 

当提供的索引少于轴的数量时,缺少的索引被视为完整的切片:

>>> b[-1]   # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43]) 

b[i]方括号中的表达式被视为i,后面跟着需要表示剩余轴的的实例数。NumPy 还允许您使用点...来编写这个表达式,即b[i, ...]

(...) 表示需要产生一个完整索引元组所需的冒号数量。例如,如果x是一个具有 5 个轴的数组,则

  • x[1, 2, ...]等同于x[1, 2, :, :, :],

  • x[..., 3]转换为x[:, :, :, :, 3],以及

  • x[4, ..., 5, :]转换为x[4, :, :, 5, :]

>>> c = np.array([[[  0,  1,  2],  # a 3D array (two stacked 2D arrays)
...                [ 10, 12, 13]],
...               [[100, 101, 102],
...                [110, 112, 113]]])
>>> c.shape
(2, 2, 3)
>>> c[1, ...]  # same as c[1, :, :] or c[1]
array([[100, 101, 102],
 [110, 112, 113]])
>>> c[..., 2]  # same as c[:, :, 2]
array([[  2,  13],
 [102, 113]]) 

对多维数组的迭代是针对第一个轴完成的:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43] 

然而,如果想对数组中的每个元素执行操作,可以使用flat属性,它是数组所有元素的迭代器

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43 

另请参见

ndarrays 的索引, 索引例程 (参考), newaxis, ndenumerate, indices

一个例子

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
 [ 5,  6,  7,  8,  9],
 [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'> 

数组创建

有几种创建数组的方法。

例如,您可以使用array函数从常规 Python 列表或元组创建数组。结果数组的类型是从序列中元素的类型推断出来的。

>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64') 

一个常见错误是使用多个参数调用array,而不是提供一个序列作为参数。

>>> a = np.array(1, 2, 3, 4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1, 2, 3, 4])  # RIGHT 

array将序列转换为二维数组,序列的序列转换为三维数组,依此类推。

>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2\. , 3\. ],
 [4\. , 5\. , 6\. ]]) 

数组的类型也可以在创建时显式指定:

>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
 [3.+0.j, 4.+0.j]]) 

通常,数组的元素最初是未知的,但其大小是已知的。因此,NumPy 提供了几个函数来创建具有初始占位内容的数组。这减少了增长数组的必要性,这是一个昂贵的操作。

函数zeros创建一个全零数组,函数ones创建一个全一数组,函数empty创建一个其初始内容是随机的并且取决于内存状态的数组。默认情况下,创建的数组的 dtype 是float64,但可以通过关键字参数dtype进行指定。

>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
 [0., 0., 0., 0.],
 [0., 0., 0., 0.]])
>>> np.ones((2, 3, 4), dtype=np.int16)
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]]], dtype=int16)
>>> np.empty((2, 3)) 
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
 [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) 

为了创建数字序列,NumPy 提供了类似于 Python 内置的rangearange函数,但它返回一个数组。

>>> np.arange(10, 30, 5)
array([10, 15, 20, 25])
>>> np.arange(0, 2, 0.3)  # it accepts float arguments
array([0\. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) 

arange与浮点数参数一起使用时,通常无法预测获得的元素数量,这是由于有限浮点精度。因此,通常最好使用接收我们想要的元素数量作为参数而不是步长的函数linspace

>>> from numpy import pi
>>> np.linspace(0, 2, 9)                   # 9 numbers from 0 to 2
array([0\.  , 0.25, 0.5 , 0.75, 1\.  , 1.25, 1.5 , 1.75, 2\.  ])
>>> x = np.linspace(0, 2 * pi, 100)        # useful to evaluate function at lots of points
>>> f = np.sin(x) 

另请参阅

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.Generator.rand, numpy.random.Generator.randn, fromfunction, fromfile

打印数组

当打印数组时,NumPy 以类似嵌套列表的方式显示它,但布局如下:

  • 最后一个轴从左到右打印,

  • 倒数第二个从上到下打印,

  • 剩余部分也从上到下打印,每个切片与下一个切片之间由一个空行分隔。

一维数组被打印为行,二维数组被打印为矩阵,三维数组被打印为矩阵的列表。

>>> a = np.arange(6)                    # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4, 3)     # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2, 3, 4)  # 3d array
>>> print(c)
[[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

 [[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]] 

请参见下面以获取有关reshape的更多详细信息。

如果数组太大而无法打印,NumPy 会自动跳过数组的中间部分,仅打印角落:

>>> print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]] 

要禁用此行为并强制 NumPy 打印整个数组,可以使用set_printoptions更改打印选项。

>>> np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported 

基本操作

数组上的算术运算逐元素应用。创建一个新数组并用结果填充。

>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> c = a - b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a < 35
array([ True,  True, False, False]) 

与许多矩阵语言不同,在 NumPy 数组中,乘积运算符*是按元素操作的。可以使用@运算符(在 python >=3.5 中)或dot函数或方法执行矩阵乘积:

>>> A = np.array([[1, 1],
...               [0, 1]])
>>> B = np.array([[2, 0],
...               [3, 4]])
>>> A * B     # elementwise product
array([[2, 0],
 [0, 4]])
>>> A @ B     # matrix product
array([[5, 4],
 [3, 4]])
>>> A.dot(B)  # another matrix product
array([[5, 4],
 [3, 4]]) 

一些操作,比如+=*=,是就地修改现有数组而不是创建新数组。

>>> rg = np.random.default_rng(1)  # create instance of default random number generator
>>> a = np.ones((2, 3), dtype=int)
>>> b = rg.random((2, 3))
>>> a *= 3
>>> a
array([[3, 3, 3],
 [3, 3, 3]])
>>> b += a
>>> b
array([[3.51182162, 3.9504637 , 3.14415961],
 [3.94864945, 3.31183145, 3.42332645]])
>>> a += b  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind' 

在操作不同类型的数组时,结果数组的类型对应于更一般或更精确的类型(这种行为称为向上转型)。

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0, pi, 3)
>>> b.dtype.name
'float64'
>>> c = a + b
>>> c
array([1\.        , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c * 1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
 -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128' 

许多一元操作,比如计算数组中所有元素的和,都作为ndarray类的方法实现。

>>> a = rg.random((2, 3))
>>> a
array([[0.82770259, 0.40919914, 0.54959369],
 [0.02755911, 0.75351311, 0.53814331]])
>>> a.sum()
3.1057109529998157
>>> a.min()
0.027559113243068367
>>> a.max()
0.8277025938204418 

默认情况下,这些操作会将数组视为数字列表进行,而不考虑其形状。但是,通过指定axis参数,您可以沿着数组的指定轴应用操作:

>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)     # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)     # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)  # cumulative sum along each row
array([[ 0,  1,  3,  6],
 [ 4,  9, 15, 22],
 [ 8, 17, 27, 38]]) 

通用函数

NumPy 提供了一些熟悉的数学函数,如 sin、cos 和 exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数对数组进行逐元素操作,并产生一个数组作为输出。

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1\.        , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0\.        , 1\.        , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([2., 0., 6.]) 

另见

all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, invert, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

索引、切片和迭代

一维数组可以像列表和其他 Python 序列一样进行索引、切片和迭代。

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> # equivalent to a[0:6:2] = 1000;
>>> # from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 1000
>>> a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>> a[::-1]  # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>> for i in a:
...     print(i**(1 / 3.))
...
9.999999999999998  # may vary
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998 

多维数组可以有每个轴的一个索引。这些索引用逗号分隔的元组给出:

>>> def f(x, y):
...     return 10 * x + y
...
>>> b = np.fromfunction(f, (5, 4), dtype=int)
>>> b
array([[ 0,  1,  2,  3],
 [10, 11, 12, 13],
 [20, 21, 22, 23],
 [30, 31, 32, 33],
 [40, 41, 42, 43]])
>>> b[2, 3]
23
>>> b[0:5, 1]  # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[:, 1]    # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, :]  # each column in the second and third row of b
array([[10, 11, 12, 13],
 [20, 21, 22, 23]]) 

当提供的索引少于轴的数量时,缺少的索引被视为完整切片

>>> b[-1]   # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43]) 

表达式b[i]中的括号内被视为i后面跟着尽可能多的:来表示剩下的轴。NumPy 还允许您使用点号表示为b[i, ...]

省略号...)表示为产生一个完整索引元组所需的冒号。例如,如果x是一个具有 5 个轴的数组,则

  • x[1, 2, ...]等同于x[1, 2, :, :, :]

  • x[..., 3]x[:, :, :, :, 3]

  • x[4, ..., 5, :]x[4, :, :, 5, :]

>>> c = np.array([[[  0,  1,  2],  # a 3D array (two stacked 2D arrays)
...                [ 10, 12, 13]],
...               [[100, 101, 102],
...                [110, 112, 113]]])
>>> c.shape
(2, 2, 3)
>>> c[1, ...]  # same as c[1, :, :] or c[1]
array([[100, 101, 102],
 [110, 112, 113]])
>>> c[..., 2]  # same as c[:, :, 2]
array([[  2,  13],
 [102, 113]]) 

对多维数组的迭代是针对第一个轴进行的:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43] 

但是,如果想要对数组中的每个元素执行操作,可以使用flat属性,它是一个对数组的所有元素进行迭代的迭代器:

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43 

另见

ndarrays 上的索引,索引例程(参考文档),newaxisndenumerateindices

形状操作

改变数组的形状

数组的形状由每个轴上的元素数量确定:

>>> a = np.floor(10 * rg.random((3, 4)))
>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.shape
(3, 4) 

数组的形状可以使用各种命令来改变。请注意,以下三个命令都会返回一个修改后的数组,但不会改变原始数组:

>>> a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>> a.reshape(6, 2)  # returns the array with a modified shape
array([[3., 7.],
 [3., 4.],
 [1., 4.],
 [2., 2.],
 [7., 2.],
 [4., 9.]])
>>> a.T  # returns the array, transposed
array([[3., 1., 7.],
 [7., 4., 2.],
 [3., 2., 4.],
 [4., 2., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4) 

ravel操作中生成的数组的元素顺序通常是“C-style”,即右边的索引“变化最快”,所以下标为a[0, 0]之后的元素是a[0, 1]。如果数组被重新整形为其他形状,这个数组仍然被视为“C-style”。NumPy 通常创建存储在这个顺序中的数组,所以ravel通常不需要复制它的参数,但如果数组是通过对另一个数组切片或使用不寻常的选项创建的,则可能需要复制它。函数ravelreshape还可以使用可选参数指示使用 FORTRAN-style 数组,其中最左边的索引变化得最快。

reshape函数用修改后的形状返回其参数,而ndarray.resize方法修改数组本身:

>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.resize((2, 6))
>>> a
array([[3., 7., 3., 4., 1., 4.],
 [2., 2., 7., 2., 4., 9.]]) 

如果在重整形操作中给出的维度为-1,则其他维度会自动计算:

>>> a.reshape(3, -1)
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]]) 

另请参阅

ndarray.shape, reshape, resize, ravel

将不同数组堆叠在一起

可以沿不同轴将多个数组堆叠在一起:

>>> a = np.floor(10 * rg.random((2, 2)))
>>> a
array([[9., 7.],
 [5., 2.]])
>>> b = np.floor(10 * rg.random((2, 2)))
>>> b
array([[1., 9.],
 [5., 1.]])
>>> np.vstack((a, b))
array([[9., 7.],
 [5., 2.],
 [1., 9.],
 [5., 1.]])
>>> np.hstack((a, b))
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]]) 

函数column_stack 将 1D 数组堆叠为列到 2D 数组中。它与 hstack 仅适用于 2D 数组:

>>> from numpy import newaxis
>>> np.column_stack((a, b))  # with 2D arrays
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]])
>>> a = np.array([4., 2.])
>>> b = np.array([3., 8.])
>>> np.column_stack((a, b))  # returns a 2D array
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a, b))        # the result is different
array([4., 2., 3., 8.])
>>> a[:, newaxis]  # view `a` as a 2D column vector
array([[4.],
 [2.]])
>>> np.column_stack((a[:, newaxis], b[:, newaxis]))
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a[:, newaxis], b[:, newaxis]))  # the result is the same
array([[4., 3.],
 [2., 8.]]) 

另一方面,函数row_stack对于任何输入数组都等同于vstack。事实上,row_stackvstack的别名:

>>> np.column_stack is np.hstack
False
>>> np.row_stack is np.vstack
True 

一般而言,对于维数大于两的数组,hstack 沿第二轴堆叠,vstack 沿第一轴堆叠,而 concatenate 允许使用一个可选参数,指定应该发生连接的轴的编号。

注意

在复杂情况下,r_c_ 对于通过沿一个轴堆叠数字创建数组很有用。它们允许使用范围文本 :

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

当与数组一起使用时,r_c_ 在默认行为上类似于 vstackhstack,但允许使用可选参数指定沿其进行连接的轴的编号。

另请参阅

hstack, vstack, column_stack, concatenate, c_, r_

将一个数组分割成几个较小的数组

使用 hsplit,可以沿水平轴分割数组,可以指定要返回的等形状数组的数量,也可以在哪些列之后进行分割:

>>> a = np.floor(10 * rg.random((2, 12)))
>>> a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
 [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
>>> # Split `a` into 3
>>> np.hsplit(a, 3)
[array([[6., 7., 6., 9.],
 [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
 [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
 [1., 8., 1., 0.]])]
>>> # Split `a` after the third and the fourth column
>>> np.hsplit(a, (3, 4))
[array([[6., 7., 6.],
 [8., 5., 5.]]), array([[9.],
 [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
 [1., 8., 6., 7., 1., 8., 1., 0.]])] 

vsplit 沿垂直轴拆分,而 array_split 允许指定沿哪个轴拆分。

改变数组的形状

数组的形状由每个轴上的元素数量决定:

>>> a = np.floor(10 * rg.random((3, 4)))
>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.shape
(3, 4) 

使用各种命令可以更改数组的形状。请注意,以下三个命令都返回修改后的数组,但不会更改原始数组:

>>> a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>> a.reshape(6, 2)  # returns the array with a modified shape
array([[3., 7.],
 [3., 4.],
 [1., 4.],
 [2., 2.],
 [7., 2.],
 [4., 9.]])
>>> a.T  # returns the array, transposed
array([[3., 1., 7.],
 [7., 4., 2.],
 [3., 2., 4.],
 [4., 2., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4) 

ravel 产生的数组中元素的顺序通常是“C-style”,即右边的索引“变化最快”,所以 a[0, 0] 后面的元素是 a[0, 1]。如果将数组重新塑形为其他形状,那么数组将再次被视为“C-style”。NumPy 通常创建以此顺序存储的数组,因此 ravel 通常不需要复制其参数,但是如果数组由另一个数组的切片组成或通过使用不寻常的选项创建,则可能需要进行复制。函数 ravelreshape 还可以通过可选参数指示使用 FORTRAN-style 数组,其中左侧索引变化最快。

reshape 函数返回带有修改形状的参数,而 ndarray.resize 方法修改数组本身:

>>> a
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]])
>>> a.resize((2, 6))
>>> a
array([[3., 7., 3., 4., 1., 4.],
 [2., 2., 7., 2., 4., 9.]]) 

如果在重塑操作中一个维度给定为 -1,则其他维度将自动计算:

>>> a.reshape(3, -1)
array([[3., 7., 3., 4.],
 [1., 4., 2., 2.],
 [7., 2., 4., 9.]]) 

另请参阅

ndarray.shape, reshape, resize, ravel

组合不同的数组

可以沿不同轴堆叠几个数组:

>>> a = np.floor(10 * rg.random((2, 2)))
>>> a
array([[9., 7.],
 [5., 2.]])
>>> b = np.floor(10 * rg.random((2, 2)))
>>> b
array([[1., 9.],
 [5., 1.]])
>>> np.vstack((a, b))
array([[9., 7.],
 [5., 2.],
 [1., 9.],
 [5., 1.]])
>>> np.hstack((a, b))
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]]) 

函数 column_stack 将 1D 数组依次堆叠为 2D 数组的列。它与 hstack 对于 2D 数组是等效的:

>>> from numpy import newaxis
>>> np.column_stack((a, b))  # with 2D arrays
array([[9., 7., 1., 9.],
 [5., 2., 5., 1.]])
>>> a = np.array([4., 2.])
>>> b = np.array([3., 8.])
>>> np.column_stack((a, b))  # returns a 2D array
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a, b))        # the result is different
array([4., 2., 3., 8.])
>>> a[:, newaxis]  # view `a` as a 2D column vector
array([[4.],
 [2.]])
>>> np.column_stack((a[:, newaxis], b[:, newaxis]))
array([[4., 3.],
 [2., 8.]])
>>> np.hstack((a[:, newaxis], b[:, newaxis]))  # the result is the same
array([[4., 3.],
 [2., 8.]]) 

另一方面,row_stack函数对任意输入数组等效于vstack。事实上,row_stackvstack的别名:

>>> np.column_stack is np.hstack
False
>>> np.row_stack is np.vstack
True 

一般来说,对于超过两个维度的数组,hstack沿第二轴堆叠,vstack沿第一轴堆叠,而concatenate允许可选参数指定沿哪个轴进行连接。

注意

在复杂情况下,r_c_用于通过沿一个轴堆叠数字来创建数组。它们允许使用区间字面值:

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

当作为数组参数使用时,r_c_与默认的行为下的vstackhstack相似,但允许可选参数指定沿哪个轴连接。

另请参阅

hstackvstackcolumn_stackconcatenatec_r_

将一个数组分割为几个较小的数组

使用hsplit,您可以沿水平轴拆分数组,要么指定返回的等尺寸数组的数量,要么指定分割应该发生的列:

>>> a = np.floor(10 * rg.random((2, 12)))
>>> a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
 [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
>>> # Split `a` into 3
>>> np.hsplit(a, 3)
[array([[6., 7., 6., 9.],
 [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
 [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
 [1., 8., 1., 0.]])]
>>> # Split `a` after the third and the fourth column
>>> np.hsplit(a, (3, 4))
[array([[6., 7., 6.],
 [8., 5., 5.]]), array([[9.],
 [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
 [1., 8., 6., 7., 1., 8., 1., 0.]])] 

vsplit沿垂直轴分割,而array_split允许指定沿哪个轴进行分割。

复制和视图

在操作和操作数组时,它们的数据有时会复制到新数组中,有时不会。这通常是初学者困惑的原因。有三种情况:

没有任何复制

简单赋值不会复制对象或其数据。

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True 

Python 将可变对象作为引用传递,因此函数调用不会复制。

>>> def f(x):
...     print(id(x))
...
>>> id(a)  # id is a unique identifier of an object 
148293216  # may vary
>>> f(a)   
148293216  # may vary 

视图或浅复制

不同的数组对象可以共享相同的数据。view方法创建一个新的数组对象,它查看相同的数据。

>>> c = a.view()
>>> c is a
False
>>> c.base is a            # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))  # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234         # a's data changes
>>> a
array([[   0,    1,    2,    3],
 [1234,    5,    6,    7],
 [   8,    9,   10,   11]]) 

对数组进行切片会返回一个视图:

>>> s = a[:, 1:3]
>>> s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

深复制

copy方法会完全复制数组及其数据。

>>> d = a.copy()  # a new array object with new data is created
>>> d is a
False
>>> d.base is a  # d doesn't share anything with a
False
>>> d[0, 0] = 9999
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

有时在切片之后,原始数组不再需要时,应该调用copy。例如,假设a是一个很大的中间结果,而最终结果b只包含a的一小部分,那么在用切片构造b时应该进行深复制:

>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a  # the memory of ``a`` can be released. 

如果使用b = a[:100]ab引用,并且即使执行del aa也会在内存中保留。

函数和方法概述

以下是一些有用的 NumPy 函数和方法名称,按类别排序。请参见 Routines 获取完整列表。

数组创建

arangearraycopyemptyempty_likeeyefromfilefromfunctionidentitylinspacelogspacemgridogridonesones_liker_zeroszeros_like

转换

ndarray.astypeatleast_1datleast_2datleast_3dmat

操作

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

问题

all, any, nonzero, where

排序

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作

choosecompresscumprodcumsuminnerndarray.fillimagprodputputmaskrealsum

基本统计学

covmeanstdvar

基本线性代数

crossdotouterlinalg.svdvdot

完全不复制

简单赋值不会复制对象或其数据。

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True 

Python 将可变对象传递为引用,所以函数调用不会复制。

>>> def f(x):
...     print(id(x))
...
>>> id(a)  # id is a unique identifier of an object 
148293216  # may vary
>>> f(a)   
148293216  # may vary 

查看或浅复制

不同的数组对象可以共享相同的数据。view方法创建一个查看相同数据的新数组对象。

>>> c = a.view()
>>> c is a
False
>>> c.base is a            # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))  # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234         # a's data changes
>>> a
array([[   0,    1,    2,    3],
 [1234,    5,    6,    7],
 [   8,    9,   10,   11]]) 

对数组进行切片会返回一个视图:

>>> s = a[:, 1:3]
>>> s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

深度复制

copy方法完全复制数组及其数据。

>>> d = a.copy()  # a new array object with new data is created
>>> d is a
False
>>> d.base is a  # d doesn't share anything with a
False
>>> d[0, 0] = 9999
>>> a
array([[   0,   10,   10,    3],
 [1234,   10,   10,    7],
 [   8,   10,   10,   11]]) 

如果原始数组不再需要,有时应在切片后调用copy。例如,假设a是一个巨大的中间结果,最终结果b只包含a的一小部分,那么在使用切片构建b时应进行深拷贝:

>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a  # the memory of ``a`` can be released. 

如果使用b = a[:100]ab引用并且即使执行del a,它也将在内存中持久存在。

函数和方法概览

这是一些有用的 NumPy 函数和方法名称按类别排序的列表。完整的列表请参见 Routines。

创建数组

arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r_, zeros, zeros_like

转换

ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

操作

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

问题

all, any, nonzero, where

排序

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作

choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

基本统计

cov, mean, std, var

基本线性代数

cross, dot, outer, linalg.svd, vdot

较不基本

广播规则

广播使得通用函数可以对不完全相同形状的输入进行有意义的处理。

广播的第一个规则是,如果所有的输入数组的维度数不相同,则“1”将被重复添加到较小数组的形状之前,直到所有数组具有相同的维度数。

第二个广播规则确保在特定维度上大小为 1 的数组,表现得就像它们在该维度上的最大形状的数组一样。假设“广播”数组在该维度上的数组元素值是相同的。

应用广播规则后,所有数组的大小必须匹配。更多细节请参见广播。 ### 广播规则

广播使得通用函数可以对不完全相同形状的输入进行有意义的处理。

广播的第一个规则是,如果所有的输入数组的维度数不相同,则“1”将被重复添加到较小数组的形状之前,直到所有数组具有相同的维度数。

广播的第二规则确保在特定维度上大小为 1 的数组会像在该维度上具有最大形状的数组一样起作用。假定在广播数组中,数组元素的值沿该维度是相同的。

应用广播规则后,所有数组的大小必须匹配。更多细节可以在 广播 中找到。

高级索引和索引技巧

NumPy 提供的索引功能比常规 Python 序列更多。除了之前看到的通过整数和切片进行索引外,数组还可以通过整数数组和布尔数组进行索引。

使用索引数组进行索引

>>> a = np.arange(12)**2  # the first 12 square numbers
>>> i = np.array([1, 1, 3, 8, 5])  # an array of indices
>>> a[i]  # the elements of `a` at the positions `i`
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array([[3, 4], [9, 7]])  # a bidimensional array of indices
>>> a[j]  # the same shape as `j`
array([[ 9, 16],
 [81, 49]]) 

当索引数组 a 是多维的时,单个索引数组指的是 a 的第一个维度。以下示例通过使用调色板将标签图像转换为彩色图像来展示这种行为。

>>> palette = np.array([[0, 0, 0],         # black
...                     [255, 0, 0],       # red
...                     [0, 255, 0],       # green
...                     [0, 0, 255],       # blue
...                     [255, 255, 255]])  # white
>>> image = np.array([[0, 1, 2, 0],  # each value corresponds to a color in the palette
...                   [0, 3, 4, 0]])
>>> palette[image]  # the (2, 4, 3) color image
array([[[  0,   0,   0],
 [255,   0,   0],
 [  0, 255,   0],
 [  0,   0,   0]],

 [[  0,   0,   0],
 [  0,   0, 255],
 [255, 255, 255],
 [  0,   0,   0]]]) 

我们还可以为多个维度提供索引。每个维度的索引数组必须具有相同的形状。

>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>> i = np.array([[0, 1],  # indices for the first dim of `a`
...               [1, 2]])
>>> j = np.array([[2, 1],  # indices for the second dim
...               [3, 3]])
>>>
>>> a[i, j]  # i and j must have equal shape
array([[ 2,  5],
 [ 7, 11]])
>>>
>>> a[i, 2]
array([[ 2,  6],
 [ 6, 10]])
>>>
>>> a[:, j]
array([[[ 2,  1],
 [ 3,  3]],

 [[ 6,  5],
 [ 7,  7]],

 [[10,  9],
 [11, 11]]]) 

在 Python 中,arr[i, j]arr[(i, j)] 完全相同—所以我们可以将 ij 放入一个 tuple 中,然后使用那个进行索引。

>>> l = (i, j)
>>> # equivalent to a[i, j]
>>> a[l]
array([[ 2,  5],
 [ 7, 11]]) 

但是,我们不能将 ij 放入数组中,因为这个数组会被解释为对 a 的第一个维度进行索引。

>>> s = np.array([i, j])
>>> # not what we want
>>> a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3
>>> # same as `a[i, j]`
>>> a[tuple(s)]
array([[ 2,  5],
 [ 7, 11]]) 

使用数组进行索引的另一个常见用途是搜索时序系列的最大值:

>>> time = np.linspace(20, 145, 5)  # time scale
>>> data = np.sin(np.arange(20)).reshape(5, 4)  # 4 time-dependent series
>>> time
array([ 20\.  ,  51.25,  82.5 , 113.75, 145\.  ])
>>> data
array([[ 0\.        ,  0.84147098,  0.90929743,  0.14112001],
 [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
 [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
 [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
 [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
>>> # index of the maxima for each series
>>> ind = data.argmax(axis=0)
>>> ind
array([2, 0, 3, 1])
>>> # times corresponding to the maxima
>>> time_max = time[ind]
>>>
>>> data_max = data[ind, range(data.shape[1])]  # => data[ind[0], 0], data[ind[1], 1]...
>>> time_max
array([ 82.5 ,  20\.  , 113.75,  51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>> np.all(data_max == data.max(axis=0))
True 

您还可以使用数组索引作为目标进行赋值:

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

然而,当索引列表包含重复时,赋值会多次进行,留下最后一个值:

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

这是合理的,但是如果你想使用 Python 的 += 构造,请注意,它可能不会产生你期望的结果:

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

即使列表中 0 出现两次,第 0 个元素也只增加了一次。这是因为 Python 要求 a += 1 等价于 a = a + 1

使用布尔数组进行索引

当我们用(整数)索引数组索引数组时,我们提供了要选择的索引列表。布尔索引的方法不同;我们明确选择要选择哪些数组项和哪些不选择。

布尔索引最自然的用法之一是使用与原始数组相同形状的布尔数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b = a > 4
>>> b  # `b` is a boolean with `a`'s shape
array([[False, False, False, False],
 [False,  True,  True,  True],
 [ True,  True,  True,  True]])
>>> a[b]  # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11]) 

这个属性在赋值中非常有用:

>>> a[b] = 0  # All elements of `a` higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
 [4, 0, 0, 0],
 [0, 0, 0, 0]]) 

你可以查看以下示例,了解如何使用布尔索引来生成 Mandelbrot 集合 的图像:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot(h, w, maxit=20, r=2):
...  """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     x = np.linspace(-2.5, 1.5, 4*h+1)
...     y = np.linspace(-1.5, 1.5, 3*w+1)
...     A, B = np.meshgrid(x, y)
...     C = A + B*1j
...     z = np.zeros_like(C)
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + C
...         diverge = abs(z) > r                    # who is diverging
...         div_now = diverge & (divtime == maxit)  # who is diverging now
...         divtime[div_now] = i                    # note when
...         z[diverge] = r                          # avoid diverging too much
...
...     return divtime
>>> plt.clf()
>>> plt.imshow(mandelbrot(400, 400)) 

../_images/quickstart-1.png

使用布尔的第二种索引方式更类似于整数索引;对于数组的每个维度,我们给出一个 1D 布尔数组,选择我们想要的切片:

>>> a = np.arange(12).reshape(3, 4)
>>> b1 = np.array([False, True, True])         # first dim selection
>>> b2 = np.array([True, False, True, False])  # second dim selection
>>>
>>> a[b1, :]                                   # selecting rows
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                      # same thing
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[:, b2]                                   # selecting columns
array([[ 0,  2],
 [ 4,  6],
 [ 8, 10]])
>>>
>>> a[b1, b2]                                  # a weird thing to do
array([ 4, 10]) 

注意,1D 布尔数组的长度必须与你想要切片的维度(或轴)的长度相一致。在前面的例子中,b1 的长度为 3(a 的数量),而b2(长度为 4)适合索引a的第二个轴(列)。

ix_() 函数

ix_ 函数可以用于组合不同的向量,以便获得每个 n-uplet 的结果。例如,如果你想要计算从向量 a、b 和 c 中取出的所有三元组的 a+b*c:

>>> a = np.array([2, 3, 4, 5])
>>> b = np.array([8, 5, 4])
>>> c = np.array([5, 4, 6, 8, 3])
>>> ax, bx, cx = np.ix_(a, b, c)
>>> ax
array([[[2]],

 [[3]],

 [[4]],

 [[5]]])
>>> bx
array([[[8],
 [5],
 [4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax + bx * cx
>>> result
array([[[42, 34, 50, 66, 26],
 [27, 22, 32, 42, 17],
 [22, 18, 26, 34, 14]],

 [[43, 35, 51, 67, 27],
 [28, 23, 33, 43, 18],
 [23, 19, 27, 35, 15]],

 [[44, 36, 52, 68, 28],
 [29, 24, 34, 44, 19],
 [24, 20, 28, 36, 16]],

 [[45, 37, 53, 69, 29],
 [30, 25, 35, 45, 20],
 [25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17 

你也可以这样实现 reduce:

>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r, v)
...    return r 

然后将其用作:

>>> ufunc_reduce(np.add, a, b, c)
array([[[15, 14, 16, 18, 13],
 [12, 11, 13, 15, 10],
 [11, 10, 12, 14,  9]],

 [[16, 15, 17, 19, 14],
 [13, 12, 14, 16, 11],
 [12, 11, 13, 15, 10]],

 [[17, 16, 18, 20, 15],
 [14, 13, 15, 17, 12],
 [13, 12, 14, 16, 11]],

 [[18, 17, 19, 21, 16],
 [15, 14, 16, 18, 13],
 [14, 13, 15, 17, 12]]]) 

与普通的 ufunc.reduce 版本相比,这个版本的 reduce 的优势在于它利用了 广播规则 来避免创建大小为输出大小乘以向量数的参数数组。

使用字符串进行索引

参见 结构化数组。

使用索引数组进行索引

>>> a = np.arange(12)**2  # the first 12 square numbers
>>> i = np.array([1, 1, 3, 8, 5])  # an array of indices
>>> a[i]  # the elements of `a` at the positions `i`
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array([[3, 4], [9, 7]])  # a bidimensional array of indices
>>> a[j]  # the same shape as `j`
array([[ 9, 16],
 [81, 49]]) 

当被索引的数组 a 是多维的时,单个索引数组指的是 a 的第一个维度。下面的例子通过使用调色板将标签图像转换为彩色图像来展示这种行为。

>>> palette = np.array([[0, 0, 0],         # black
...                     [255, 0, 0],       # red
...                     [0, 255, 0],       # green
...                     [0, 0, 255],       # blue
...                     [255, 255, 255]])  # white
>>> image = np.array([[0, 1, 2, 0],  # each value corresponds to a color in the palette
...                   [0, 3, 4, 0]])
>>> palette[image]  # the (2, 4, 3) color image
array([[[  0,   0,   0],
 [255,   0,   0],
 [  0, 255,   0],
 [  0,   0,   0]],

 [[  0,   0,   0],
 [  0,   0, 255],
 [255, 255, 255],
 [  0,   0,   0]]]) 

我们还可以为多个维度提供索引。每个维度的索引数组必须具有相同的形状。

>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>> i = np.array([[0, 1],  # indices for the first dim of `a`
...               [1, 2]])
>>> j = np.array([[2, 1],  # indices for the second dim
...               [3, 3]])
>>>
>>> a[i, j]  # i and j must have equal shape
array([[ 2,  5],
 [ 7, 11]])
>>>
>>> a[i, 2]
array([[ 2,  6],
 [ 6, 10]])
>>>
>>> a[:, j]
array([[[ 2,  1],
 [ 3,  3]],

 [[ 6,  5],
 [ 7,  7]],

 [[10,  9],
 [11, 11]]]) 

在 Python 中,arr[i, j]arr[(i, j)]完全相同—所以我们可以将ij放入一个tuple中,然后用它进行索引。

>>> l = (i, j)
>>> # equivalent to a[i, j]
>>> a[l]
array([[ 2,  5],
 [ 7, 11]]) 

然而,我们不能将ij放入一个数组中,因为这个数组将被解释为索引a的第一个维度。

>>> s = np.array([i, j])
>>> # not what we want
>>> a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3
>>> # same as `a[i, j]`
>>> a[tuple(s)]
array([[ 2,  5],
 [ 7, 11]]) 

数组索引的另一个常见用途是搜索时间依赖系列的最大值:

>>> time = np.linspace(20, 145, 5)  # time scale
>>> data = np.sin(np.arange(20)).reshape(5, 4)  # 4 time-dependent series
>>> time
array([ 20\.  ,  51.25,  82.5 , 113.75, 145\.  ])
>>> data
array([[ 0\.        ,  0.84147098,  0.90929743,  0.14112001],
 [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
 [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
 [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
 [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
>>> # index of the maxima for each series
>>> ind = data.argmax(axis=0)
>>> ind
array([2, 0, 3, 1])
>>> # times corresponding to the maxima
>>> time_max = time[ind]
>>>
>>> data_max = data[ind, range(data.shape[1])]  # => data[ind[0], 0], data[ind[1], 1]...
>>> time_max
array([ 82.5 ,  20\.  , 113.75,  51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>> np.all(data_max == data.max(axis=0))
True 

你也可以使用数组索引作为赋值的目标:

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

然而,当索引列表包含重复时,赋值会进行多次,留下最后一个值:

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

这是足够合理的,但是如果你想要使用 Python 的 += 结构,请注意,因为它可能不会产生你期望的结果:

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

尽管在索引列表中 0 出现了两次,但第 0 个元素只增加了一次。这是因为 Python 要求 a += 1 等价于 a = a + 1

使用布尔数组进行索引

当我们用 (整数) 索引数组索引数组时,我们提供了要选择的索引列表。对于布尔索引,方法是不同的;我们明确地选择我们想要的数组项和我们不想要的数组项。

布尔索引最自然的想法是使用与原始数组形状相同的布尔数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b = a > 4
>>> b  # `b` is a boolean with `a`'s shape
array([[False, False, False, False],
 [False,  True,  True,  True],
 [ True,  True,  True,  True]])
>>> a[b]  # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11]) 

这个属性在赋值时非常有用:

>>> a[b] = 0  # All elements of `a` higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
 [4, 0, 0, 0],
 [0, 0, 0, 0]]) 

你可以看下面的例子,看看如何使用布尔索引生成 曼德布洛特集 的图像:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot(h, w, maxit=20, r=2):
...  """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     x = np.linspace(-2.5, 1.5, 4*h+1)
...     y = np.linspace(-1.5, 1.5, 3*w+1)
...     A, B = np.meshgrid(x, y)
...     C = A + B*1j
...     z = np.zeros_like(C)
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + C
...         diverge = abs(z) > r                    # who is diverging
...         div_now = diverge & (divtime == maxit)  # who is diverging now
...         divtime[div_now] = i                    # note when
...         z[diverge] = r                          # avoid diverging too much
...
...     return divtime
>>> plt.clf()
>>> plt.imshow(mandelbrot(400, 400)) 

../_images/quickstart-1.png

使用布尔值进行索引的第二种方式更类似于整数索引;对于数组的每个维度,我们提供一个选择我们想要的切片的 1D 布尔数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b1 = np.array([False, True, True])         # first dim selection
>>> b2 = np.array([True, False, True, False])  # second dim selection
>>>
>>> a[b1, :]                                   # selecting rows
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                      # same thing
array([[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>>
>>> a[:, b2]                                   # selecting columns
array([[ 0,  2],
 [ 4,  6],
 [ 8, 10]])
>>>
>>> a[b1, b2]                                  # a weird thing to do
array([ 4, 10]) 

请注意,1D 布尔数组的长度必须与您要切片的维度(或轴)的长度相符。在前面的示例中,b1 的长度为 3(a 中的数),而长度为 4 的 b2 适合索引 a 的第二轴(列)。

ix_() 函数

ix_ 函数可用于组合不同向量,以便为每个 n 元组获取结果。例如,如果您想要计算所有来自向量 a、b 和 c 的三元组的 a+b*c:

>>> a = np.array([2, 3, 4, 5])
>>> b = np.array([8, 5, 4])
>>> c = np.array([5, 4, 6, 8, 3])
>>> ax, bx, cx = np.ix_(a, b, c)
>>> ax
array([[[2]],

 [[3]],

 [[4]],

 [[5]]])
>>> bx
array([[[8],
 [5],
 [4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax + bx * cx
>>> result
array([[[42, 34, 50, 66, 26],
 [27, 22, 32, 42, 17],
 [22, 18, 26, 34, 14]],

 [[43, 35, 51, 67, 27],
 [28, 23, 33, 43, 18],
 [23, 19, 27, 35, 15]],

 [[44, 36, 52, 68, 28],
 [29, 24, 34, 44, 19],
 [24, 20, 28, 36, 16]],

 [[45, 37, 53, 69, 29],
 [30, 25, 35, 45, 20],
 [25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17 

您还可以这样实现 reduce:

>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r, v)
...    return r 

然后可如下使用它:

>>> ufunc_reduce(np.add, a, b, c)
array([[[15, 14, 16, 18, 13],
 [12, 11, 13, 15, 10],
 [11, 10, 12, 14,  9]],

 [[16, 15, 17, 19, 14],
 [13, 12, 14, 16, 11],
 [12, 11, 13, 15, 10]],

 [[17, 16, 18, 20, 15],
 [14, 13, 15, 17, 12],
 [13, 12, 14, 16, 11]],

 [[18, 17, 19, 21, 16],
 [15, 14, 16, 18, 13],
 [14, 13, 15, 17, 12]]]) 

与普通 ufunc.reduce 的这个版本相比的优势是,它利用了广播规则 以避免创建大小为输出大小乘以向量数量的参数数组。

使用字符串进行索引

参见结构化数组。

技巧和提示

这里我们提供一些简短而实用的技巧清单。

“自动”重新整形

要更改数组的尺寸,您可以省略其中一个尺寸,该尺寸将自动推导出来:

>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3))  # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
array([[[ 0,  1,  2],
 [ 3,  4,  5],
 [ 6,  7,  8],
 [ 9, 10, 11],
 [12, 13, 14]],

 [[15, 16, 17],
 [18, 19, 20],
 [21, 22, 23],
 [24, 25, 26],
 [27, 28, 29]]]) 

向量叠加

如何从等尺寸行向量列表构建二维数组?在 MATLAB 中,这很容易:如果 xy 是两个相同长度的向量,则只需进行 m=[x;y]。在 NumPy 中,这可以通过函数 column_stackdstackhstackvstack 来实现,具体取决于要执行叠加的维度。例如:

>>> x = np.arange(0, 10, 2)
>>> y = np.arange(5)
>>> m = np.vstack([x, y])
>>> m
array([[0, 2, 4, 6, 8],
 [0, 1, 2, 3, 4]])
>>> xy = np.hstack([x, y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4]) 

那些超过两维的函数背后的逻辑可能会有些奇怪。

请参阅

面向 MATLAB 用户的 NumPy

直方图

应用于数组的 NumPy histogram 函数返回一个向量对:数组的直方图和一个箱边向量。注意:matplotlib 也有一个构建直方图的函数(称为 hist,类似于 Matlab 中的),与 NumPy 中的函数不同。主要区别在于,pylab.hist 会自动绘制直方图,而 numpy.histogram 只生成数据。

>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5² and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu, sigma, 10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=True)       # matplotlib version (plot)
(array...)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True)  # NumPy version (no plot)
>>> plt.plot(.5 * (bins[1:] + bins[:-1]), n) 

../_images/quickstart-2.png

对于 Matplotlib >=3.4,您也可以使用 plt.stairs(n, bins)

“自动”重新整形

要更改数组的尺寸,您可以省略其中一个尺寸,该尺寸将自动推导出来:

>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3))  # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
array([[[ 0,  1,  2],
 [ 3,  4,  5],
 [ 6,  7,  8],
 [ 9, 10, 11],
 [12, 13, 14]],

 [[15, 16, 17],
 [18, 19, 20],
 [21, 22, 23],
 [24, 25, 26],
 [27, 28, 29]]]) 

向量叠加

如何从等尺寸行向量列表构建二维数组?在 MATLAB 中,这很容易:如果 xy 是两个相同长度的向量,则只需进行 m=[x;y]。在 NumPy 中,这可以通过函数 column_stackdstackhstackvstack 来实现,具体取决于要执行叠加的维度。例如:

>>> x = np.arange(0, 10, 2)
>>> y = np.arange(5)
>>> m = np.vstack([x, y])
>>> m
array([[0, 2, 4, 6, 8],
 [0, 1, 2, 3, 4]])
>>> xy = np.hstack([x, y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4]) 

针对超过两维的函数背后的逻辑可能会有些奇怪。

请参阅

面向 MATLAB 用户的 NumPy

直方图

应用于数组的 NumPy histogram 函数返回一对向量:数组的直方图和一个边界向量。注意:matplotlib 也有一个构建直方图的函数(称为 hist,就像 Matlab 中的一样),它与 NumPy 中的不同。主要区别在于 pylab.hist 会自动绘制直方图,而 numpy.histogram 只会生成数据。

>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5² and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu, sigma, 10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=True)       # matplotlib version (plot)
(array...)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True)  # NumPy version (no plot)
>>> plt.plot(.5 * (bins[1:] + bins[:-1]), n) 

../_images/quickstart-2.png

在 Matplotlib >=3.4 中,你也可以使用 plt.stairs(n, bins)

进一步阅读

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

原文:numpy.org/doc/1.26/user/absolute_beginners.html

欢迎来到 NumPy 的绝对初学者指南!如果你有评论或建议,请不要犹豫联系我们

欢迎来到 NumPy!

NumPy(Numerical Python)是一个开源的 Python 库,几乎在每个科学和工程领域中都被使用。它是 Python 中处理数值数据的通用标准,在科学 Python 和 PyData 生态系统的核心地位不可撼动。NumPy 的用户包括从初学者程序员到经验丰富的从事最前沿的科学和工业研究与开发的研究人员。NumPy API 在 Pandas、SciPy、Matplotlib、scikit-learn、scikit-image 和大多数其他数据科学和科学 Python 软件包中得到广泛应用。

NumPy 库包含多维数组和矩阵数据结构(你会在后面的章节中找到更多关于这个的信息)。它提供ndarray,一个同构的 n 维数组对象,并提供了一些有效操作的方法。NumPy 可以用来对数组执行各种数学运算。它为 Python 提供了强大的数据结构,保证了对数组和矩阵的高效计算,并提供了一个庞大的高级数学函数库,可用于这些数组和矩阵的操作。

了解更多关于 NumPy 的信息!

安装 NumPy

为了安装 NumPy,我们强烈建议使用科学 Python 发行版。如果你正在寻找在你的操作系统上安装 NumPy 的完整说明,请参阅安装 NumPy

如果你已经安装了 Python,可以使用以下命令安装 NumPy:

conda install numpy 

或者

pip install numpy 

如果你还没有安装 Python,你可能想考虑使用Anaconda。这是最简单的入门方式。获取这个发行版的好处是你不需要太担心单独安装 NumPy 或者你将用于数据分析的其他主要软件包,比如 pandas, Scikit-Learn 等等。

如何导入 NumPy

要访问 NumPy 及其函数,请在你的 Python 代码中这样导入:

import numpy as np 

我们将导入名称缩短为np,以提高使用 NumPy 的代码的可读性。这是一个被广泛采用的惯例,可以使你的代码对每个人在上面工作时更容易阅读。我们建议始终使用import numpy as np导入。

阅读示例代码

如果你还不习惯阅读包含大量代码的教程,你可能不知道如何解释如下的代码块:

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

如果您不熟悉这种风格,那么这很容易理解。如果您看到>>>,那么这是 输入,或者您要输入的代码。没有 >>> 的一切都是 输出,或者您代码运行的结果。这是在命令行上运行python时看到的风格,但如果您使用 IPython,可能会看到不同的风格。请注意,它不是代码的一部分,如果键入或粘贴到 Python shell 中会导致错误。可以安全地键入或粘贴到 IPython shell; >>> 会被忽略。

Python 列表和 NumPy 数组之间有什么区别?

NumPy 为您提供了大量快速有效的方式来创建数组并在其中操作数值数据。虽然 Python 列表可以在单个列表内包含不同的数据类型,但 NumPy 数组中的所有元素应该是同类元素。如果数组不同类的话,那么这些数组上执行的数学运算将非常低效。

为什么要使用 NumPy?

NumPy 数组比 Python 列表更快、更紧凑。数组占用更少的内存,使用起来更加方便。NumPy 使用更少的内存存储数据,并提供了一种指定数据类型的机制。这允许进一步优化代码。

什么是数组?

数组是 NumPy 库的核心数据结构。数组是一组值的网格,它包含关于原始数据、如何定位元素以及如何解释元素的信息。它有一组可以用各种方式进行索引的元素。这些元素都是相同类型的,称为数组的 dtype

数组可以通过非负整数的元组、布尔值、另一个数组或整数进行索引。数组的rank是维度的数量。数组的shape是包含沿每个维度的数组大小的整数元组。

我们初始化 NumPy 数组的一种方法是使用 Python 列表,对于二维或更高维数据,使用嵌套列表。

例如:

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

或者:

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

我们可以使用方括号访问数组中的元素。访问元素时,请记住 NumPy 中的索引从 0 开始。这意味着如果您要访问数组中的第一个元素,您将访问元素“0”。

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

关于数组的更多信息

本节涵盖 1D 数组2D 数组ndarray向量矩阵


你可能偶尔会听到将数组称为ndarray,这是“N 维数组”的缩写。一个 N 维数组就是一个具有任意数量维度的数组。您还可能听到1-D,或一维数组,2-D,或二维数组,等等。NumPy 的 ndarray 类用于表示矩阵和向量。向量是一个具有单一维度的数组(行向量和列向量之间没有区别),而矩阵指的是具有两个维度的数组。对于3-D或更高维度的数组,术语张量也经常使用。

数组的属性是什么?

数组通常是相同类型和大小的项目的固定大小容器。数组的维度和项目的数量由其形状定义。数组的形状是一组非负整数的元组,指定了每个维度的大小。

在 NumPy 中,维度被称为。这意味着如果你有一个看起来像这样的 2D 数组:

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

您的数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。

就像在其他 Python 容器对象中一样,可以通过对数组进行索引或切片来访问和修改数组的内容。与典型的容器对象不同,不同的数组可以共享相同的数据,因此对一个数组的更改可能会在另一个数组中可见。

数组的属性反映了数组本身的内在信息。如果需要获取甚至设置数组的属性而不创建新数组,通常可以通过其属性访问数组。

在这里阅读更多关于数组属性的信息 并了解这里的数组对象。

如何创建一个基本数组

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


要创建一个 NumPy 数组,可以使用函数np.array()

要创建一个简单的数组,您只需向其传递一个列表。如果愿意,还可以指定列表中的数据类型。您可以在这里找到有关数据类型的更多信息。

>>> import numpy as np
>>> a = np.array([1, 2, 3]) 

您可以通过这种方式将数组可视化:

../_images/np_array.png

请注意,这些可视化旨在简化概念并为您提供对 NumPy 概念和机制的基本理解。数组和数组操作比这里呈现的要复杂得多!

除了从一系列元素创建数组之外,您还可以轻松地创建一个填充有0的数组:

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

或者一个由1填充的数组:

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

或者甚至一个空数组!函数empty创建一个数组,其初始内容是随机的,并取决于内存的状态。使用empty而不是zeros(或类似物)的原因是速度—只需确保稍后填充每个元素!

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

您可以创建一个具有元素范围的数组:

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

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

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

您也可以使用np.linspace()在指定的间隔中创建一系列线性间隔的值的数组:

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

指定您的数据类型

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

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

在这里了解更多关于创建数组的信息

添加、删除和排序元素

这个部分涵盖 np.sort()np.concatenate()


使用np.sort()简单地对元素进行排序。在调用函数时,您可以指定轴、种类和顺序。

如果你从这个数组开始:

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

您可以快速将数字按升序排序:

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

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

  • argsort,是沿着指定轴的间接排序,

  • lexsort,是多个键的间接稳定排序,

  • searchsorted,用于在排序的数组中查找元素,以及

  • partition 是一个部分排序。

要了解更多关于数组排序的内容,请参阅:sort

如果你从这些数组开始:

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

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

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

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

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

你可以用以下方法将它们连接起来:

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

要从数组中删除元素,可以简单地使用索引选择要保留的元素。

要了解更多关于连接的信息,请参阅:concatenate

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

这个部分涵盖 ndarray.ndimndarray.sizendarray.shape


ndarray.ndim会告诉您数组的轴数,或者维度数。

ndarray.size会告诉您数组中元素的总数。这是数组形状各元素的乘积

ndarray.shape将显示一个整数元组,表示数组沿每个维度存储的元素数。例如,如果您有一个有 2 行 3 列的二维数组,则数组形状是(2, 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, newshape=(1, 6), order='C')
array([[0, 1, 2, 3, 4, 5]]) 

a 是待重塑的数组。

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

order: C 表示使用类 C 索引顺序读取/写入元素,F 表示使用类 Fortran 索引顺序读取/写入元素,A 表示如果 a 在内存中是 Fortran 连续的,则使用类 Fortran 索引顺序读取/写入元素,否则使用类 C 顺序。(这是一个可选参数,不需要指定。)

如果你想了解关于 C 和 Fortran 顺序的更多信息,你可以在这里读更多关于 NumPy 数组内部组织的信息。基本上,C 和 Fortran 顺序与索引如何对应到数组在内存中的存储顺序有关。在 Fortran 中,移动二维数组元素时,第一个索引是变化最快的索引。当第一个索引改变时,矩阵按列存储在内存中一列一列地变化。这就是为什么 Fortran 被认为是一种基于列的语言。而在 C 中,最后一个索引最快变化。矩阵按行存储,使之成为基于行的语言。你使用 C 或 Fortran 取决于是否更重要地保持索引约定或者不重新排序数据。

在这里了解更多有关形状操作的信息。

如何将一个一维数组转换为二维数组(如何给数组添加一个新的轴)

这一节介绍了 np.newaxisnp.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 的更多信息和关于expand_dimsexpand_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)
(0, 0)
(0, 1)
(0, 2)
(0, 3) 

你也可以使用np.nonzero()打印数组中小于 5 的元素:

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

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

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

在这里了解更多关于索引和切片的信息和这里。

在这里阅读有关使用 nonzero 函数的更多信息:nonzero

如何从现有数据创建数组

这部分涵盖切片和索引np.vstack()np.hstack()np.hsplit().view()copy()


你可以轻松地从现有数组的一部分创建一个新数组。

假设你有这个数组:

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

你可以随时通过指定想要切片的位置来从数组的一个部分创建新数组。

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

在这里,你从索引位置 3 到索引位置 8 取出了数组的一个部分。

你还可以垂直和水平堆叠两个已经存在的数组。假设你有两个数组,a1a2

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

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

你可以使用vstack将它们垂直堆叠:

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

或者使用hstack将它们水平堆叠:

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

你可以使用hsplit将数组分割成几个更小的数组。你可以指定要返回的形状相同的数组的数量,也可以指定分割应发生的列的位置。

假设你有这个数组:

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

如果你想将这个数组等分成三个形状相同的数组,你需要运行:

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

如果你想在第三和第四列后分割你的数组,你需要运行:

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

在这里了解更多有关堆叠和分割数组。

你可以使用view方法创建一个查看原始数组相同数据的新数组对象(浅复制)。

视图是 NumPy 中的重要概念! 在可能的情况下,NumPy 函数以及诸如索引和切片之类的操作都会返回视图。这样可以节省内存并提高速度(数据不需要复制)。然而,了解这一点很重要 - 在视图中修改数据也会修改原始数组!

假设你创建了这个数组:

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

现在我们通过切片a创建一个数组b1,并修改b1的第一个元素。这将同时修改a中对应的元素!

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

使用copy方法将创建数组的完整副本和数据(深复制)。你可以运行以下命令来使用它在数组上:

>>> b2 = a.copy() 

在这里了解有关复制和视图的更多内容。

基本数组操作

本节涵盖了加法、减法、乘法、除法等内容


创建数组后,你可以开始使用它们。例如,你创建了两个名为“data”和“ones”的数组

../_images/np_array_dataones.png

你可以用加号将数组相加。

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

../_images/np_data_plus_ones.png

当然,你当然还可以进行更多操作!

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

../_images/np_sub_mult_divide.png

在 NumPy 中,基本操作非常简单。如果你想要找到数组中元素的总和,你可以使用sum()。这适用于 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]) 

在这里了解更多有关基本操作的内容。

广播

有时你可能想要在数组和单个数字之间进行操作(也称为向量和标量之间的操作)或者在两个不同大小的数组之间进行操作。例如,你的数组(我们称之为“数据”)可能包含了以英里为单位的距离信息,但你想要将信息转换为公里。你可以通过以下操作来实现:

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

../_images/np_multiply_broadcasting.png

NumPy 理解乘法应该发生在每个单元格上。这个概念被称为广播。广播是一种机制,允许 NumPy 对不同形状的数组执行操作。你的数组的维度必须兼容,例如,当两个数组的维度相等时,或者其中一个维度是 1 时。如果维度不兼容,你将得到一个ValueError

在这里了解更多关于广播的信息。

更有用的数组操作

本节涵盖最大值、最小值、和、平均值、乘积、标准差等内容


NumPy 还执行聚合函数。除了minmaxsum之外,你还可以轻松地运行mean得到平均值,prod得到乘积的结果,std得到标准差等。

>>> data.max()
2.0
>>> data.min()
1.0
>>> data.sum()
3.0 

../_images/np_aggregation.png

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

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

想要沿着行或列进行聚合通常很常见。默认情况下,每个 NumPy 聚合函数将返回整个数组的聚合结果。要找到数组中元素的和或最小值,运行:

>>> a.sum()
4.8595784 

或者:

>>> a.min()
0.05093587 

你可以指定要计算聚合函数的轴。例如,你可以通过指定axis=0来找到每列中的最小值。

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

上述四个值对应于数组中列的数量。对于一个有四列的数组,你将得到四个值作为你的结果。

阅读更多关于 数组方法的内容。

创建矩阵

你可以传递 Python 的列表列表来创建一个 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

阅读更多关于创建数组,填充为01、其他值或未初始化的信息,参见数组创建例程。

产生随机数

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

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

这也适用于 2D 数组!如果你从这个数组开始:

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

你可以找到唯一值,np.unique()可以帮你实现。

>>> 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将数组展平为一个一维数组。

>>> 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.flattenravel中了解更多关于flattenravel的信息。

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

本节介绍 help()???


在数据科学生态系统方面,Python 和 NumPy 是为用户设计的。其中一个最好的例子就是内置访问文档的功能。每个对象都包含对一个字符串的引用,这个字符串被称为文档字符串。在大多数情况下,这个文档字符串包含对象和如何使用它的快速简明摘要。Python 有一个内置的help()函数,可以帮助您访问这些信息。这意味着几乎任何时候您需要更多信息,都可以使用help()快速找到您需要的信息。

例如:

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

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

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

因为额外信息的获取非常有用,IPython 使用?字符作为访问此文档以及其他相关信息的简短方式。IPython 是用于多种语言的交互式计算的命令行。你可以在这里找到有关 IPython 的更多信息

例如:

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

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

甚至可以对对象方法和对象本身使用这种表示法。

假设你创建了这个数组:

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

然后你可以获得很多有用的信息(首先是关于a本身的详细信息,然后是a所属的ndarray的文档字符串):

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

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

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

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

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

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

对于创建的函数和其他对象也适用这个方法。只需记住使用字符串字面值(用 """ """''' ''' 将你的文档括起来)为你的函数添加文档字符串即可。

例如,如果你创建了这个函数:

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

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

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

通过阅读你感兴趣的对象的源代码,可以获得更深入的信息。使用两个问号(??)可以访问源代码。

例如:

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

如果问题中的对象是在 Python 以外的语言中编译的,使用??将返回与?相同的信息。例如,您会发现许多内置对象和类型都是如此:

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

和:

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

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

处理数学公式

实现在数组上运行数学公式的简易性是让 NumPy 在科学 Python 社区中得到广泛应用的原因之一。

例如,这是均方误差公式(监督学习模型中常用于回归问题的中心公式):

../_images/np_MSE_formula.png

在 NumPy 中实现此公式简单而直接:

../_images/np_MSE_implementation.png

这样做得很好的原因是 predictionslabels 可能包含一个或一千个值,它们只需要具有相同的大小。

您可以以这种方式可视化它:

../_images/np_mse_viz1.png

在此示例中,预测和标签向量都包含三个值,这意味着 n 的值为三。在我们进行减法操作后,向量中的值被平方。然后 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函数保存到磁盘文件中,这些函数处理普通文本文件,使用处理 NumPy 二进制文件的loadsave函数,具有 .npy 文件扩展名,并使用处理具有 .npz 文件扩展名的 NumPy 文件的savez函数。

.npy.npz 文件存储数据、形状、数据类型以及其他信息,以便在需重建数组的情况下以一种允许正确检索数组的方式。即使文件位于具有不同架构的另一台机器上,也能正确检索数组。

如果要存储单个 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)、注释等。

了解更多关于输入和输出例程的信息。

导入和导出 CSV 文件

读取包含现有信息的 CSV 非常简单。最好和最简单的方法是使用Pandas

>>> import pandas as pd

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

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

../_images/np_pandas.png

使用 Pandas 导出数组也很简单。如果对 NumPy 不熟悉,可以从数组的值中创建一个 Pandas 数据框,然后使用 Pandas 将数据框写入 CSV 文件。

如果创建了该数组“a”

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

你可以创建一个 Pandas 数据框

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

你可以轻松地保存你的 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 感兴趣,请查看官方 Pandas 文档。了解如何使用官方 Pandas 安装信息安装 Pandas。

使用 Matplotlib 绘制数组

如果需要为你的值生成一个图表,使用Matplotlib非常简单。

例如,你可能有一个像这样的数组:

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

如果已经安装了 Matplotlib,可以使用以下方式导入它:

>>> import matplotlib.pyplot as plt

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

%matplotlib inline 

要绘制你的值,只需要运行:

>>> plt.plot(a)

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

../_images/matplotlib1.png

例如,可以通过以下方式绘制 1D 数组:

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

../_images/matplotlib2.png

使用 Matplotlib,你有许多可视化选项。

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

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

../_images/matplotlib3.png

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


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

欢迎来到 NumPy!

NumPy(Numerical Python)是一个开源的 Python 库,几乎在科学和工程的每个领域中都有使用。它是 Python 中处理数值数据的通用标准,是科学 Python 和 PyData 生态系统的核心。NumPy 的用户包括从初学者到进行尖端科学和工业研究与开发的经验丰富的研究人员。NumPy API 在 Pandas、SciPy、Matplotlib、scikit-learn、scikit-image 和大多数其他数据科学和科学 Python 包中广泛使用。

NumPy 库包含多维数组和矩阵数据结构(你将在后面的部分中找到更多信息)。它提供了ndarray,一个同构的 n 维数组对象,并提供了方法来高效地对其进行操作。NumPy 可以用于对数组执行各种各样的数学操作。它向 Python 添加了强大的数据结构,保证了对数组和矩阵的高效计算,并提供了大量的高级数学函数库,可以操作这些数组和矩阵。

在这里了解更多关于 NumPy 的信息!

安装 NumPy

要安装 NumPy,我们强烈建议使用科学 Python 发行版。如果你正在寻找有关在你的操作系统上安装 NumPy 的完整说明,请参阅安装 NumPy

如果你已经安装了 Python,你可以使用以下命令安装 NumPy:

conda install numpy 

或者

pip install numpy 

如果你还没有安装 Python,你可能想考虑使用Anaconda。这是最简单的入门方式。使用这个发行版的好处是你不需要过多地担心单独安装 NumPy 或者你将用于数据分析的任何主要包,如 pandas、Scikit-Learn 等。

如何导入 NumPy

要访问 NumPy 及其函数,请在你的 Python 代码中像这样导入它:

import numpy as np 

我们将导入的名称缩短为np,以提高使用 NumPy 的代码的可读性。这是一种被广泛采用的惯例,使得你的代码对所有工作在其中的人更易读。我们建议始终使用import numpy as np导入。

阅读示例代码

如果你还不熟悉阅读包含大量代码的教程,你可能不知道如何解释像这样的代码块:

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

如果你不熟悉这种风格,那么很容易理解。如果你看到>>>,你正在看输入,或者你将要输入的代码。任何不以>>>开头的都是输出,或者是你代码运行的结果。这是在命令行上运行python时看到的样式,但如果你使用 IPython,你可能会看到不同的样式。请注意,它不是代码的一部分,如果输入或粘贴到 Python shell 中会导致错误。它可以安全地输入或粘贴到 IPython shell 中;>>>会被忽略。

Python 列表和 NumPy 数组之间有什么区别?

NumPy 为您提供了大量快速高效的方式来创建数组并在其中操纵数字数据。 虽然 Python 列表可以包含单个列表中的不同数据类型,但 NumPy 数组中的所有元素应该是同质的。 如果数组不是同质的,那么对数组执行的数学运算将非常低效。

为什么使用 NumPy?

NumPy 数组比 Python 列表更快,更紧凑。 数组占用的内存更少且使用方便。 NumPy 使用的内存比存储数据需要的内存少得多,并提供了指定数据类型的机制。 这进一步优化了代码。

什么是数组?

数组是 NumPy 库的核心数据结构。 数组是一组值的网格,它包含有关原始数据的信息,如何定位元素以及如何解释元素。 它有一组可以以各种方式进行索引的元素。 这些元素都是相同类型的,称为数组的dtype

数组可以通过非负整数元组、布尔值、另一个数组或整数进行索引。 数组的rank是维数。 数组的shape是一组整数,给出沿每个维度的数组大小。

我们可以从 Python 列表中初始化 NumPy 数组的一种方式是使用嵌套列表进行二维或多维数据。

例如:

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

或者:

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

我们可以使用方括号访问数组中的元素。 当访问元素时,要记住 NumPy 中的索引从 0 开始。 这意味着如果您要访问数组中的第一个元素,您将访问元素“0”。

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

有关数组的更多信息

本部分涵盖 1D array, 2D array, ndarray, vector, matrix


你可能偶尔会听到一个数组被称为ndarray,这是“N-dimensional array”的简称。 N 维数组只是具有任意数量维度的数组。 您还可能听说1-D或一维数组,2-D或二维数组等。 NumPy ndarray类用于表示矩阵和向量。 向量是具有单个维度的数组(行向量和列向量之间没有区别),而矩阵指的是具有两个维度的数组。 对于3-D或更高维数组,术语张量也常常使用。

数组的属性是什么?

数组通常是相同类型和大小的项目的固定大小容器。 数组中的维数和项目数由其形状定义。数组的形状是一组非负整数元组,用于指定每个维度的大小。

在 NumPy 中,维度被称为。 这意味着如果你有一个看起来像这样的二维数组:

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

你的数组有 2 个轴。 第一个轴的长度为 2,第二个轴的长度为 3。

就像其他 Python 容器对象一样,数组的内容可以通过对数组进行索引或切片来访问和修改。与典型的容器对象不同,不同的数组可以共享相同的数据,因此在一个数组上进行的更改可能会在另一个数组中可见。

数组的属性反映了数组本身的内在信息。如果你需要获取或设置数组的属性而不创建新数组,通常可以通过其属性访问数组。

在这里阅读有关数组属性的更多信息,并了解数组对象。

如何创建基本数组

这一部分涵盖了np.array()np.zeros()np.ones()np.empty()np.arange()np.linspace()dtype


要创建一个 NumPy 数组,您可以使用函数np.array()

要创建一个简单的数组,您只需要将列表传递给它。如果您选择的话,还可以指定列表中的数据类型。您可以在这里找到有关数据类型的更多信息。

>>> import numpy as np
>>> a = np.array([1, 2, 3]) 

您可以以此方式可视化您的数组:

../_images/np_array.png

请注意,这些可视化是为了简化思想并让您对 NumPy 概念和机制有基本了解。数组和数组操作比这里描述的要复杂得多!

除了从一系列元素创建数组之外,您还可以轻松创建一个填满0的数组:

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

或者一个填满1的数组:

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

或者甚至是一个空数组!函数empty创建一个初始内容是随机的数组,取决于内存状态。使用empty而不是zeros(或类似的东西)的原因是速度快 - 只需确保之后填充每个元素!

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

您可以创建一个包含一系列元素的数组:

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

甚至包含一系列间隔均匀的范围的数组。为此,您将指定第一个数字最后一个数字步长

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

您还可以使用np.linspace()在指定间隔中创建线性间隔的值数组:

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

指定数据类型

尽管默认数据类型是浮点数(np.float64),但您可以使用dtype关键字明确指定您想要的数据类型。

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

在这里了解更多有关创建数组的信息

添加、删除和排序元素

这一部分涵盖了np.sort()np.concatenate()


使用np.sort()对元素进行排序很简单。在调用该函数时,您可以指定轴、种类和顺序。

如果您从这个数组开始:

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

您可以快速将数字按升序排序为:

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

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

  • argsort,它是沿指定轴的间接排序,

  • lexsort,这是多个键的间接稳定排序,

  • searchsorted,将在排序数组中查找元素,并

  • partition,这是一种部分排序。

要阅读有关对数组排序的更多信息,请参阅:sort.

如果你从这些数组开始:

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

您可以使用np.concatenate()进行连接。

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

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

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

您可以使用以下内容进行连接:

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

为了从数组中移除元素,可以简单地使用索引来选择要保留的元素。

要阅读有关连接的更多信息,请参阅:concatenate.

你如何知道数组的形状和大小?

本节介绍 ndarray.ndimndarray.sizendarray.shape


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

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

ndarray.shape将显示一个元组,其中的整数指示数组沿每个维度存储的元素数。例如,如果你有一个 2 行 3 列的二维数组,你的数组的形状是(2, 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, newshape=(1, 6), order='C')
array([[0, 1, 2, 3, 4, 5]]) 

a是要重塑的数组。

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

order: C表示使用类似 C 的索引顺序读取/写入元素,F表示使用类似 Fortran 的索引顺序读取/写入元素,A表示如果 a 在内存中是 Fortran 连续的,使用类似 Fortran 的索引顺序读取/写入元素,否则使用类似 C 的顺序。(这是一个可选参数,无需指定。)

如果你想了解有关 C 和 Fortran 排序的更多信息,你可以在这里读取有关 NumPy 数组内存中的内部组织的更多信息。基本上,C 和 Fortran 排序与索引与数组在内存中存储的顺序相对应有关。在 Fortran 中,当在内存中移动二维数组的元素时,第一个索引是最快变化的索引。随着第一个索引的变化移动到下一行,矩阵按列存储。这就是为什么 Fortran 被认为是一种列主语言。另一方面,在 C 中,最后的索引变化最快。矩阵按行存储,使其成为一种行主语言。你在 C 或 Fortran 中所做的取决于是更重要保留索引约定还是不重新排序数据。

在这里了解更多有关形状操作的信息。

如何将一个 1 维数组转换为 2 维数组(如何向数组添加一个新轴)

本节介绍 np.newaxisnp.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 将 1 维数组显式地转换为行向量或列向量。例如,你可以通过在第一维度上插入一个轴将 1 维数组转换为行向量:

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

索引和切片

你可以像切片 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)
(0, 0)
(0, 1)
(0, 2)
(0, 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

如何从现有数据创建数组

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


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

假设你有这样一个数组:

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

您可以随时使用切片指定要从数组中切片的位置创建数组的新数组。

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

在这里,你从索引位置 3 到索引位置 8 获取了你数组的一个部分。

你也可以垂直和水平地堆叠两个现有数组。假设你有两个数组,a1a2

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

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

你可以使用vstack将它们垂直堆叠:

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

或者使用hstack水平堆叠它们:

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

你可以使用hsplit将数组分割成几个较小的数组。你可以指定要返回的形状相等的数组的数量,或者应该在哪个列之后进行分割。

假设你有这样一个数组:

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

如果你想将此数组分割成三个形状相等的数组,你需要执行:

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

如果您想在第三和第四列之后分割数组,您需要执行:

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

在这里了解有关堆叠和分割数组的更多信息。

您可以使用view方法创建一个查看原始数组相同数据的新数组对象(浅复制)。

视图是一个重要的 NumPy 概念!NumPy 函数以及索引和切片等操作将尽可能返回视图。这样可以节省内存并提高速度(不需要进行数据复制)。但是要注意这一点——修改视图中的数据也会修改原始数组!

假设您创建了这个数组:

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

现在我们通过对a进行切片并修改b1的第一个元素来创建数组b1。这将同时修改a中对应的元素!

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

使用copy方法将创建数组及其数据的完整副本(深度复制)。要在您的数组上使用此方法,您可以运行:

>>> b2 = a.copy() 

在此处了解更多关于副本和视图的信息。

基本数组操作

本节涵盖了加法、减法、乘法、除法等内容


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

../_images/np_array_dataones.png

您可以使用加号将数组相加。

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

../_images/np_data_plus_ones.png

当然,您不仅可以进行加法运算!

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

../_images/np_sub_mult_divide.png

NumPy 的基本操作非常简单。如果要找到数组中元素的总和,您可以使用sum()。这适用于 1D 数组、2D 数组和更高维度的数组。

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

>>> a.sum()
10 

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

如果您从这个数组开始:

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

您可以对行轴求和:

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

您可以对列轴求和:

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

在此处了解更多关于基本操作的信息。

广播

有时,您可能希望在数组和单个数字之间执行操作(也称为向量和标量之间的操作),或者在两个不同大小的数组之间执行操作。例如,您的数组(我们将其称为“data”)可能包含有关以英里为单位的距离的信息,但您希望将信息转换为公里。您可以执行此操作:

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

../_images/np_multiply_broadcasting.png

NumPy 理解乘法应该在每个单元格中发生。这个概念称为广播。广播是一种机制,允许 NumPy 在不同形状的数组上执行操作。您的数组的维度必须兼容,例如,当两个数组的维度相等或其中一个为 1 时。如果维度不兼容,您将收到一个ValueError

在此处了解更多关于广播的信息。

更多有用的数组操作

本节涵盖了最大值、最小值、求和、平均值、乘积、标准差等内容


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

>>> data.max()
2.0
>>> data.min()
1.0
>>> data.sum()
3.0 

../_images/np_aggregation.png

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

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

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

>>> a.sum()
4.8595784 

或:

>>> a.min()
0.05093587 

你可以指定要计算聚合函数的轴。 例如,你可以通过指定axis=0来找到每列的最小值。

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

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

阅读更多关于数组方法的信息。

创建矩阵

你可以传递 Python 的列表列表来创建一个代表它们的 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

阅读更多关于创建数组,填充01、其他值或未初始化的数组的内容,在 array creation routines 中。

生成随机数

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

使用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

../_images/np_transposing_reshaping.png

在某些情况下,你可能也需要调换矩阵的维度。例如,当你的模型所期望的输入形状与你的数据集不同时,就需要这样做。这时reshape方法就很有用。你只需要传入你想要给该矩阵的新维度即可。

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

../_images/np_reshape.png

你也可以使用.transpose()根据你指定的值来反转或更改数组的轴。

如果你从这个数组开始:

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

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

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

你也可以使用arr.T

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

要了解更多关于转置和重新塑形数组的信息,请参阅transposereshape

如何反转一个数组

本节涵盖 np.flip()


NumPy 的np.flip()函数允许您沿着轴翻转或反转数组的内容。当使用np.flip()时,请指定您想要翻转的数组和轴。如果您不指定轴,NumPy 将沿着输入数组的所有轴反转内容。

反转一维数组

如果你从这样一个一维数组开始:

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

你可以用以下方式反转它:

>>> reversed_arr = np.flip(arr) 

如果你想打印你反转的数组,你可以运行:

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

反转二维数组

一个二维数组的工作方式基本相同。

如果你从这个数组开始:

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

你可以用以下方式反转所有行和所有列的内容:

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

你可以只轻松地反转

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

或者只反转

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

你也可以只反转一个列或行的内容。例如,你可以反转索引位置为 1(第二行)的行的内容:

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

你也可以反转索引位置为 1(第二列)的列:

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

阅读有关反转数组的更多信息在flip

重新塑造和展平多维数组

本节涵盖 .flatten()ravel()


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

如果你从这个数组开始:

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

你可以使用flatten将你的数组展平成一个一维数组。

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

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

例如:

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

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

例如:

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

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

如何访问更多信息的文档字符串

本节涵盖 help()???


当涉及到数据科学生态系统时,Python 和 NumPy 是为用户而构建的。这中的一个最好的例子就是内置的文档访问。每个对象都包含对字符串的引用,这被称为文档字符串。在大多数情况下,这个文档字符串包含了关于对象以及如何使用它的快速而简洁的摘要。Python 有一个内置的 help() 函数,可以帮助您访问这些信息。这意味着几乎任何时候,您需要更多的信息,都可以使用 help() 快速找到您需要的信息。

例如:

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

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

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

因为访问附加信息非常有用,IPython 使用 ? 字符作为访问此文档以及其他相关信息的速记。IPython 是一个多语言交互计算的命令行壳。您可以在此处找到有关 IPython 的更多信息

例如:

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

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

甚至可以使用这种表示法来表示对象方法和对象本身。

假设你创建了这个数组:

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

然后您可以获得许多有用的信息(首先关于 a 本身的细节,然后是 a 是其实例的 ndarray 的文档字符串):

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

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

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

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

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

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

这对于您创建的函数和其他对象也是有效的。只需记住使用字符串文字 (""" """''' ''' 将您的函数文档包含在您的函数中)。

例如,如果您创建了这个函数:

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

您可以获取有关该函数的信息:

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

通过阅读您感兴趣的对象的源代码,您可以获得另一个级别的信息。使用双问号 (??) 允许您访问源代码。

例如:

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

如果所讨论的对象是用 Python 以外的语言编译的,使用 ?? 将返回与 ? 相同的信息。您会发现在许多内置对象和类型中都是如此,例如:

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

和:

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

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

使用数学公式

实现可以在数组上工作的数学公式的简便性是使 NumPy 在科学 Python 社区中被广泛使用的因素之一。

例如,这是均方误差公式(在处理回归的监督式机器学习模型中使用的一个核心公式):

../_images/np_MSE_formula.png

在 NumPy 中实现这个公式简单而直接:

../_images/np_MSE_implementation.png

这样做的原因是 predictionslabels 可以包含一个或一千个值。它们只需要是相同的大小。

您可以这样可视化它:

../_images/np_mse_viz1.png

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

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

如何保存和加载 NumPy 对象

这一部分涵盖了 np.savenp.saveznp.savetxtnp.loadnp.loadtxt


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

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

如果你想要存储一个单一的 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()函数接受额外的可选参数,如头部、尾部和分隔符。虽然文本文件更容易共享,但.npy 和.npz 文件更小且读取速度更快。如果你需要更复杂的文本文件处理(例如,如果需要处理包含缺失值的行),你会想要使用genfromtxt函数。

使用savetxt,你可以指定头部、尾部、注释等额外的可选参数。

在这里了解更多关于输入和输出例程。

导入和导出 CSV

很容易读取包含现有信息的 CSV 文件。这样做的最佳、最简单的方式是使用Pandas

>>> import pandas as pd

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

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

../_images/np_pandas.png

使用 Pandas 导出数组同样简单。如果您是 NumPy 的新手,您可能希望从数组的值中创建一个 Pandas 数据帧,然后用 Pandas 将数据帧写入 CSV 文件。

如果你创建了这个数组“a”

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

您可以创建一个 Pandas 数据帧

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

您可以轻松保存您的数据帧:

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

并使用以下命令读取您的 CSV 文件:

>>> data = pd.read_csv('pd.csv') 

../_images/np_readcsv.png

您还可以使用 NumPy 的savetxt方法保存数组。

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

如果您使用命令行,随时可以通过像这样的命令读取已保存的 CSV 文件:

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

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

如果您有兴趣了解更多关于 Pandas 的信息,请查看官方 Pandas 文档。通过官方 Pandas 安装信息了解如何安装 Pandas。

用 Matplotlib 绘制数组

如果您需要为您的值生成一张图表,使用Matplotlib非常简单。

例如,您可能有这样一个数组:

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

如果您已经安装了 Matplotlib,可以用以下方法导入:

>>> import matplotlib.pyplot as plt

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

%matplotlib inline 

要绘制您的值,您只需运行:

>>> plt.plot(a)

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

../_images/matplotlib1.png

例如,您可以这样绘制一个一维数组:

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

../_images/matplotlib2.png

通过 Matplotlib,您可以获得大量的可视化选项。

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

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

../_images/matplotlib3.png

要了解更多关于 Matplotlib 及其功能的信息,请查看官方文档。有关安装 Matplotlib 的指导,请参阅官方的安装部分


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

posted @ 2024-06-24 14:57  绝不原创的飞龙  阅读(15)  评论(0编辑  收藏  举报