NumPy 用户指南
本指南是一个概述,解释了重要特性;细节请参阅 NumPy 参考文档。
入门指南
- 什么是 NumPy?
- 安装
- NumPy 快速入门
- NumPy:初学者的绝对基础
基础知识和用法
- NumPy 基础知识
- 适用于 MATLAB 用户的 NumPy
- NumPy 教程
- NumPy 实用技巧
高级用法和互操作性
- 从源代码构建
- 使用 NumPy C-API
- F2PY 用户指南和参考手册
- 开发者幕后文档
- 与 NumPy 的互操作性
开始入门
什么是 NumPy?
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 列表a
和b
中,我们可以遍历每个元素:
c = [] for i in range(len(a)): c.append(a[i]*b[i])
这可以得出正确的答案,但如果a
和b
中各包含数百万个数字,那么我们将为在 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 中,所有操作(不仅仅是算术操作,还有逻辑、位运算、函数等)都以这种隐式逐点方式行为,即它们进行广播。此外,在上面的示例中,a
和 b
可能是相同形状的多维数组,或者是标量和数组,甚至是两个形状不同的数组,只要较小的数组可以“扩展”到大数组的形状,使得结果的广播是明确的。有关广播的详细“规则”请参见 Broadcasting。
谁还在使用 NumPy?
NumPy 充分支持面向对象的方法,再次从 ndarray 开始。例如,ndarray 是一个类,拥有 numerous 方法和属性。它的许多方法与 NumPy 最外层命名空间中的函数相对应,允许程序员使用他们偏好的任何范例编码。这种灵活性使得 NumPy 数组方言和 NumPy ndarray 类成为 Python 中用于多维数据交换的事实上语言。
为什么 NumPy 如此快速?
矢量化描述代码中的任何显式循环、索引等都不存在——当然,这些事情当然存在,只是以优化的、预编译的 C 代码“在幕后”方式进行。矢量化代码有许多优势,其中包括:
- 向量化的代码更简洁,更易读。
- 更少的代码行通常意味着更少的错误。
- 代码更接近标准数学表示法(通常更容易正确编写数学结构)。
- 向量化导致更“Pythonic”的代码。没有向量化,我们的代码将充满效率低下且难以阅读的
for
循环。
广播是用来描述操作的隐式逐元素行为的术语;一般来说,在 NumPy 中所有操作,不仅仅是算术操作,还包括逻辑、位操作、函数等等,都以这种隐式逐元素的方式进行,即它们进行广播。此外,在上面的示例中,a
和 b
可以是相同形状的多维数组,或者是标量和数组,甚至是两个形状不同的数组,只要较小的数组可以“扩展”到较大数组的形状,使得结果的广播不会产生歧义即可。有关广播的详细“规则”,请参阅广播。
谁还在使用 NumPy?
NumPy 完全支持面向对象的方法,再次以 ndarray 为例。例如,ndarray 是一个类,拥有许多方法和属性。其中许多方法在 NumPy 最外层的命名空间中都有相应的函数,使程序员可以按照他们喜欢的范式编码。这种灵活性使得 NumPy 数组方言和 NumPy ndarray 类成为 Python 中多维数据交换的事实标准语言。
NumPy 快速开始
先决条件
你需要了解一点 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
## 形状操作
改变数组的形状
数组的形状由沿每个轴的元素数量确定:
>>> 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
通常不需要复制其参数,但如果数组是通过对另一个数组取片或使用不寻常的选项创建的,则可能需要复制。函数ravel
和reshape
还可以使用可选参数指示使用 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_stack
是 vstack
的别名:
>>> 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_
在默认行为上类似于 vstack
和 hstack
,但允许一个可选参数,用于指定沿哪个轴进行连接。
另请参阅
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]
,则a
由b
引用,并且即使执行del a
,a
也会在内存中持续存在。
函数和方法概述
这是一些有用的 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 1.26 中文官方指南(一)(2)https://developer.aliyun.com/article/1510632