NumPy-1-26-中文文档-五-

NumPy 1.26 中文文档(五)

原文:numpy.org/doc/

numpy.ndenumerate

原文:numpy.org/doc/1.26/reference/generated/numpy.ndenumerate.html

class numpy.ndenumerate(arr)

多维索引迭代器。

返回一个迭代器,产生数组坐标和值的对。

参数:

arrndarray

输入数组。

另请参见:

ndindexflatiter

示例

>>> a = np.array([[1, 2], [3, 4]])
>>> for index, x in np.ndenumerate(a):
...     print(index, x)
(0, 0) 1
(0, 1) 2
(1, 0) 3
(1, 1) 4 

numpy.ndindex

原文:numpy.org/doc/1.26/reference/generated/numpy.ndindex.html

class numpy.ndindex(*shape)

用于索引数组的 N 维迭代器对象。

给定数组的形状,一个ndindex实例会遍历数组的 N 维索引。在每次迭代时,一个索引元组被返回,最后一个维度先被迭代。

参数:

shapeints 或一个整数元组

数组的每个维度大小可以作为单独的参数传递,也可以作为元组的元素传递。

参见

ndenumerate, flatiter

示例

Dimensions as individual arguments

>>> for index in np.ndindex(3, 2, 1):
...     print(index)
(0, 0, 0)
(0, 1, 0)
(1, 0, 0)
(1, 1, 0)
(2, 0, 0)
(2, 1, 0) 

相同的维度 - 但以元组形式(3, 2, 1)

>>> for index in np.ndindex((3, 2, 1)):
...     print(index)
(0, 0, 0)
(0, 1, 0)
(1, 0, 0)
(1, 1, 0)
(2, 0, 0)
(2, 1, 0) 

方法

ndincr() 将多维索引增加一。

numpy.nested_iters

原文:numpy.org/doc/1.26/reference/generated/numpy.nested_iters.html

numpy.nested_iters(op, axes, flags=None, op_flags=None, op_dtypes=None, order='K', casting='safe', buffersize=0)

创建用于嵌套循环中的 nditer

创建一个 nditer 对象的元组,它在 op 参数的不同轴上以嵌套循环的方式进行迭代。第一个迭代器用于最外层循环,最后一个用于最内层循环。推进一个将会使后续的迭代器指向它的新元素。

参数:

op ndarray 或数组样式的序列

要迭代的数组。

axes int 的列表的列表

每个项目都被用作一个“op_axes”参数传递给 nditer

flags, op_flags, op_dtypes, order, casting, buffersize(可选)

参见相同名称的 nditer 参数

返回:

iters nditer 的元组

对于 axes 中的每个项目,从最外层开始的一个 nditer

另请参阅

nditer

示例

基本用法。注意,由于我们将第一个迭代器的轴指定为 [1],因此 y 是 [a[:, 0, :], a[:, 1, 0], a[:, 2, :]] 的“扁平化”版本

>>> a = np.arange(12).reshape(2, 3, 2)
>>> i, j = np.nested_iters(a, [[1], [0, 2]], flags=["multi_index"])
>>> for x in i:
...      print(i.multi_index)
...      for y in j:
...          print('', j.multi_index, y)
(0,)
 (0, 0) 0
 (0, 1) 1
 (1, 0) 6
 (1, 1) 7
(1,)
 (0, 0) 2
 (0, 1) 3
 (1, 0) 8
 (1, 1) 9
(2,)
 (0, 0) 4
 (0, 1) 5
 (1, 0) 10
 (1, 1) 11 

numpy.flatiter

原文:numpy.org/doc/1.26/reference/generated/numpy.flatiter.html

class numpy.flatiter

用于遍历数组的平坦迭代器对象。

任意数组xflatiter迭代器是由x.flat返回的。它允许像遍历 1-D 数组一样遍历数组,可以通过 for 循环或调用其next方法来实现。

迭代按行优先、C 风格顺序进行(最后的索引变化最快)。迭代器还可以使用基本切片或高级索引进行索引。

另请参阅

ndarray.flat

返回数组的平坦迭代器。

ndarray.flatten

返回数组的平坦副本。

注意事项

不能通过调用flatiter构造函数直接从 Python 代码中构建flatiter迭代器。

示例

>>> x = np.arange(6).reshape(2, 3)
>>> fl = x.flat
>>> type(fl)
<class 'numpy.flatiter'>
>>> for item in fl:
...     print(item)
...
0
1
2
3
4
5 
>>> fl[2:4]
array([2, 3]) 

属性:

base

被遍历的数组的引用。

coords

当前坐标的 N 维元组。

index

数组中的当前平坦索引。

方法

copy() 获取迭代器的 1-D 数组副本。

numpy.lib.Arrayterator

原文:numpy.org/doc/1.26/reference/generated/numpy.lib.Arrayterator.html

class numpy.lib.Arrayterator(var, buf_size=None)

大数组的缓冲迭代器。

Arrayterator创建一个缓冲迭代器,用于以小的连续块读取大数组。该类对存储在文件系统中的对象非常有用。它允许对对象进行迭代而不是读取所有内存;相反,将读取和迭代小块。

Arrayterator可以与支持多维切片的任何对象一起使用。这包括 NumPy 数组,也包括来自 Scientific.IO.NetCDF 或 pynetcdf 的变量等。

参数:

vararray_like

要迭代的对象。

buf_sizeint, 可选

缓冲区大小。如果提供了buf_size,则将读取到内存的数据的最大数量是buf_size元素。默认为 None,将尽可能多地读取元素到内存中。

参见

ndenumerate

多维数组迭代器。

flatiter

平面数组迭代器。

memmap

创建一个映射到存储在磁盘上的二进制文件中的数组的内存映射。

注意

该算法首先找到一个“运行维度”,沿着这个维度将提取块。给定一个维度数组(d1, d2, ..., dn),例如如果buf_size小于d1,则将使用第一维。另一方面,如果d1 < buf_size < d1*d2,则将使用第二维,依此类推。沿着这个维度提取块,并且当返回最后一个块时,该过程将从下一个维度继续进行,直到所有元素都被读取。

例子

>>> a = np.arange(3 * 4 * 5 * 6).reshape(3, 4, 5, 6)
>>> a_itor = np.lib.Arrayterator(a, 2)
>>> a_itor.shape
(3, 4, 5, 6) 

现在我们可以对a_itor进行迭代,它会返回大小为二的数组。由于buf_size比任何维度都小,首先将迭代第一维:

>>> for subarr in a_itor:
...     if not subarr.all():
...         print(subarr, subarr.shape) 
>>> # [[[[0 1]]]] (1, 1, 1, 2) 

属性:

var

buf_size

start

stop

step

shape

要迭代的数组的形状。

flat

用于 Arrayterator 对象的一维平面迭代器。

numpy.iterable

numpy.org/doc/1.26/reference/generated/numpy.iterable.html

numpy.iterable(y)

检查对象是否可以迭代。

参数:

y object

输入对象。

返回值:

b bool

如果对象具有迭代器方法或是一个序列,则返回True,否则返回False

注意事项

在大多数情况下,np.iterable(obj)的结果与isinstance(obj, collections.abc.Iterable)一致。一个显著的例外是对于 0 维数组的处理:

>>> from collections.abc import Iterable
>>> a = np.array(1.0)  # 0-dimensional numpy array
>>> isinstance(a, Iterable)
True
>>> np.iterable(a)
False 

示例

>>> np.iterable([1, 2, 3])
True
>>> np.iterable(2)
False 

遍历数组

原文:numpy.org/doc/1.26/reference/arrays.nditer.html

注意

数组支持迭代器协议,可以像 Python 列表一样进行迭代。查看快速入门指南中的“索引、切片和迭代”部分,了解基本用法和示例。本文档的其余部分介绍了nditer对象,并涵盖了更高级的用法。

nditer迭代器对象在 NumPy 1.6 中引入,提供了许多灵活的方式以系统化的方式访问一个或多个数组的所有元素。本页面介绍了如何在 Python 中对数组进行计算的一些基本方法,然后总结了如何可以在 Cython 中加速内部循环。由于 Python 对nditer的暴露相对直接地映射了 C 数组迭代器 API,这些想法也将对使用 C 或 C++进行数组迭代工作提供帮助。

单数组迭代

nditer最基本的任务是访问数组的每个元素。使用标准的 Python 迭代器接口逐个提供每个元素。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a):
...     print(x, end=' ')
...
0 1 2 3 4 5 

对于这种迭代需要注意的重要事项是,顺序是选择与数组的内存布局匹配而不是使用标准的 C 或 Fortran 顺序。这样做是为了访问效率,体现了默认情况下只需访问每个元素而不考虑特定顺序的思想。通过迭代前一数组的转置,与以 C 顺序复制该转置进行比较,我们可以看到这一点。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
...     print(x, end=' ')
...
0 1 2 3 4 5 
>>> for x in np.nditer(a.T.copy(order='C')):
...     print(x, end=' ')
...
0 3 1 4 2 5 

aa.T的元素以相同顺序遍历,即它们在内存中存储的顺序,而a.T.copy(order=’C’)的元素以不同顺序访问,因为它们已经放置到不同的内存布局中。

控制迭代顺序

有时重要的是按照特定顺序访问数组的元素,而不考虑内存中元素的布局。nditer对象提供了一个order参数来控制迭代的这个方面。默认情况下,具有上述行为的行为是 order=’K’以保持现有顺序。这可以通过 order=’C’覆盖为 C 顺序和 order=’F’为 Fortran 顺序。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
...     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
...     print(x, end=' ')
...
0 3 1 4 2 5 

修改数组值

默认情况下,nditer将输入操作数视为只读对象。要能够修改数组元素,必须使用‘readwrite’‘writeonly’每个操作数标记来指定读写或仅写模式。

nditer 然后将生成可修改的缓冲区数组。然而,因为 nditer 必须在迭代结束时将此缓冲区数据复制回原始数组,所以必须通过两种方法之一来标志迭代何时结束。您可以选择:

  • with语句使用 nditer 作为上下文管理器,并在上下文退出时将临时数据写回。
  • 在迭代结束时调用迭代器的close方法,这将触发写回操作。

迭代器不能在close被调用或者它的上下文退出后继续迭代。

示例

>>> a = np.arange(6).reshape(2,3)
>>> a
array([[0, 1, 2],
 [3, 4, 5]])
>>> with np.nditer(a, op_flags=['readwrite']) as it:
...    for x in it:
...        x[...] = 2 * x
...
>>> a
array([[ 0,  2,  4],
 [ 6,  8, 10]]) 

如果你正在编写需要支持较旧版本的 numpy 的代码,请注意,在 1.15 之前,nditer不是一个上下文管理器,并且没有close方法。相反,它依靠析构函数来初始化缓冲区的写回。

使用外部循环

到目前为止,所有的示例中,a的元素都是由迭代器逐个提供的,因为所有的循环逻辑都是迭代器内部的。虽然这很简单和方便,但效率不高。更好的方法是将一维内层循环移到你的代码中,迭代器之外。这样,NumPy 的矢量化操作可以在更大的元素块上使用。

nditer将尝试提供尽可能大的块给内部循环。通过强制使用'C'和'F'顺序,我们可以得到不同的外部循环大小。通过指定迭代器标志来启用此模式。

注意,默认情况下保持本机内存顺序,迭代器能够提供一个单一的一维块,而当强制使用 Fortran 顺序时,它必须提供三个块,每个块有两个元素。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop']):
...     print(x, end=' ')
...
[0 1 2 3 4 5] 
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 

跟踪一个索引或多索引

在迭代过程中,您可能希望在计算中使用当前元素的索引。例如,您可能希望以内存顺序访问数组的元素,但使用 C 顺序、Fortran 顺序或多维索引在不同的数组中查找值。

索引由迭代器对象本身跟踪,并通过indexmulti_index属性访问,具体取决于请求了哪个。下面的示例展示了打印输出,展示了索引的进展:

示例

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> for x in it:
...     print("%d <%d>" % (x, it.index), end=' ')
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> for x in it:
...     print("%d <%s>" % (x, it.multi_index), end=' ')
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     for x in it:
...         x[...] = it.multi_index[1] - it.multi_index[0]
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

跟踪一个索引或多索引与使用外部循环不兼容,因为它需要每个元素一个不同的索引值。如果尝试组合这些标志,nditer对象将引发异常。

示例

>>> a = np.zeros((2,3))
>>> it = np.nditer(a, flags=['c_index', 'external_loop'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Iterator flag EXTERNAL_LOOP cannot be used if an index or multi-index is being tracked 

替代循环和元素访问

为了在迭代时更方便地访问其属性,nditer具有与迭代器对象本身明确工作的用于迭代的替代语法。通过这种循环结构,可以通过对迭代器进行索引来访问当前值。其它属性,例如跟踪的索引,保持与之前相同。下面的示例产生了与前一节中相同的结果。

示例

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> while not it.finished:
...     print("%d <%d>" % (it[0], it.index), end=' ')
...     is_not_finished = it.iternext()
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> while not it.finished:
...     print("%d <%s>" % (it[0], it.multi_index), end=' ')
...     is_not_finished = it.iternext()
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     while not it.finished:
...         it[0] = it.multi_index[1] - it.multi_index[0]
...         is_not_finished = it.iternext()
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

对数组元素进行缓冲

在强制迭代顺序时,我们观察到外部循环选项可能以较小的块提供元素,因为元素不能以恒定步长按适当顺序访问。在编写 C 代码时,这通常没问题,但在纯 Python 代码中,这可能会导致性能显着降低。

通过启用缓冲模式,迭代器提供给内部循环的块可以变得更大,从而显着减少了 Python 解释器的开销。在强制 Fortran 迭代顺序的示例中,启用缓冲时,内部循环可以一次看到所有元素。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 
>>> for x in np.nditer(a, flags=['external_loop','buffered'], order='F'):
...     print(x, end=' ')
...
[0 3 1 4 2 5] 

迭代特定数据类型

有时有必要将数组视为与其存储方式不同的数据类型。例如,即使被操作的数组是 32 位浮点数,也可能希望在 64 位浮点数上进行所有计算。除了编写低级 C 代码时,通常最好让迭代器处理复制或缓冲,而不是在内部循环中自己进行数据类型转换。

有两种机制可以实现这一点,临时复制和缓冲模式。通过临时复制,使用新数据类型制作整个数组的副本,然后对副本进行迭代。在所有迭代完成之后,可以通过更新原始数组的方式进行写访问。临时复制的主要缺点是,如果迭代数据类型的 itemsize 比原始数据类型更大,临时副本可能会占用大量内存。

缓冲模式减轻了内存使用问题,并且比制作临时副本更加缓存友好。除了特殊情况,在迭代器外需要一次性整个数组的情况下,推荐使用缓冲而不是临时复制。在 NumPy 中,ufuncs 和其他函数使用缓冲来支持以最小内存开销支持灵活输入。

在我们的示例中,我们将以复杂数据类型处理输入数组,以便我们可以对负数取平方根。在不启用复制或缓冲模式的情况下,如果数据类型与迭代器不精确匹配,迭代器将引发异常。

示例

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand required copying or buffering, but neither copying nor buffering was enabled 

在复制模式中,“copy”被指定为每个操作数的标志。这是为了以每个操作数的方式提供控制。缓冲模式被指定为迭代器标志。

示例

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_flags=['readonly','copy'],
...                 op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 

迭代器使用 NumPy 的转换规则来确定是否允许特定的转换。默认情况下,它强制执行‘safe’转换。这意味着,例如,如果您尝试将 64 位浮点数数组视为 32 位浮点数数组,它会引发异常。在许多情况下,‘same_kind’规则是最合理的规则,因为它允许从 64 位转换为 32 位浮点数,但不允许从浮点数转换为整数或从复数转换为浮点数。

实例

>>> a = np.arange(6.)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32']):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('float32') according to the rule 'safe' 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32'],
...                 casting='same_kind'):
...     print(x, end=' ')
...
0.0 1.0 2.0 3.0 4.0 5.0 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['int32'], casting='same_kind'):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int32') according to the rule 'same_kind' 

当使用读写或仅写操作数时,要注意的一点是在转换回原始数据类型时可能会遇到问题。一个常见情况是用 64 位浮点数实现内部循环,使用‘same_kind’转换来允许处理其他浮点类型。在只读模式下,可以提供一个整数数组,但在读写模式下会抛出异常,因为转换回数组会违反转换规则。

实例

>>> a = np.arange(6)
>>> for x in np.nditer(a, flags=['buffered'], op_flags=['readwrite'],
...                 op_dtypes=['float64'], casting='same_kind'):
...     x[...] = x / 2.0
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Iterator requested dtype could not be cast from dtype('float64') to dtype('int64'), the operand 0 dtype, according to the rule 'same_kind' 

广播数组迭代

NumPy 有一组规则用于处理形状不同的数组,惯例上在函数接受多个操作数时会对这些规则进行应用,这些操作数合并在一起进行元素级操作。这称为广播。nditer对象可以在需要编写这样一个函数时为您应用这些规则。

举个例子,我们打印出一个一维数组和一个二维数组进行广播的结果。

实例

>>> a = np.arange(3)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print("%d:%d" % (x,y), end=' ')
...
0:0 1:1 2:2 0:3 1:4 2:5 

当广播错误发生时,迭代器会引发一个异常,其中包括输入形状,以帮助诊断问题。

实例

>>> a = np.arange(2)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print("%d:%d" % (x,y), end=' ')
...
Traceback (most recent call last):
...
ValueError: operands could not be broadcast together with shapes (2,) (2,3) 

迭代器分配输出数组

NumPy 函数中的一个常见情况是根据输入的广播分配输出,并且还有一个名为‘out’的可选参数,用于提供结果位置。nditer对象提供了一个方便的习语,使得支持这种机制变得非常容易。

我们将通过创建一个square函数来展示它是如何工作的,该函数将输入的平方。让我们从一个最小的函数定义开始,不包括‘out’参数支持。

实例

>>> def square(a):
...     with np.nditer([a, None]) as it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9]) 

默认情况下,nditer对传入的为 None 的操作数使用标志‘allocate’和‘writeonly’。这意味着我们只需提供迭代器的两个操作数,它就会处理其余部分。

在添加‘out’参数时,我们必须显式提供这些标志,因为如果有人将数组作为‘out’传入,迭代器将默认为‘readonly’,而我们的内部循环会失败。‘readonly’是输入数组的默认选项是为了防止意外触发减少操作的混淆。如果默认选项是‘readwrite’,任何广播操作都会触发减少操作,这个主题稍后在本文档中会讨论。

顺便提一句,我们还要介绍‘no_broadcast’标志,它将阻止输出被广播。这很重要,因为我们只希望每个输出具有一个输入值。聚合多个输入值是一个需要特殊处理的约简操作。虽然会因为迭代器标志未明确启用约简而引发错误,但其结果仅限于禁用广播的错误消息对最终用户更加易懂。要查看如何将平方函数推广为约简函数,请查看有关 Cython 部分中的平方和函数。

为了完整起见,我们还将添加‘external_loop’和‘buffered’标志,因为出于性能原因,这些通常是您想要的。

示例

>>> def square(a, out=None):
...     it = np.nditer([a, out],
...             flags = ['external_loop', 'buffered'],
...             op_flags = [['readonly'],
...                         ['writeonly', 'allocate', 'no_broadcast']])
...     with it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
... 
>>> square([1,2,3])
array([1, 4, 9]) 
>>> b = np.zeros((3,))
>>> square([1,2,3], out=b)
array([1.,  4.,  9.])
>>> b
array([1.,  4.,  9.]) 
>>> square(np.arange(6).reshape(2,3), out=b)
Traceback (most recent call last):
  ...
ValueError: non-broadcastable output operand with shape (3,) doesn't
match the broadcast shape (2,3) 

外积迭代

任何二进制操作都可以像outer中一样以外积方式扩展为数组操作,nditer对象通过显式映射操作数的轴提供了一种实现这一点的方法。也可以使用newaxis索引来完成这个操作,但我们将向您展示如何直接使用 nditer 的op_axes参数来在没有中间视图的情况下完成此操作。

我们将进行一个简单的外积计算,将第一个操作数的维度放在第二个操作数的维度之前。op_axes参数需要为每个操作数提供一个包含轴的列表,并提供了一个从迭代器的轴到操作数的轴的映射。

假设第一个操作数是一维的,第二个操作数是二维的。迭代器将具有三个维度,因此op_axes将有两个包含三个元素的列表。第一个列表选择第一个操作数的一个轴,其余迭代器轴为-1,最终结果为[0, -1, -1]。第二个列表选择第二个操作数的两个轴,但不应与第一个操作数中选择的轴重叠。它的列表是[-1, 0, 1]。输出操作数按照标准方式映射到迭代器轴上,因此我们可以提供 None 而不是构造另一个列表。

在内部循环中的操作是直接乘法。所有外积相关的事情都由迭代器设置处理。

示例

>>> a = np.arange(3)
>>> b = np.arange(8).reshape(2,4)
>>> it = np.nditer([a, b, None], flags=['external_loop'],
...             op_axes=[[0, -1, -1], [-1, 0, 1], None])
>>> with it:
...     for x, y, z in it:
...         z[...] = x*y
...     result = it.operands[2]  # same as z
...
>>> result
array([[[ 0,  0,  0,  0],
 [ 0,  0,  0,  0]],
 [[ 0,  1,  2,  3],
 [ 4,  5,  6,  7]],
 [[ 0,  2,  4,  6],
 [ 8, 10, 12, 14]]]) 

请注意,一旦迭代器关闭,我们就无法访问operands,而必须使用在上下文管理器中创建的引用。

约简迭代

每当可写操作数的元素少于完整迭代空间时,该操作数正在进行约简。nditer对象要求任何约简操作数都被标记为读写,并且仅在提供‘reduce_ok’作为迭代器标志时允许约简。

以简单的例子为例,考虑计算数组中所有元素的和。

例子

>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
>>> with np.nditer([a, b], flags=['reduce_ok'],
...                     op_flags=[['readonly'], ['readwrite']]) as it:
...     for x,y in it:
...         y[...] += x
...
>>> b
array(276)
>>> np.sum(a)
276 

当组合约简和分配操作数时,情况变得有点棘手。在开始迭代之前,任何约简操作数必须初始化为其起始值。以下是我们如何做到这一点,对a的最后一个轴进行求和。

例子

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]])
>>> np.sum(a, axis=2)
array([[ 6, 22, 38],
 [54, 70, 86]]) 

进行缓冲进行约简需要在设置期间进行另一个调整。通常,迭代器构造涉及将可读数组的第一个缓冲区复制到缓冲区中。任何约简操作数都是可读的,所以它可能被读入缓冲区。不幸的是,在这个缓冲操作完成后初始化操作数的值将不会反映在迭代开始时的缓冲区中,将产生垃圾结果。

迭代器标志“delay_bufalloc”用于允许迭代器分配的约简操作数与缓冲共存。设置此标志时,迭代器将保留其缓冲区,直到接收到重置信号,之后将准备好进行常规迭代。如果我们同时启用缓冲,那么前面的例子看起来是这样的。

例子

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok',
...                                  'buffered', 'delay_bufalloc'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     it.reset()
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]]) 

将内部循环放入 Cython 中

那些希望从他们的低级操作中获得真正好的性能的人应该强烈考虑直接使用 C 提供的迭代 API,但对于那些不熟悉 C 或 C++的人来说,Cython 是一个性能合理的中间选择。对于nditer对象,这意味着让迭代器负责广播、数据类型转换和缓冲,同时将内部循环交给 Cython 处理。

举个例子,我们将创建一个平方和函数。首先,让我们在简单的 Python 中实现这个函数。我们想要支持一个类似于 numpy sum 函数的‘axis’参数,因此我们需要为op_axes参数构建一个列表。这是看起来的样子。

例子

>>> def axis_to_axeslist(axis, ndim):
...     if axis is None:
...         return [-1] * ndim
...     else:
...         if type(axis) is not tuple:
...             axis = (axis,)
...         axeslist = [1] * ndim
...         for i in axis:
...             axeslist[i] = -1
...         ax = 0
...         for i in range(ndim):
...             if axeslist[i] != -1:
...                 axeslist[i] = ax
...                 ax += 1
...         return axeslist
...
>>> def sum_squares_py(arr, axis=None, out=None):
...     axeslist = axis_to_axeslist(axis, arr.ndim)
...     it = np.nditer([arr, out], flags=['reduce_ok',
...                                       'buffered', 'delay_bufalloc'],
...                 op_flags=[['readonly'], ['readwrite', 'allocate']],
...                 op_axes=[None, axeslist],
...                 op_dtypes=['float64', 'float64'])
...     with it:
...         it.operands[1][...] = 0
...         it.reset()
...         for x, y in it:
...             y[...] += x*x
...         return it.operands[1]
...
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_py(a)
array(55.)
>>> sum_squares_py(a, axis=-1)
array([  5.,  50.]) 

要将这个函数 Cython 化,我们将内部循环(y[…] += x*x)替换为针对 float64 数据类型专门化的 Cython 代码。启用‘external_loop’标志后,提供给内部循环的数组将始终是一维的,因此几乎不需要进行太多的检查。

这是 sum_squares.pyx 的代码清单:

import numpy as np
cimport numpy as np
cimport cython

def axis_to_axeslist(axis, ndim):
    if axis is None:
        return [-1] * ndim
    else:
        if type(axis) is not tuple:
            axis = (axis,)
        axeslist = [1] * ndim
        for i in axis:
            axeslist[i] = -1
        ax = 0
        for i in range(ndim):
            if axeslist[i] != -1:
                axeslist[i] = ax
                ax += 1
        return axeslist

@cython.boundscheck(False)
def sum_squares_cy(arr, axis=None, out=None):
    cdef np.ndarray[double] x
    cdef np.ndarray[double] y
    cdef int size
    cdef double value

    axeslist = axis_to_axeslist(axis, arr.ndim)
    it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
                                      'buffered', 'delay_bufalloc'],
                op_flags=[['readonly'], ['readwrite', 'allocate']],
                op_axes=[None, axeslist],
                op_dtypes=['float64', 'float64'])
    with it:
        it.operands[1][...] = 0
        it.reset()
        for xarr, yarr in it:
            x = xarr
            y = yarr
            size = x.shape[0]
            for i in range(size):
               value = x[i]
               y[i] = y[i] + value * value
        return it.operands[1] 

在这台机器上,将 .pyx 文件构建为一个模块如下,但你可能需要找一些 Cython 教程来告诉你系统配置的具体情况。:

$ cython sum_squares.pyx
$ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c 

从 Python 解释器运行这个函数会产生与我们原生的 Python/NumPy 代码相同的答案。

例子

>>> from sum_squares import sum_squares_cy 
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_cy(a) 
array(55.0)
>>> sum_squares_cy(a, axis=-1) 
array([  5.,  50.]) 

在 IPython 中进行一点时间测试显示,Cython 内部循环减少的开销和内存分配为我们提供了一个非常好的加速效果,比起直接的 Python 代码和使用 NumPy 内置的求和函数表达式要好得多。

>>> a = np.random.rand(1000,1000)

>>> timeit sum_squares_py(a, axis=-1)
10 loops, best of 3: 37.1 ms per loop

>>> timeit np.sum(a*a, axis=-1)
10 loops, best of 3: 20.9 ms per loop

>>> timeit sum_squares_cy(a, axis=-1)
100 loops, best of 3: 11.8 ms per loop

>>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1))
True

>>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1))
True 

单个数组迭代

使用nditer可以完成的最基本任务是访问数组的每个元素。使用标准的 Python 迭代器接口,每个元素会逐个提供。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a):
...     print(x, end=' ')
...
0 1 2 3 4 5 

对于这种迭代,需要注意的一点是迭代顺序是选择与数组的内存布局匹配,而不是使用标准的 C 或 Fortran 顺序。这样做是为了提高访问效率,反映了默认情况下,简单地希望访问每个元素而不关注特定的顺序的想法。我们可以通过迭代我们上一个数组的转置来看到这一点,与以 C 顺序复制该转置的方式进行比较。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
...     print(x, end=' ')
...
0 1 2 3 4 5 
>>> for x in np.nditer(a.T.copy(order='C')):
...     print(x, end=' ')
...
0 3 1 4 2 5 

aa.T的元素以相同的顺序遍历,即它们在内存中存储的顺序,而a.T.copy(order='C')的元素以不同的顺序访问,因为它们已经被放置在不同的内存布局中。

控制迭代顺序

有时候,重要的是按照特定的顺序访问数组的元素,而不论内存中元素的布局如何。nditer对象提供了一个order参数来控制迭代的这个方面。默认情况下,该参数的行为如上所述,为 order='K',保持现有顺序。可以使用 order='C'来强制使用 C 顺序,order='F'来强制使用 Fortran 顺序。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
...     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
...     print(x, end=' ')
...
0 3 1 4 2 5 

修改数组值

默认情况下,nditer将输入操作数视为只读对象。要能够修改数组元素,必须使用‘readwrite’‘writeonly’的每个操作数标志指定读写或只写模式。

nditer 将产生可写入的缓冲数组,您可以修改这些数组。然而,由于 nditer 必须在迭代结束时将该缓冲区数据复制回原始数组,因此您必须在迭代结束时发出信号,可以采用以下两种方法之一。可以选择:

  • 当使用with语句将nditer作为上下文管理器时,临时数据将在退出上下文时写回。
  • 迭代结束后,调用迭代器的close方法,将触发写回。

一旦调用close或退出上下文,nditer将无法再迭代。

示例

>>> a = np.arange(6).reshape(2,3)
>>> a
array([[0, 1, 2],
 [3, 4, 5]])
>>> with np.nditer(a, op_flags=['readwrite']) as it:
...    for x in it:
...        x[...] = 2 * x
...
>>> a
array([[ 0,  2,  4],
 [ 6,  8, 10]]) 

如果你正在编写需要支持旧版本 numpy 的代码,请注意在 1.15 之前,nditer不是上下文管理器,也没有close方法。相反,它依赖于析构函数来启动缓冲区的写回。

使用外部循环

到目前为止的所有示例中,迭代器一次只提供一个元素,因为所有循环逻辑都是迭代器内部的。虽然这很简单和方便,但效率不高。更好的方法是将一维最内层循环移到你的代码中,外部超过迭代器。这样,NumPy 的向量化操作可以用于正在访问的元素的更大块。

nditer将尝试为内部循环提供尽可能大的块。强制使用‘C’和‘F’顺序时,我们得到不同的外部循环大小。可以通过指定迭代器标志来启用此模式。

注意,默认保留本机内存顺序,迭代器能够提供单个一维块,而强制 Fortran 顺序时,它必须提供三个每个两个元素的块。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop']):
...     print(x, end=' ')
...
[0 1 2 3 4 5] 
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 

跟踪索引或多索引

在迭代过程中,您可能希望在计算中使用当前元素的索引。例如,您可能希望以内存顺序访问数组的元素,但使用 C 顺序、Fortran 顺序或多维索引来查找不同数组中的值。

索引由迭代器对象本身跟踪,并通过indexmulti_index属性访问,具体取决于所请求的内容。下面的示例显示了打印输出,用于演示索引的变化:

示例

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> for x in it:
...     print("%d <%d>" % (x, it.index), end=' ')
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> for x in it:
...     print("%d <%s>" % (x, it.multi_index), end=' ')
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     for x in it:
...         x[...] = it.multi_index[1] - it.multi_index[0]
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

跟踪索引或多索引与使用外部循环不兼容,因为每个元素需要不同的索引值。如果尝试结合这些标志,nditer对象将引发异常。

示例

>>> a = np.zeros((2,3))
>>> it = np.nditer(a, flags=['c_index', 'external_loop'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Iterator flag EXTERNAL_LOOP cannot be used if an index or multi-index is being tracked 

替代循环和元素访问

为了在迭代过程中更容易地使用其属性,nditer具有一种替代语法,可以显式地使用迭代器对象本身进行迭代。使用这种循环结构,可以通过对迭代器进行索引来访问当前值。其他属性,如跟踪的索引与之前一样。下面的示例产生与前一节中相同的结果。

示例

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> while not it.finished:
...     print("%d <%d>" % (it[0], it.index), end=' ')
...     is_not_finished = it.iternext()
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> while not it.finished:
...     print("%d <%s>" % (it[0], it.multi_index), end=' ')
...     is_not_finished = it.iternext()
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     while not it.finished:
...         it[0] = it.multi_index[1] - it.multi_index[0]
...         is_not_finished = it.iternext()
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

缓冲数组元素

在强制迭代顺序时,我们观察到外部循环选项可能会以较小的块提供元素,因为元素不能以恒定的步长以恰当的顺序访问。在编写 C 代码时,这通常没问题,然而在纯 Python 代码中,这可能会导致性能显著下降。

通过启用缓冲模式,迭代器传递给内部循环的块可以更大,大大减少了 Python 解释器的开销。在例子中强制 Fortran 迭代顺序时,启用缓冲时,内部循环可以一次性看到所有元素。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 
>>> for x in np.nditer(a, flags=['external_loop','buffered'], order='F'):
...     print(x, end=' ')
...
[0 3 1 4 2 5] 

作为特定数据类型迭代

有时候需要将数组视为与其存储的不同数据类型。例如,即使被操作的数组是 32 位浮点数,可以要求在所有计算中都使用 64 位浮点数进行。除非编写低级 C 代码,通常最好让迭代器处理拷贝或缓冲,而不是在内部循环中自己进行数据类型转换。

有两种机制可以实现这个目标,即临时拷贝和缓冲模式。使用临时拷贝,会使用新的数据类型创建整个数组的副本,然后对副本进行迭代。在所有迭代完成后,可以通过一种模式对原始数组进行写入访问。临时拷贝的主要缺点是,如果迭代数据类型的 itemsize 大于原始数据类型的 itemsize,临时拷贝可能会消耗大量内存。

缓冲模式可以减少内存使用,并且比临时拷贝更加高效。除了一些特殊情况,例如在迭代器外部需要使用整个数组时,建议使用缓冲模式代替临时拷贝。在 NumPy 中,缓冲模式被 ufuncs 和其他函数用于支持灵活输入,并且内存开销很小。

在我们的示例中,我们将使用复杂数据类型处理输入数组,以便我们可以对负数进行平方根运算。如果不启用拷贝或缓冲模式,迭代器将在数据类型不精确匹配时引发异常。

示例

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand required copying or buffering, but neither copying nor buffering was enabled 

在拷贝模式下,可以通过每个操作数的标志“copy”来指定。这样可以提供按操作数的方式进行控制。缓冲模式可以通过一个迭代器标志来指定。

示例

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_flags=['readonly','copy'],
...                 op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 

迭代器使用 NumPy 的类型转换规则来确定是否允许特定的转换。默认情况下,它实施“safe”类型转换。这意味着,例如,如果尝试将 64 位浮点数组视为 32 位浮点数组,它将引发异常。在许多情况下,“same_kind”规则是最合理的规则,因为它允许从 64 位转换为 32 位浮点数,但不允许从浮点数转换为整数或从复数转换为浮点数。

示例

>>> a = np.arange(6.)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32']):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('float32') according to the rule 'safe' 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32'],
...                 casting='same_kind'):
...     print(x, end=' ')
...
0.0 1.0 2.0 3.0 4.0 5.0 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['int32'], casting='same_kind'):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int32') according to the rule 'same_kind' 

使用读写或只写操作数进行转换回原始数据类型时需注意。常见的情况是使用 64 位浮点数实现内部循环,并使用“same_kind”类型转换以便处理其他浮点数类型。虽然在只读模式下可以提供整数数组,但在读写模式下将引发异常,因为转换回数组将违反类型转换规则。

示例

>>> a = np.arange(6)
>>> for x in np.nditer(a, flags=['buffered'], op_flags=['readwrite'],
...                 op_dtypes=['float64'], casting='same_kind'):
...     x[...] = x / 2.0
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Iterator requested dtype could not be cast from dtype('float64') to dtype('int64'), the operand 0 dtype, according to the rule 'same_kind' 

控制迭代顺序

有时重要的是以特定顺序访问数组的元素,不管元素在内存中的布局如何。nditer 对象提供了一个order参数来控制迭代的这个方面。默认情况下,保持上述行为的是 order=’K’以保持现有顺序。可以用 order=’C’覆盖为 C 顺序和 order=’F’为 Fortran 顺序。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
...     print(x, end=' ')
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
...     print(x, end=' ')
...
0 3 1 4 2 5 

修改数组值

默认情况下,nditer 将输入操作数视为只读对象。要能够修改数组元素,必须使用‘readwrite’‘writeonly’逐操作数标志指定读写或仅写模式。

然后 nditer 将生成可写入的缓冲区数组,您可以修改。然而,由于 nditer 在迭代结束后必须将此缓冲区数据复制回原始数组,因此必须通过两种方法之一来信号迭代结束。您可以:

  • 使用with语句将 nditer 作为上下文管理器使用,并且在退出上下文时将临时数据写回。
  • 在迭代完成后调用迭代器的close方法,这将触发写回。

一旦调用close或退出其上下文,nditer 将不再可迭代。

示例

>>> a = np.arange(6).reshape(2,3)
>>> a
array([[0, 1, 2],
 [3, 4, 5]])
>>> with np.nditer(a, op_flags=['readwrite']) as it:
...    for x in it:
...        x[...] = 2 * x
...
>>> a
array([[ 0,  2,  4],
 [ 6,  8, 10]]) 

如果您正在编写需要支持较旧版本的 numpy 的代码,请注意,在 1.15 之前,nditer 不是上下文管理器,也没有close方法。相反,它依赖析构函数来启动缓冲区的写回。

使用外部循环

到目前为止,所有示例中,a 中的元素都是由迭代器逐个提供的,因为所有循环逻辑都是迭代器内部的。虽然这种方法简单方便,但并不是很高效。更好的方法是将一维的内部循环移动到您的代码中,即在迭代器外部。这样,NumPy 的向量化操作可以用于正在访问的元素的更大块。

nditer 将尝试提供尽可能大的块给内部循环。通过强制‘C’和‘F’顺序,我们得到不同的外部循环大小。通过指定迭代器标志来启用此模式。

请注意,保持本机内存顺序的默认设置下,迭代器可以提供一个单一的一维块,而当强制 Fortran 顺序时,它必须提供三个每个两个元素的块。

示例

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop']):
...     print(x, end=' ')
...
[0 1 2 3 4 5] 
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 

跟踪一个索引或多维索引

在迭代过程中,您可能希望在计算中使用当前元素的索引。例如,您可能希望按内存顺序访问数组的元素,但使用 C 顺序、Fortran 顺序或多维索引来查找不同数组中的值。

索引由迭代器对象本身跟踪,并可以通过 indexmulti_index 属性访问,具体取决于所请求的内容。下面的示例展示了打印输出,以演示索引的进展情况:

例子

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> for x in it:
...     print("%d <%d>" % (x, it.index), end=' ')
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> for x in it:
...     print("%d <%s>" % (x, it.multi_index), end=' ')
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     for x in it:
...         x[...] = it.multi_index[1] - it.multi_index[0]
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

跟踪索引或多索引不兼容于使用外部循环,因为它需要每个元素一个不同的索引值。如果尝试结合这些标志,则nditer对象将引发异常。

例子

>>> a = np.zeros((2,3))
>>> it = np.nditer(a, flags=['c_index', 'external_loop'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Iterator flag EXTERNAL_LOOP cannot be used if an index or multi-index is being tracked 

替代循环和元素访问

为了在迭代过程中更容易访问其属性,nditer提供了另一种迭代的语法,它直接与迭代器对象本身一起工作。使用这种循环结构,可以通过对迭代器进行索引来访问当前值。其他属性,如跟踪的索引,保持不变。下面的示例生成了与前一节中相同的结果。

例子

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> while not it.finished:
...     print("%d <%d>" % (it[0], it.index), end=' ')
...     is_not_finished = it.iternext()
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> 
>>> it = np.nditer(a, flags=['multi_index'])
>>> while not it.finished:
...     print("%d <%s>" % (it[0], it.multi_index), end=' ')
...     is_not_finished = it.iternext()
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> 
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
...     while not it.finished:
...         it[0] = it.multi_index[1] - it.multi_index[0]
...         is_not_finished = it.iternext()
...
>>> a
array([[ 0,  1,  2],
 [-1,  0,  1]]) 

缓冲数组元素

在强制迭代顺序时,我们观察到外循环选项可能会以较小的块提供元素,因为元素不能以恒定的步幅按适当的顺序访问。在编写 C 代码时,这通常没问题,但在纯 Python 代码中,这可能会导致性能显著下降。

通过启用缓冲模式,可以使迭代器提供给内循环的块变得更大,从而显著降低 Python 解释器的开销。例如,强制使用 Fortran 迭代顺序时,当启用缓冲时,内循环可以一次性看到所有元素。

例子

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print(x, end=' ')
...
[0 3] [1 4] [2 5] 
>>> for x in np.nditer(a, flags=['external_loop','buffered'], order='F'):
...     print(x, end=' ')
...
[0 3 1 4 2 5] 

作为特定数据类型进行迭代

有时需要将数组视为存储时的不同数据类型。例如,即使被操作的数组是 32 位浮点数,也可能希望在 64 位浮点数上进行所有计算。除了编写低级 C 代码时,通常最好让迭代器处理复制或缓冲,而不是在内部循环中自己转换数据类型。

有两种机制可以实现这个目标,即临时副本和缓冲模式。使用临时副本时,会对整个数组进行新数据类型的复制,然后在副本上进行迭代。在迭代完成后,可以通过一种更新原数组的模式进行写入。临时副本的主要缺点在于,如果迭代数据类型的 itemsize 大于原始数据类型,临时副本可能会消耗大量内存。

缓冲模式缓解了内存使用问题,并且比制作临时副本更符合缓存的工作方式。除了一些特殊情况,需要在迭代器之外一次性使用整个数组时,建议使用缓冲而不是临时复制。在 NumPy 中,ufuncs 和其他函数使用缓冲支持具有最小内存开销的灵活输入。

在我们的例子中,我们将处理具有复杂数据类型的输入数组,以便我们可以对负数取平方根。如果数据类型不精确匹配,迭代器将在不启用复制或缓冲模式时引发异常。

例子

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand required copying or buffering, but neither copying nor buffering was enabled 

在复制模式中,‘copy’被指定为每个操作数的标志。这样做是为了以每个操作数的方式来提供控制。缓冲模式被指定为迭代器标志。

例子

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_flags=['readonly','copy'],
...                 op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']):
...     print(np.sqrt(x), end=' ')
...
1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) 

迭代器使用 NumPy 的转换规则来确定特定转换是否被允许。默认情况下,它执行‘safe’转换。这意味着,例如,如果尝试将 64 位浮点数组视为 32 位浮点数组,它会引发异常。在许多情况下,‘same_kind’规则是最合理的规则,因为它允许从 64 位转换为 32 位浮点,但不允许从浮点转换为整数或从复数转换为浮点。

例子

>>> a = np.arange(6.)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32']):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('float32') according to the rule 'safe' 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32'],
...                 casting='same_kind'):
...     print(x, end=' ')
...
0.0 1.0 2.0 3.0 4.0 5.0 
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['int32'], casting='same_kind'):
...     print(x, end=' ')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int32') according to the rule 'same_kind' 

要注意的一点是当使用可读写或仅写入操作数时,将数据类型转换回原始数据类型。一个常见情况是根据 64 位浮点实现内部循环,并使用‘same_kind’转换来允许处理其他浮点类型。在只读模式下,可以提供一个整数数组,而在读写模式下,将引发异常,因为转换回数组将违反转换规则。

例子

>>> a = np.arange(6)
>>> for x in np.nditer(a, flags=['buffered'], op_flags=['readwrite'],
...                 op_dtypes=['float64'], casting='same_kind'):
...     x[...] = x / 2.0
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Iterator requested dtype could not be cast from dtype('float64') to dtype('int64'), the operand 0 dtype, according to the rule 'same_kind' 

广播数组迭代

NumPy 有一组规则来处理具有不同形状的数组,这些规则适用于函数接受多个操作数并进行逐元素结合时。这被称为广播。当需要编写这样一个函数时,nditer 对象可以为您应用这些规则。

举个例子,我们打印出将一维和二维数组进行广播的结果。

例子

>>> a = np.arange(3)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print("%d:%d" % (x,y), end=' ')
...
0:0 1:1 2:2 0:3 1:4 2:5 

当发生广播错误时,迭代器会引发异常,其中包含输入形状以帮助诊断问题。

例子

>>> a = np.arange(2)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print("%d:%d" % (x,y), end=' ')
...
Traceback (most recent call last):
...
ValueError: operands could not be broadcast together with shapes (2,) (2,3) 

迭代器分配的输出数组

在 NumPy 函数中的一个常见情况是根据输入的广播分配输出,并且还有一个可选参数称为 ‘out’,为提供结果时提供位置。nditer对象提供了一个方便的习语,使得支持这一机制变得非常容易。

通过创建一个名为square的函数来展示它是如何工作的,该函数的作用是对输入进行平方。让我们从一个最简的函数定义开始,不包括‘out’参数支持。

例子

>>> def square(a):
...     with np.nditer([a, None]) as it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9]) 

默认情况下,nditer 对传入的 None 作为操作数使用‘allocate’和‘writeonly’标志。这意味着我们只需为迭代器提供这两个操作数,它会处理其余的。

当添加“out”参数时,我们必须明确提供这些标志,因为如果有人将数组作为“out”传入,则迭代器将默认为“readonly”,我们的内部循环将失败。 “readonly” 是输入数组的默认值的原因是为了防止意外触发缩减操作而引起混淆。 如果默认值为“readwrite”,任何广播操作也将触发缩减操作,这是本文档后面将涉及的一个主题。

在此之际,让我们也引入“no_broadcast”标志,这将阻止输出进行广播。 这很重要,因为我们只想要每个输出一个输入值。 聚合多个输入值是一个需要特殊处理的缩减操作。 它已经会引发错误,因为缩减必须在迭代器标志中明确启用,但是由于禁用广播而产生的错误消息对终端用户来说更容易理解。 要了解如何将平方函数推广到缩减,请查看有关 Cython 部分中的平方和函数。

为了完整起见,我们还将添加“external_loop”和“buffered”标志,因为出于性能原因,这通常是您所需要的。

示例

>>> def square(a, out=None):
...     it = np.nditer([a, out],
...             flags = ['external_loop', 'buffered'],
...             op_flags = [['readonly'],
...                         ['writeonly', 'allocate', 'no_broadcast']])
...     with it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
... 
>>> square([1,2,3])
array([1, 4, 9]) 
>>> b = np.zeros((3,))
>>> square([1,2,3], out=b)
array([1.,  4.,  9.])
>>> b
array([1.,  4.,  9.]) 
>>> square(np.arange(6).reshape(2,3), out=b)
Traceback (most recent call last):
  ...
ValueError: non-broadcastable output operand with shape (3,) doesn't
match the broadcast shape (2,3) 

外积迭代

任何二元操作都可以以类似于 outer 中的外积方式扩展为数组操作,并且 nditer 对象通过显式映射操作数的轴提供了一种实现此目的的方法。 也可以使用 newaxis 索引来完成此操作,但我们将向您展示如何直接使用 nditer 的 op_axes 参数来完成此操作,而无需中间视图。

我们将执行一个简单的外积,将第一个操作数的维度放在第二个操作数的维度之前。 op_axes 参数需要每个操作数的一个轴列表,并提供迭代器轴到操作数轴的映射。

假设第一个操作数是一维的,第二个操作数是二维的。 迭代器将有三个维度,因此 op_axes 将有两个 3 元素列表。 第一个列表选择第一个操作数的一个轴,并且对于迭代器的其余轴,它为 -1,最终结果为 [0, -1, -1]。 第二个列表选择第二个操作数的两个轴,但不应与第一个操作数中选择的轴重叠。 其列表为 [-1, 0, 1]。 输出操作数以标准方式映射到迭代器轴,因此我们可以提供 None 而不是构造另一个列表。

内循环中的操作是直接的乘法。 关于外积的所有内容都由迭代器设置处理。

示例

>>> a = np.arange(3)
>>> b = np.arange(8).reshape(2,4)
>>> it = np.nditer([a, b, None], flags=['external_loop'],
...             op_axes=[[0, -1, -1], [-1, 0, 1], None])
>>> with it:
...     for x, y, z in it:
...         z[...] = x*y
...     result = it.operands[2]  # same as z
...
>>> result
array([[[ 0,  0,  0,  0],
 [ 0,  0,  0,  0]],
 [[ 0,  1,  2,  3],
 [ 4,  5,  6,  7]],
 [[ 0,  2,  4,  6],
 [ 8, 10, 12, 14]]]) 

请注意,一旦迭代器被关闭,我们就无法访问operands,必须使用在上下文管理器中创建的引用。

减少迭代

每当可写操作数的元素少于完整迭代空间时,该操作数都将经历一个减少。nditer对象要求任何减少操作数标记为读写,并且只允许在提供‘reduce_ok’作为迭代器标志时进行减少。

举个简单的例子,考虑对数组中所有元素进行求和。

例如

>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
>>> with np.nditer([a, b], flags=['reduce_ok'],
...                     op_flags=[['readonly'], ['readwrite']]) as it:
...     for x,y in it:
...         y[...] += x
...
>>> b
array(276)
>>> np.sum(a)
276 

将缩减和分配的操作数组合时会有一些复杂。在开始迭代之前,任何减少操作数都必须被初始化为其开始值。以下是我们如何做到这一点,对a的最后一个轴求和。

例如

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]])
>>> np.sum(a, axis=2)
array([[ 6, 22, 38],
 [54, 70, 86]]) 

要做缓冲减少需要在设置期间进行另一个调整。通常,迭代器构造涉及从可读数组中复制数据的第一个缓冲区。任何减少操作数都是可读取的,因此它可以被读取到缓冲区中。不幸的是,在这个缓冲操作完成之后初始化操作数不会反映在迭代开始时的缓冲区中,并且将产生垃圾结果。

迭代器标志“delay_bufalloc”是为了允许迭代器分配的缩减操作数与缓冲存在在一起。当设置了这个标志时,迭代器将保持其缓冲区未初始化,直到收到重置信号,之后它将准备好进行常规迭代。如果我们还启用缓冲,前面的示例将如下所示。

例如

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok',
...                                  'buffered', 'delay_bufalloc'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     it.reset()
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]]) 

迭代器分配的输出数组

在 NumPy 函数中的一个常见情况是,根据输入的广播来分配输出,并且还有一个名为‘out’的可选参数,在提供结果时将其放置在那里。nditer对象提供了一个方便的习语,使得支持这种机制非常容易。

我们将通过创建一个函数square来展示这是如何工作的,它会对其输入进行平方操作。让我们从一个排除‘out’参数支持的最小函数定义开始。

例如

>>> def square(a):
...     with np.nditer([a, None]) as it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9]) 

默认情况下,nditer对于以 None 传入的操作数使用了‘allocate’和‘writeonly’标志。这意味着我们只需向迭代器提供两个操作数,剩下的交给它处理。

当添加‘out’参数时,必须明确提供这些标志,因为如果有人将数组传递给‘out’,迭代器将默认为‘readonly’,而我们的内部循环将失败。输入数组的默认值为‘readonly’是为了防止意外触发减少操作产生混淆。如果默认值是‘readwrite’,任何广播操作也会触发减少操作,这个主题稍后在本文档中讨论。

在这个过程中,让我们也介绍‘no_broadcast’标志,它将阻止输出被广播。这很重要,因为我们只想要每个输出一个输入值。聚合多于一个输入值是一个需要特殊处理的减少操作。尽管因为减少操作必须在迭代器标志中显式启用,这会引发错误,但禁用广播导致的错误消息对最终用户来说更容易理解。要了解如何将平方函数推广为减少函数,请参阅有关 Cython 的平方和函数的部分。

为了完整起见,我们还将添加‘external_loop’和‘buffered’标志,因为这通常是出于性能原因。

例子

>>> def square(a, out=None):
...     it = np.nditer([a, out],
...             flags = ['external_loop', 'buffered'],
...             op_flags = [['readonly'],
...                         ['writeonly', 'allocate', 'no_broadcast']])
...     with it:
...         for x, y in it:
...             y[...] = x*x
...         return it.operands[1]
... 
>>> square([1,2,3])
array([1, 4, 9]) 
>>> b = np.zeros((3,))
>>> square([1,2,3], out=b)
array([1.,  4.,  9.])
>>> b
array([1.,  4.,  9.]) 
>>> square(np.arange(6).reshape(2,3), out=b)
Traceback (most recent call last):
  ...
ValueError: non-broadcastable output operand with shape (3,) doesn't
match the broadcast shape (2,3) 

外部积迭代

任何二进制操作都可以像outer中那样以外部积方式扩展为数组操作,而nditer对象通过显式映射操作数的轴提供了一种方法。还可以通过newaxis索引来做到这一点,但我们将向您展示如何直接使用 nditer 的 op_axes 参数来实现这一点,而不需要中间视图。

我们将进行一个简单的外部积,将第一个操作数的维度放在第二个操作数的维度之前。op_axes 参数需要为每个操作数提供一个轴列表,并且提供了一个从迭代器的轴到操作数的轴的映射。

假设第一个操作数是一维的,而第二个操作数是二维的。迭代器将有三个维度,因此 op_axes 将有两个包含三个元素的列表。第一个列表选择第一个操作数的一个轴,并且对于迭代器的其他轴,为-1,最终结果是[0,-1,-1]。第二个列表选择第二个操作数的两个轴,但不应与第一个操作数选择的轴重叠。它的列表是[-1,0,1]。输出操作数以标准方式映射到迭代器轴,因此我们可以使用 None 代替构造另一个列表。

内循环中的操作是直接的乘法。与外部积有关的所有事情都由迭代器设置处理。

例子

>>> a = np.arange(3)
>>> b = np.arange(8).reshape(2,4)
>>> it = np.nditer([a, b, None], flags=['external_loop'],
...             op_axes=[[0, -1, -1], [-1, 0, 1], None])
>>> with it:
...     for x, y, z in it:
...         z[...] = x*y
...     result = it.operands[2]  # same as z
...
>>> result
array([[[ 0,  0,  0,  0],
 [ 0,  0,  0,  0]],
 [[ 0,  1,  2,  3],
 [ 4,  5,  6,  7]],
 [[ 0,  2,  4,  6],
 [ 8, 10, 12, 14]]]) 

请注意,一旦迭代器关闭,我们就无法访问 operands ,必须使用在上下文管理器内创建的引用。

归约迭代

每当可写操作数的元素少于完整迭代空间时,该操作数正在进行归约。 nditer 对象要求任何归约操作数都必须标记为读写,只有在提供“reduce_ok”作为迭代器标志时才允许进行归约。

举个简单的例子,考虑对数组中所有元素求和。

示例

>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
>>> with np.nditer([a, b], flags=['reduce_ok'],
...                     op_flags=[['readonly'], ['readwrite']]) as it:
...     for x,y in it:
...         y[...] += x
...
>>> b
array(276)
>>> np.sum(a)
276 

在合并归约和分配操作数时,事情变得有些棘手。在启动迭代之前,任何归约操作数都必须初始化为其起始值。这是我们如何做到这一点的,沿着 a 的最后一个轴求和。

示例

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]])
>>> np.sum(a, axis=2)
array([[ 6, 22, 38],
 [54, 70, 86]]) 

要进行缓冲归约需要在设置过程中进行另一个调整。通常,迭代器构建涉及将可读数组的第一个缓冲区数据复制到缓冲区中。任何归约操作数都是可读的,因此可能会被读入缓冲区。不幸的是,在此缓冲操作完成后初始化操作数不会反映在迭代开始时的缓冲区中,将产生垃圾结果。

迭代器标志“delay_bufalloc”允许迭代器分配的归约操作数与缓冲存在在一起。设置此标志后,迭代器将保持其缓冲区未初始化,直到收到重置之后,才会准备好进行常规迭代。如果我们还启用缓冲,前面的例子将如何呈现。

示例

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok',
...                                  'buffered', 'delay_bufalloc'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> with it:
...     it.operands[1][...] = 0
...     it.reset()
...     for x, y in it:
...         y[...] += x
...     result = it.operands[1]
...
>>> result
array([[ 6, 22, 38],
 [54, 70, 86]]) 

将内部循环放入 Cython 中

那些希望在低级操作中获得非常好性能的人应该认真考虑直接使用 C 中提供的迭代 API,但对于那些不熟悉 C 或 C++ 的人来说,Cython 是一个性能合理的好中间地带。对于 nditer 对象来说,这意味着让迭代器处理广播、数据类型转换和缓冲,同时将内部循环交给 Cython。

对于我们的示例,我们将创建一个求平方和的函数。首先,让我们在直接的 Python 中实现这个函数。我们想支持类似于 numpy sum 函数的 'axis' 参数,因此我们需要为 op_axes 参数构造一个列表。这是实现的方式。

示例

>>> def axis_to_axeslist(axis, ndim):
...     if axis is None:
...         return [-1] * ndim
...     else:
...         if type(axis) is not tuple:
...             axis = (axis,)
...         axeslist = [1] * ndim
...         for i in axis:
...             axeslist[i] = -1
...         ax = 0
...         for i in range(ndim):
...             if axeslist[i] != -1:
...                 axeslist[i] = ax
...                 ax += 1
...         return axeslist
...
>>> def sum_squares_py(arr, axis=None, out=None):
...     axeslist = axis_to_axeslist(axis, arr.ndim)
...     it = np.nditer([arr, out], flags=['reduce_ok',
...                                       'buffered', 'delay_bufalloc'],
...                 op_flags=[['readonly'], ['readwrite', 'allocate']],
...                 op_axes=[None, axeslist],
...                 op_dtypes=['float64', 'float64'])
...     with it:
...         it.operands[1][...] = 0
...         it.reset()
...         for x, y in it:
...             y[...] += x*x
...         return it.operands[1]
...
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_py(a)
array(55.)
>>> sum_squares_py(a, axis=-1)
array([  5.,  50.]) 

要将此函数进行 Cython-化,我们用针对 float64 数据类型专门设计的 Cython 代码替换内部循环(y […] += x*x)。启用 'external_loop' 标志后,提供给内部循环的数组始终是一维的,因此很少需要进行检查。

这里是 sum_squares.pyx 的代码清单:

import numpy as np
cimport numpy as np
cimport cython

def axis_to_axeslist(axis, ndim):
    if axis is None:
        return [-1] * ndim
    else:
        if type(axis) is not tuple:
            axis = (axis,)
        axeslist = [1] * ndim
        for i in axis:
            axeslist[i] = -1
        ax = 0
        for i in range(ndim):
            if axeslist[i] != -1:
                axeslist[i] = ax
                ax += 1
        return axeslist

@cython.boundscheck(False)
def sum_squares_cy(arr, axis=None, out=None):
    cdef np.ndarray[double] x
    cdef np.ndarray[double] y
    cdef int size
    cdef double value

    axeslist = axis_to_axeslist(axis, arr.ndim)
    it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
                                      'buffered', 'delay_bufalloc'],
                op_flags=[['readonly'], ['readwrite', 'allocate']],
                op_axes=[None, axeslist],
                op_dtypes=['float64', 'float64'])
    with it:
        it.operands[1][...] = 0
        it.reset()
        for xarr, yarr in it:
            x = xarr
            y = yarr
            size = x.shape[0]
            for i in range(size):
               value = x[i]
               y[i] = y[i] + value * value
        return it.operands[1] 

在这台机器上,将 .pyx 文件构建为一个模块看起来像下面这样,但是你可能需要找一些 Cython 教程来告诉你系统配置的具体信息。:

$ cython sum_squares.pyx
$ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c 

从 Python 解释器中运行这段代码会产生与我们本地 Python/NumPy 代码相同的答案。

例子

>>> from sum_squares import sum_squares_cy 
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_cy(a) 
array(55.0)
>>> sum_squares_cy(a, axis=-1) 
array([  5.,  50.]) 

在 IPython 中进行一点计时显示,Cython 内循环的减少开销和内存分配提供了对直接 Python 代码和使用 NumPy 内置 sum 函数的表达式都有很好的加速。:

>>> a = np.random.rand(1000,1000)

>>> timeit sum_squares_py(a, axis=-1)
10 loops, best of 3: 37.1 ms per loop

>>> timeit np.sum(a*a, axis=-1)
10 loops, best of 3: 20.9 ms per loop

>>> timeit sum_squares_cy(a, axis=-1)
100 loops, best of 3: 11.8 ms per loop

>>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1))
True

>>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1))
True 

标准数组子类

原文:numpy.org/doc/1.26/reference/arrays.classes.html

注意

numpy.ndarray 的子类化是可能的,但如果您的目标是创建具有修改行为的数组,如用于分布式计算的 Dask 数组和用于基于 GPU 的计算的 CuPy 数组,不建议子类化。相反,推荐使用 numpy 的调度机制。

可以继承 ndarray(在 Python 或 C 中)。因此,它可以成为许多有用类的基础。通常,是子类化数组对象还是只是将核心数组组件作为新类的内部部分的决定是一个困难的决定,只是一种选择。NumPy 有几种工具可简化新对象与其他数组对象的交互方式,因此最终选择可能并不重要。简化问题的一种方式是问自己您感兴趣的对象是否可以被单个数组替换或者是否它的核心真的需要两个或更多数组。

请注意,asarray 总是返回基类 ndarray。如果您确信您对数组对象的使用可以处理 ndarray 的任何子类,那么 asanyarray 可以用于允许子类更清晰地传播到您的子例程中。在原则上,子类可以重新定义数组的任何方面,因此,在严格的指导方针下,asanyarray 很少有用。但是,数组对象的大多数子类不会重新定义数组对象的某些方面,如缓冲区接口或数组的属性。然而,您的子例程可能无法处理数组的任意子类的一个重要示例是矩阵将“*”操作符重新定义为矩阵乘法,而不是逐元素乘法。

特殊属性和方法

另见

作为 ndarray 的子类

NumPy 提供了几个类可以自定义的钩子:

class.__array_ufunc__(ufunc, method, *inputs, **kwargs)

自版本 1.13 新功能。

任何类、ndarray 子类或非子类都可以定义此方法或将其设置为 None 以覆盖 NumPy 的 ufunc 的行为。这与 Python 的 __mul__ 和其他二进制操作例程的工作方式相似。

  • ufunc 是调用的 ufunc 对象。

  • method 是指调用了哪个 Ufunc 方法的字符串("__call__""reduce""reduceat""accumulate""outer""inner"之一)。

  • inputsufunc 的输入参数的元组。

  • kwargs是一个包含 ufunc 的可选输入参数的字典。如果提供,任何out参数,无论是位置参数还是关键字参数,都作为tuple传递给kwargs。有关详细信息,请参阅通用函数(ufunc)中的讨论。

该方法应该返回操作的结果,或者如果请求的操作未实现,则返回NotImplemented

如果输入、输出或where参数中的一个具有__array_ufunc__方法,那么将执行该方法而不是 ufunc。如果多个参数实现了__array_ufunc__,则按照以下顺序尝试:子类优先于超类,输入优先于输出,输出优先于where,否则从左到右。第一个返回不是NotImplemented的例程确定结果。如果所有的__array_ufunc__操作都返回NotImplemented,则会引发TypeError

注意

我们打算将 numpy 函数重新实现为(广义的)Ufunc,这样它们就可以被__array_ufunc__方法覆盖。一个主要的候选者是matmul,它目前不是一个 Ufunc,但可以相对容易地重写为(一组)广义 Ufuncs。与medianaminargsort等功能可能发生相同的情况。

与其他在 Python 中的一些特殊方法,如__hash____iter__一样,您可以通过设置__array_ufunc__ = None来指示您的类支持 ufuncs。当调用在设置了__array_ufunc__ = None的对象上时,ufuncs 总是会引发TypeError

__array_ufunc__的存在还会影响当arr是一个ndarrayobj是自定义类的实例时,ndarray如何处理arr + objarr < obj等二元操作。有两种可能性。如果obj.__array_ufunc__存在且不为None,那么ndarray.__add__等方法将委托给 ufunc 机制,意味着arr + obj变成了np.add(arr, obj),然后add调用obj.__array_ufunc__。如果你想定义一个像数组一样的对象,这非常有用。

或者,如果obj.__array_ufunc__被设置为None,那么作为特殊情况,像ndarray.__add__这样的特殊方法会注意到这一点,并无条件地引发TypeError。这在你想要创建通过二元操作与数组交互但本身不是数组的对象时很有用。例如,一个处理单位的系统可能有一个代表“米”单位的对象m,并且想要支持语法arr * m以表示数组具有“米”单位,但不希望通过 ufunc 或其他方式与数组交互。可以通过设置__array_ufunc__ = None并定义__mul____rmul__方法来实现这一点。(请注意,这意味着编写一个始终返回NotImplemented__array_ufunc__与设置__array_ufunc__ = None不完全相同:在前一种情况下,arr + obj将引发TypeError,而在后一种情况下,可以定义一个__radd__方法来防止这种情况发生。)

对于就地操作,ndarray不会返回NotImplemented,因此arr += obj总是会导致TypeError。这是因为对于数组来说,就地操作通常无法简单地替换为一个简单的反向操作。(例如,默认情况下,arr += obj将被转换为arr = arr + obj,即arr将被替换,与期望的就地数组操作相反。)

注意

如果你定义了__array_ufunc__

  • 如果你不是ndarray的子类,我们建议你的类定义特殊方法如__add____lt__,并像ndarray一样委托给 ufuncs。一个简单的方法是从NDArrayOperatorsMixin继承。

  • 如果你是ndarray的子类,我们建议你将所有的重载逻辑放在__array_ufunc__中,而不是同时重载特殊方法。这确保了类层次结构只在一个地方被确定,而不是由 ufunc 机制和二进制操作规则分别确定(后者优先考虑子类的特殊方法;确保层次结构只在一个地方被确定的替代方法是将__array_ufunc__设置为None,这可能看起来很意外并令人困惑,因为这样子类将完全无法与 ufuncs 一起使用)。

  • ndarray定义了自己的__array_ufunc__方法,如果没有参数被重载,则评估 ufunc,并且否则返回NotImplemented。这对于生成将自身类的任何实例转换为ndarray的子类可能有用:然后可以使用super().__array_ufunc__(*inputs, **kwargs)将这些传递给其超类,最终在可能的反向转换后返回结果。这种做法的优点在于确保能够拥有一个扩展行为的子类层次结构。请参阅 ndarray 的子类化了解详情。

注意

如果一个类定义了__array_ufunc__方法,这将禁用 ufuncs 的__array_wrap____array_prepare__、和__array_priority__机制(这些机制可能最终被弃用)。

class.__array_function__(func, types, args, kwargs)

在 1.16 版中新增。

  • func是 NumPy 公共 API 中暴露的任意可调用对象,以func(*args, **kwargs)的形式调用。

  • types是原始 NumPy 函数调用中实现__array_function__的唯一参数类型的集合collections.abc.Collection

  • 元组args和字典kwargs直接从原始调用中传递。

作为__array_function__实现者的便利,types提供了所有参数类型的'__array_function__'属性。这使得实现者能够快速识别出应该将其他参数的处理委托给__array_function__实现的情况。实现不应依赖于types的迭代顺序。

大多数__array_function__的实现将以两个检查开始:

  1. 给定的函数是否是我们知道如何重载的?

  2. 所有的参数都是我们知道如何处理的类型吗?

如果满足这些条件,__array_function__应该返回调用其实现函数func(*args, **kwargs)的结果。否则,应该返回标记值NotImplemented,表示这些类型未实现该函数。

对于__array_function__的返回值,没有一般性要求,尽管大多数明智的实现应该返回与函数一个参数类型相同的数组。

也可以方便地定义自定义装饰器(如下所示的implements)来注册__array_function__的实现。

HANDLED_FUNCTIONS = {}

class MyArray:
    def __array_function__(self, func, types, args, kwargs):
        if func not in HANDLED_FUNCTIONS:
            return NotImplemented
        # Note: this allows subclasses that don't override
        # __array_function__ to handle MyArray objects
        if not all(issubclass(t, MyArray) for t in types):
            return NotImplemented
        return HANDLED_FUNCTIONSfunc

def implements(numpy_function):
  """Register an __array_function__ implementation for MyArray objects."""
    def decorator(func):
        HANDLED_FUNCTIONS[numpy_function] = func
        return func
    return decorator

@implements(np.concatenate)
def concatenate(arrays, axis=0, out=None):
    ...  # implementation of concatenate for MyArray objects

@implements(np.broadcast_to)
def broadcast_to(array, shape):
    ...  # implementation of broadcast_to for MyArray objects 

注意,__array_function__的实现不需要包含所有对应的 NumPy 函数的可选参数(例如,上面的broadcast_to省略了无关的subok参数)。只有在 NumPy 函数调用中明确使用了可选参数时,才会将可选参数传递给__array_function__

就像内置特殊方法__add__的情况一样,正确编写的__array_function__方法在遇到未知类型时应该始终返回NotImplemented。否则,如果操作还包括您的对象之一,则无法正确地覆盖另一个对象的 NumPy 函数。

在大多数情况下,与__array_ufunc__的调度规则匹配的是__array_function__。特别是:

  • NumPy 将从所有指定的输入中收集__array_function__的实现,并按顺序调用它们:子类优先于超类,否则从左到右。请注意,在涉及子类的某些边缘情况下,这与 Python 的当前行为略有不同。

  • __array_function__的实现指示它们可以处理操作,方法是返回任何值,而不是NotImplemented

  • 如果所有的__array_function__方法都返回NotImplemented,NumPy 将引发TypeError

如果不存在__array_function__方法,NumPy 将默认调用其自己的实现,用于 NumPy 数组。例如,当所有类似数组的参数都是 Python 数字或列表时,会出现这种情况。(NumPy 数组确实有一个__array_function__方法,如下所示,但如果除了 NumPy 数组子类之外的任何参数都实现了__array_function__,它将始终返回NotImplemented。)

__array_ufunc__当前行为的一个偏差是,NumPy 仅会在每种唯一类型的第一个参数上调用__array_function__。这符合 Python 的调用反射方法的规则,这确保了在存在大量重载参数时检查重载的性能是可接受的。

class.__array_finalize__(obj)

当系统从objobjndarray的子类(子类型))内部分配新数组时,将调用此方法。它可用于在构造后更改self的属性(例如,以确保 2 维矩阵),或者从“父级”更新元信息。子类继承了此方法的默认实现,什么也不做。

class.__array_prepare__(array, context=None)

在每个 ufunc 的开头,会调用此方法来处理具有最高数组优先级的输入对象,或者指定的输出对象。输出数组被传递,而返回的任何内容都会传递给 ufunc。子类继承了此方法的默认实现,简单地返回未修改的输出数组。子类可以选择使用此方法将输出数组转换为子类的实例并在将数组返回给 ufunc 进行计算之前更新元数据。

对于 ufunc,希望最终弃用此方法,而转而使用__array_ufunc__

class.__array_wrap__(array, context=None)

在每个 ufunc 的结尾,会调用此方法来处理具有最高数组优先级的输入对象,或者指定的输出对象。ufunc 计算得到的数组被传递,而返回的任何内容都会传递给用户。子类继承了此方法的默认实现,将数组转换为对象类的新实例。子类可以选择使用此方法将输出数组转换为子类的实例并在返回数组给用户之前更新元数据。

对于 ufunc,希望最终弃用此方法,而转而使用__array_ufunc__

class.__array_priority__

此属性的值用于确定在返回对象的 Python 类型有多于一个可能性的情况下返回什么类型的对象。子类继承了此属性的默认值为 0.0。

对于 ufunc,希望最终弃用此方法,而转而使用__array_ufunc__

class.__array__([dtype])

如果一个类(不管是 ndarray 子类还是其他)具有 __array__ 方法,被用作 ufunc 的输出对象时,结果将不会被写入由 __array__ 返回的对象。这种做法会返回TypeError。 ## 矩阵对象

注意

强烈建议使用矩阵子类。如下所述,这使得编写函数,以一致地处理矩阵和常规数组变得非常困难。目前,它们主要用于与scipy.sparse的交互。然而,我们希望为这种用法提供一种替代方案,并最终移除matrix子类。

matrix 对象继承自 ndarray,因此它们具有与 ndarrays 相同的属性和方法。但是,matrix 对象有六个重要的不同之处,这可能会导致意外的结果,当你使用矩阵时,但期望它们的行为像数组:

  1. 可以使用字符串表示法创建矩阵对象,这样可以使用 Matlab 风格的语法,空格分隔列,分号(‘;’)分隔行。

  2. 矩阵对象始终是二维的。这有着深远的意义,因为 m.ravel() 仍然是二维的(在第一维有一个 1),并且条目选择返回二维对象,因此序列行为与数组根本不同。

  3. 矩阵对象覆盖了乘法,成为矩阵乘法。务必要理解这一点,特别是在你希望接受矩阵的函数中。尤其要注意的是,当 m 是一个矩阵时,asanyarray(m) 会返回一个矩阵。

  4. 矩阵对象覆盖了幂运算,成为矩阵的幂。对于使用 asanyarray(…) 获取数组对象的函数中使用幂运算的相同警告,也适用于这一事实。

  5. 矩阵对象的默认 array_priority 是 10.0,因此与 ndarrays 的混合操作始终产生矩阵。

  6. 矩阵具有使计算更简单的特殊属性。这些属性是

    matrix.T 返回矩阵的转置。
    matrix.H 返回自身的(复数)共轭转置。
    matrix.I 返回可逆自身的(乘法)逆。
    matrix.A ndarray 对象的形式返回自身

警告

矩阵对象重写了乘法 * 和幂运算 **,分别用于矩阵乘法和矩阵幂。如果你的子程序可以接受子类而且你没有转换为基类数组,那么你必须使用 ufuncs 的 multiply 和 power 来确保对所有输入执行正确的操作。

矩阵类是 ndarray 的 Python 子类,可用作构建自己的 ndarray 子类的参考。矩阵可以从其他矩阵、字符串和任何可转换为 ndarray 的东西创建。名称“mat”是 NumPy 中“matrix”的别名。

matrix(data[, dtype, copy])

注意

不再建议使用这个类,即使是用于线性

|

asmatrix(data[, dtype]) 将输入解释为矩阵。
bmat(obj[, ldict, gdict]) 从字符串、嵌套序列或数组构建一个矩阵对象。

示例 1:从字符串创建矩阵

>>> a = np.mat('1 2 3; 4 5 3')
>>> print((a*a.T).I)
 [[ 0.29239766 -0.13450292]
 [-0.13450292  0.08187135]] 

示例 2:从嵌套序列创建矩阵

>>> np.mat([[1,5,10],[1.0,3,4j]])
matrix([[  1.+0.j,   5.+0.j,  10.+0.j],
 [  1.+0.j,   3.+0.j,   0.+4.j]]) 

示例 3:从数组创建矩阵

>>> np.mat(np.random.rand(3,3)).T
matrix([[4.17022005e-01, 3.02332573e-01, 1.86260211e-01],
 [7.20324493e-01, 1.46755891e-01, 3.45560727e-01],
 [1.14374817e-04, 9.23385948e-02, 3.96767474e-01]]) 

内存映射文件数组

内存映射文件对于读取和/或修改具有常规布局的大文件的小段非常有用,而无需将整个文件读入内存。ndarray 的一个简单子类使用内存映射文件作为数组的数据缓冲区。对于小文件,将整个文件读入内存的开销通常不重要,但对于大文件,使用内存映射可以节省大量资源。

内存映射文件数组除了继承自 ndarray 的方法之外,还有一个额外的方法:.flush(),用户必须手动调用该方法以确保数组的任何更改实际上都被写入磁盘。

memmap(filename[, dtype, mode, offset, ...]) 创建一个映射到存储在磁盘上的二进制文件中的数组的内存映射。
memmap.flush() 将数组中的任何更改写入磁盘上的文件。

例子:

>>> a = np.memmap('newfile.dat', dtype=float, mode='w+', shape=1000)
>>> a[10] = 10.0
>>> a[30] = 30.0
>>> del a
>>> b = np.fromfile('newfile.dat', dtype=float)
>>> print(b[10], b[30])
10.0 30.0
>>> a = np.memmap('newfile.dat', dtype=float)
>>> print(a[10], a[30])
10.0 30.0 

字符数组(numpy.char

另请参阅

创建字符数组(numpy.char)

注意

chararray类是为了向后兼容 Numarray 而存在的,不建议用于新的开发。从 numpy 1.4 开始,如果需要字符串数组,建议使用dtype object_bytes_str_的数组,并使用numpy.char模块中的自由函数进行快速向量化字符串操作。

这些数组是增强型数组,可以是str_类型或bytes_类型。这些数组继承自ndarray,但特别定义了+*%操作,这些操作是以(广播)元素为基础的。这些操作在标准的字符类型的ndarray上不可用。此外,chararray具有所有标准的str (和bytes)方法,会在元素基础上执行它们。也许创建一个 chararray 最简单的方法是使用self.view(chararray),其中self是一个 str 或 unicode 数据类型的 ndarray。然而,chararray 也可以使用numpy.chararray构造函数,或通过numpy.char.array函数创建:

chararray(shape[, itemsize, unicode, ...]) 为字符串和 unicode 值的数组提供了一个方便的视图。
core.defchararray.array(obj[, itemsize, ...]) 创建一个chararray

与标准的字符串数据类型的 ndarray 的另一个不同之处是,chararray 继承了 Numarray 引入的特性,即数组中任何元素末尾的空白将在元素检索和比较操作中被忽略。

记录数组(numpy.rec

参见

创建记录数组(numpy.rec),数据类型例程,数据类型对象(dtype)。

NumPy 提供了 recarray 类,允许将结构化数组的字段作为属性访问,以及相应的标量数据类型对象 record

recarray(shape[, dtype, buf, offset, ...]) 构造一个允许使用属性进行字段访问的 ndarray。
record 允许通过属性查找进行字段访问的数据类型标量。

掩码数组 (numpy.ma)

另见

掩码数组

标准容器类

为了向后兼容并作为标准的“容器”类,将 Numeric 中的 UserArray 引入到 NumPy 并命名为 numpy.lib.user_array.container。容器类是一个 Python 类,其 self.array 属性是一个 ndarray。可能使用 numpy.lib.user_array.container 比直接使用 ndarray 自身更容易进行多重继承,因此默认情况下包含在内。这里不对其进行详细说明,仅提及其存在,因为鼓励您直接使用 ndarray 类。

numpy.lib.user_array.container(data[, ...]) 用于方便多重继承的标准容器类。

数组迭代器

迭代器是数组处理的一个强大概念。本质上,迭代器实现了一个广义的 for 循环。如果 myiter 是一个迭代器对象,则 Python 代码:

for val in myiter:
    ...
    some code involving val
    ... 

重复调用 val = next(myiter),直到迭代器引发 StopIteration。有几种可能有用的数组迭代方法:默认迭代、平面迭代和 (N)-维枚举。

默认迭代

ndarray 对象的默认迭代器是序列类型的默认 Python 迭代器。因此,当数组对象本身被用作迭代器时。默认行为相当于:

for i in range(arr.shape[0]):
    val = arr[i] 

此默认迭代器从数组中选择一个维度为 (N-1) 的子数组。这对定义递归算法是一个有用的构造。要循环遍历整个数组需要 (N) 个 for 循环。

>>> a = np.arange(24).reshape(3,2,4)+10
>>> for val in a:
...     print('item:', val)
item: [[10 11 12 13]
 [14 15 16 17]]
item: [[18 19 20 21]
 [22 23 24 25]]
item: [[26 27 28 29]
 [30 31 32 33]] 

平面迭代

ndarray.flat 数组上的 1-D 迭代器。

正如先前提到的,ndarray 对象的 flat 属性返回一个迭代器,该迭代器将以 C 风格连续顺序循环整个数组。

>>> for i, val in enumerate(a.flat):
...     if i%5 == 0: print(i, val)
0 10
5 15
10 20
15 25
20 30 

在这里,我使用内置的 enumerate 迭代器返回迭代器索引以及值。

N 维枚举

ndenumerate(arr) 多维索引迭代器。

有时在迭代时获取 N 维索引可能很有用。 ndenumerate 迭代器可以实现这一点。

>>> for i, val in np.ndenumerate(a):
...     if sum(i)%5 == 0: print(i, val)
(0, 0, 0) 10
(1, 1, 3) 25
(2, 0, 3) 29
(2, 1, 2) 32 

用于广播的迭代器

broadcast 生成一个模拟广播的对象。

广播的一般概念也可以通过 Python 的broadcast迭代器实现。该对象以(N)个对象作为输入,并返回一个迭代器,该迭代器在广播结果中提供每个输入序列元素的元组。

>>> for val in np.broadcast([[1,0],[2,3]],[0,1]):
...     print(val)
(1, 0)
(0, 1)
(2, 0)
(3, 1) 
```  ## 特殊属性和方法

另请参见

ndarray 的子类化

NumPy 提供了几个类可以自定义的挂钩:

```py
class.__array_ufunc__(ufunc, method, *inputs, **kwargs)

版本 1.13 中的新内容。

任何类,无论是 ndarray 的子类还是其他类,都可以定义此方法或将其设置为 None,以覆盖 NumPy 的 ufunc 行为。这与 Python 的__mul__和其他二进制操作例程非常相似。

  • ufunc 是被调用的 ufunc 对象。

  • method 是一个字符串,指示调用哪个 Ufunc 方法(其中之一是"__call__""reduce""reduceat""accumulate""outer""inner")。

  • inputs 是传递给ufunc的输入参数的元组。

  • kwargs 是一个包含 ufunc 的可选输入参数的字典。如果给定,任何out参数,无论是位置参数还是关键字参数,都作为tuple传递给 kwargs。有关详细信息,请参见 Universal functions (ufunc) 中的讨论。

该方法应返回操作的结果或者如果所请求的操作未实现,则返回NotImplemented

如果输入、输出或where参数中的一个具有__array_ufunc__方法,则会执行ufunc 而不是。 如果多个参数都实现了__array_ufunc__,则按顺序尝试:子类在超类之前,输入在输出之前,在where之前,否则从左到右。 第一个返回值不是NotImplemented的例程确定结果。 如果所有的__array_ufunc__操作都返回NotImplemented,则会引发TypeError

注意

我们打算将 numpy 函数重新实现为(广义的)Ufunc,这样它们就可以被__array_ufunc__方法覆盖。 一个主要的候选对象是matmul,它目前不是一个 Ufunc,但可以相对容易地重写为(一组)广义的 Ufuncs。 同样的情况可能会发生在函数如medianaminargsort

与 Python 中的其他一些特殊方法(如__hash____iter__)一样,可以通过设置__array_ufunc__ = None来指示您的类支持 ufuncs。 当在设置了__array_ufunc__ = None的对象上调用 ufuncs 时,ufuncs 始终会引发TypeError

__array_ufunc__的存在也影响了ndarray如何处理二进制操作,例如arr + objarr < obj,其中arr是一个ndarray,而obj是自定义类的一个实例。 有两种可能性。 如果obj.__array_ufunc__存在且不为 None,则ndarray.__add__等会委托给 ufunc 机制,这意味着arr + obj变为np.add(arr, obj),然后add调用obj.__array_ufunc__。 如果您想定义一个像数组一样的对象,这是很有用的。

或者,如果obj.__array_ufunc__被设置为None,那么作为一个特殊情况,像ndarray.__add__这样的特殊方法会注意到这一点,并且无条件地引发TypeError。如果你想创建与数组通过二元操作进行交互的对象,但它们本身不是数组,这将非常有用。例如,一个单位处理系统可能有一个表示“米”单位的对象m,并且希望支持语法arr * m表示数组具有“米”单位,但不希望以其他方式通过 ufunc 或其他方式与数组进行交互。这可以通过设置__array_ufunc__ = None并定义__mul____rmul__方法来实现。(注意,这意味着编写一个始终返回NotImplemented__array_ufunc__并不完全等同于设置__array_ufunc__ = None:在前一种情况下,arr + obj会引发TypeError,而在后一种情况下,可以定义一个__radd__方法来防止这种情况发生。)

以上内容不适用于就地运算符,对于这些运算符,ndarray永远不会返回NotImplemented。因此,arr += obj将总是导致TypeError。这是因为对于数组来说,就地操作通常无法简单地被一个简单的反向操作所取代。(例如,默认情况下,arr += obj将被翻译为arr = arr + obj,即arr将被替换,这与期望的就地数组操作相反。)

注意

如果你定义了__array_ufunc__

  • 如果你不是ndarray的子类,我们建议你的类定义像__add____lt__这样的特殊方法,它们像ndarray一样委托给 ufunc。一个简单的方法是从NDArrayOperatorsMixin继承。

  • 如果你继承了ndarray,我们建议你把所有的重写逻辑放在__array_ufunc__中,而不是同时重写特殊方法。这确保了类层次结构只在一个地方确定,而不是通过 ufunc 机制和二元操作规则分别确定(后者优先考虑子类的特殊方法;为了强制只在一个地方确定层次结构的另一种方法,将__array_ufunc__设置为 None,似乎会让人感到意外并且令人困惑,因为子类将完全无法与 ufuncs 一起使用)。

  • ndarray定义了自己的__array_ufunc__,如果没有参数有重写,则评估 ufunc,并在其他情况下返回NotImplemented。这对于那些__array_ufunc__将其自身类的任何实例转换为ndarray的子类可能是有用的:它然后可以使用super().__array_ufunc__(*inputs, **kwargs)将这些传递给其超类,并在可能的情况下返回结果后进行反向转换。这种做法的优势在于确保能够拥有扩展行为的子类层次结构。有关详细信息,请参阅子类化 ndarray。

注意

如果一个类定义了__array_ufunc__方法,这将禁用对 ufuncs 的下面描述的__array_wrap____array_prepare____array_priority__机制(最终可能已被弃用)。

class.__array_function__(func, types, args, kwargs)

自版本 1.16 开始的新功能。

  • func是 NumPy 公共 API 中公开的任意可调用对象,以func(*args, **kwargs)的形式调用。

  • types是原始 NumPy 函数调用中实现__array_function__的唯一参数类型的集合collections.abc.Collection

  • 元组args和字典kwargs直接从原始调用中传递。

为了方便__array_function__的实现者,types提供了所有具有'__array_function__'属性的参数类型。这使得实现者能够快速识别应该推迟到其他参数的__array_function__实现的情况。实现不应依赖于types的迭代顺序。

大多数__array_function__的实现将从以下两个检查开始:

  1. 给定的函数是我们知道如何重载的吗?

  2. 所有参数都是我们知道如何处理的类型吗?

如果这些条件成立,__array_function__应返回调用其实现的结果func(*args, **kwargs)。否则,它应返回哨兵值NotImplemented,表示这些类型未实现该函数。

对于__array_function__的返回值没有一般要求,尽管大多数明智的实现可能应该返回与函数参数之一相同类型的数组。

定义自定义装饰器(implements如下)以注册__array_function__实现可能也很方便。

HANDLED_FUNCTIONS = {}

class MyArray:
    def __array_function__(self, func, types, args, kwargs):
        if func not in HANDLED_FUNCTIONS:
            return NotImplemented
        # Note: this allows subclasses that don't override
        # __array_function__ to handle MyArray objects
        if not all(issubclass(t, MyArray) for t in types):
            return NotImplemented
        return HANDLED_FUNCTIONSfunc

def implements(numpy_function):
  """Register an __array_function__ implementation for MyArray objects."""
    def decorator(func):
        HANDLED_FUNCTIONS[numpy_function] = func
        return func
    return decorator

@implements(np.concatenate)
def concatenate(arrays, axis=0, out=None):
    ...  # implementation of concatenate for MyArray objects

@implements(np.broadcast_to)
def broadcast_to(array, shape):
    ...  # implementation of broadcast_to for MyArray objects 

请注意,__array_function__的实现不需要包含所有相应 NumPy 函数的可选参数(例如,broadcast_to上省略了无关的subok参数)。只有在 NumPy 函数调用中明确使用了可选参数时,才会将可选参数传递给__array_function__

就像内置特殊方法__add__的情况一样,正确编写的__array_function__方法在遇到未知类型时应始终返回NotImplemented。否则,如果操作还涉及到你的对象之一,那么将无法正确覆盖另一个对象的 NumPy 函数。

在大多数情况下,使用__array_function__进行分发的规则与__array_ufunc__的规则相匹配。特别是:

  • NumPy 将从所有指定的输入中收集__array_function__的实现并按顺序调用它们:子类在超类之前,否则从左到右。请注意,在涉及子类的一些边缘情况中,这与 Python 的当前行为略有不同。

  • __array_function__的实现表明它们可以通过返回任何值而不是NotImplemented来处理操作。

  • 如果所有__array_function__方法都返回NotImplemented,NumPy 将引发TypeError

如果没有__array_function__方法存在,NumPy 将默认调用其自己的实现,用于在 NumPy 数组上使用。例如,当所有类似数组的参数都是 Python 数字或列表时就会出现这种情况。(NumPy 数组确实有一个__array_function__方法,如下所示,但如果除了 NumPy 数组子类之外的任何参数实现了__array_function__,它总是返回NotImplemented。)

__array_ufunc__当前行为的一个偏差是,NumPy 仅在每种唯一类型的第一个参数上调用__array_function__。这与 Python 的调用反射方法的规则相匹配,并确保即使有大量重载参数时,检查重载也具有可接受的性能。

class.__array_finalize__(obj)

当系统从objobjndarray的子类(子类型))内部分配新数组时会调用此方法。它可以用来在构造之后更改self的属性(比如确保 2 维矩阵),或者更新来自“父类”的元信息。子类继承了这个方法的默认实现,什么都不做。

class.__array_prepare__(array, context=None)

在每个 ufunc 的开始时,这个方法被调用在具有最高数组优先级的输入对象上,或者如果指定了输出对象,则在输出对象上。输出数组被传递进来,返回的任何内容都被传递给 ufunc。子类继承了这个方法的默认实现,它只是返回未修改的输出数组。子类可以选择使用这个方法将输出数组转换成子类的一个实例,并在返回数组给 ufunc 进行计算之前更新元数据。

注意

对于 ufuncs,希望最终废弃这种方法,而支持__array_ufunc__

class.__array_wrap__(array, context=None)

在每个 ufunc 的结束时,这个方法在具有最高数组优先级的输入对象上被调用,或者如果指定了输出对象,则在输出对象上。ufunc 计算得到的数组被传递进来,返回的任何内容都被传递给用户。子类继承了这个方法的默认实现,该实现将数组转换为对象类的一个新实例。子类可以选择使用这个方法将输出数组转换为子类的一个实例,并在返回数组给用户之前更新元数据。

注意

对于 ufuncs,希望最终废弃这种方法,而支持__array_ufunc__

class.__array_priority__

在这个属性的值被用来确定返回对象的 Python 类型有多种可能性的情况下使用。子类继承了这个属性的默认值为 0.0。

注意

对于 ufuncs,希望最终废弃这种方法,而支持__array_ufunc__

class.__array__([dtype])

如果一个类(ndarray 的子类或不是)具有__array__方法,并且被用作 ufunc 的输出对象,则结果不会被写入由__array__返回的对象。这种做法会引发TypeError

矩阵对象

注意

强烈建议不要使用矩阵子类。正如下文所述,这让编写可以始终处理矩阵和常规数组的函数非常困难。目前,它们主要用于与 scipy.sparse 交互。但是我们希望提供另一种用途,最终移除 matrix 子类。

matrix 对象继承自 ndarray ,因此它们具有与 ndarrays 相同的属性和方法。然而,矩阵对象有六个重要的差异,可能导致在使用矩阵时出现意外结果但期望它们的行为类似于数组的情况:

  1. 可以使用字符串表示法创建矩阵对象,以允许 Matlab 风格的语法,其中空格分隔列,分号(‘;’)分隔行。

  2. 矩阵对象始终是二维的。这具有深远影响,因为 m.ravel() 仍然是二维的(第一维度为 1),项目选择返回二维对象,因此序列行为与数组根本不同。

  3. 矩阵对象重载乘法以实现矩阵乘法。确保你理解了这一点,因为你可能需要接收矩阵的函数。特别是因为当 m 是矩阵时,asanyarray(m)返回一个矩阵。

  4. 矩阵对象重载幂运算以得到矩阵的幂。在使用 asanyarray(…) 获取数组对象的函数内部使用幂时,需要注意相同的警告。

  5. 矩阵对象的默认 array_priority 为 10.0,因此与 ndarray 的混合操作始终产生矩阵。

  6. 矩阵具有使计算更容易的特殊属性。这些是

    matrix.T 返回矩阵的转置。
    matrix.H 返回 self 的(复数)共轭转置。
    matrix.I 返回可逆 self 的(乘法)逆。
    matrix.A self 返回为一个 ndarray 对象。

警告

矩阵对象重载乘法,‘*’,和幂,‘**’,分别用于矩阵乘法和矩阵幂。如果你的子程序能够接受子类,并且你没有转换为基类数组,则必须使用 ufuncs multiply 和 power 来确保对所有输入执行正确的操作。

矩阵类是 ndarray 的 Python 子类,并可用作如何构造你自己的 ndarray 子类的参考。可以从其他矩阵、字符串和任何可转换为 ndarray 的内容创建矩阵。在 NumPy 中,名称“mat”是“matrix”的别名。

matrix(data[, dtype, copy])

注意

不再建议使用这个类,即使是线性的

|

asmatrix(data[, dtype]) 将输入解释为矩阵。
bmat(obj[, ldict, gdict]) 从字符串、嵌套序列或数组构建一个矩阵对象。

示例 1:从字符串创建矩阵

>>> a = np.mat('1 2 3; 4 5 3')
>>> print((a*a.T).I)
 [[ 0.29239766 -0.13450292]
 [-0.13450292  0.08187135]] 

示例 2:从嵌套序列创建矩阵

>>> np.mat([[1,5,10],[1.0,3,4j]])
matrix([[  1.+0.j,   5.+0.j,  10.+0.j],
 [  1.+0.j,   3.+0.j,   0.+4.j]]) 

示例 3:从数组创建矩阵

>>> np.mat(np.random.rand(3,3)).T
matrix([[4.17022005e-01, 3.02332573e-01, 1.86260211e-01],
 [7.20324493e-01, 1.46755891e-01, 3.45560727e-01],
 [1.14374817e-04, 9.23385948e-02, 3.96767474e-01]]) 

内存映射文件数组

内存映射文件对于读取和/或修改大文件的正常布局中的小段非常有用,而不需要将整个文件读入内存。一个简单的 ndarray 的子类使用内存映射文件作为数组的数据缓冲区。对于小文件,将整个文件读入内存的开销通常不重要,但是对于大文件,使用内存映射可以节省大量资源。

内存映射文件数组还有一个额外的方法(除了它们从 ndarray 继承的方法):.flush() ,用户必须手动调用它以确保对数组的任何更改实际上被写入到磁盘。

memmap(filename[, dtype, mode, offset, ...]) 在磁盘上的二进制文件中创建一个内存映射的数组。
memmap.flush() 将数组中的任何更改写入到磁盘文件中。

示例:

>>> a = np.memmap('newfile.dat', dtype=float, mode='w+', shape=1000)
>>> a[10] = 10.0
>>> a[30] = 30.0
>>> del a
>>> b = np.fromfile('newfile.dat', dtype=float)
>>> print(b[10], b[30])
10.0 30.0
>>> a = np.memmap('newfile.dat', dtype=float)
>>> print(a[10], a[30])
10.0 30.0 

字符数组(numpy.char

另请参阅

创建字符数组(numpy.char)

注意

chararray 类是为了向后兼容 Numarray 而存在的,不建议用于新开发。从 numpy 1.4 开始,如果需要字符串数组,建议使用dtypeobject_bytes_str_ 数组,并使用numpy.char 模块中的自由函数进行快速向量化的字符串操作。

这些是增强型的数组,类型为str_bytes_。 这些数组继承自ndarray,但特别定义了+*%操作,以(广播)逐元素方式执行。 这些操作在标准的字符类型ndarray上不可用。 此外,chararray具有所有标准的str(和bytes)方法,并在逐元素的基础上执行它们。 创建chararray最简单的方法可能是使用self.view(chararray),其中self是一个 str 或 unicode 数据类型的 ndarray。 但是,也可以使用numpy.chararray构造函数或通过numpy.char.array函数来创建 chararray:

chararray(shape[, itemsize, unicode, ...]) 提供方便查看字符串和 Unicode 值数组的视图。
core.defchararray.array(obj[, itemsize, ...]) 创建一个chararray

与标准的 str 数据类型的 ndarray 的另一个区别是,chararray 继承了由 Numarray 引入的特性,即数组中任何元素末尾的空白将在项检索和比较操作中被忽略。

记录数组 (numpy.rec)

参见

创建记录数组 (numpy.rec),数据类型例程,数据类型对象 (dtype)。

NumPy 提供了recarray类,允许将结构化数组的字段作为属性进行访问,并提供相应的标量数据类型对象record

recarray(shape[, dtype, buf, offset, ...]) 构造一个允许使用属性进行字段访问的 ndarray。
record 允许字段访问作为属性查找的数据类型标量。

掩码数组(numpy.ma)

另请参阅

掩码数组

标准容器类

为了向后兼容和作为标准的“容器”类,从 Numeric 中带有的 UserArray 已经被移植到 NumPy,并命名为 numpy.lib.user_array.container 容器类是一个 Python 类,其 self.array 属性是一个 ndarray。使用 numpy.lib.user_array.container 可能比直接使用 ndarray 更容易进行多重继承,因此它被默认包含在内。这里没有对其进行文档化,只是提及它的存在,因为鼓励您直接使用 ndarray 类。

numpy.lib.user_array.container(data[, ...]) 用于轻松多重继承的标准容器类。

数组迭代器

迭代器是数组处理的一个强大概念。基本上,迭代器实现了一个广义的 for 循环。如果 myiter 是一个迭代器对象,那么 Python 代码:

for val in myiter:
    ...
    some code involving val
    ... 

重复调用 val = next(myiter),直到迭代器引发 StopIteration。有几种可能有用的数组迭代方式:默认迭代、平坦迭代和 (N)-维枚举。

默认迭代

ndarray 对象的默认迭代器是序列类型的默认 Python 迭代器。因此,当数组对象本身被用作迭代器时。默认行为等同于:

for i in range(arr.shape[0]):
    val = arr[i] 

这个默认迭代器从数组中选择一个 (N-1) 维的子数组。这对于定义递归算法可能是一个有用的构造。要循环整个数组需要 (N) 个 for 循环。

>>> a = np.arange(24).reshape(3,2,4)+10
>>> for val in a:
...     print('item:', val)
item: [[10 11 12 13]
 [14 15 16 17]]
item: [[18 19 20 21]
 [22 23 24 25]]
item: [[26 27 28 29]
 [30 31 32 33]] 

平坦迭代

ndarray.flat 数组上的一维迭代器。

正如之前提到的,ndarray 对象的 flat 属性返回一个迭代器,该迭代器将以 C 风格连续顺序循环整个数组。

>>> for i, val in enumerate(a.flat):
...     if i%5 == 0: print(i, val)
0 10
5 15
10 20
15 25
20 30 

在这里,我使用了内置的 enumerate 迭代器来返回迭代器索引以及值。

N 维枚举

ndenumerate(arr) 多维索引迭代器。

有时在迭代时获取 N 维索引可能很有用。ndenumerate 迭代器可以实现这个功能。

>>> for i, val in np.ndenumerate(a):
...     if sum(i)%5 == 0: print(i, val)
(0, 0, 0) 10
(1, 1, 3) 25
(2, 0, 3) 29
(2, 1, 2) 32 

用于广播的迭代器

broadcast 生成一个模仿广播的对象。

使用 Python 也可以实现广播的一般概念,使用 broadcast 迭代器。该对象接受 (N) 个对象作为输入,并返回一个迭代器,该迭代器在广播结果中提供每个输入序列元素的元组。

>>> for val in np.broadcast([[1,0],[2,3]],[0,1]):
...     print(val)
(1, 0)
(0, 1)
(2, 0)
(3, 1) 

默认迭代

ndarray 对象的默认迭代器是序列类型的默认 Python 迭代器。因此,当数组对象本身被用作迭代器时,其默认行为等同于:

for i in range(arr.shape[0]):
    val = arr[i] 

此默认迭代器从数组中选择一个维度为 (N-1) 的子数组。这对于定义递归算法可能是一个有用的构造。要遍历整个数组需要 (N) 个 for 循环。

>>> a = np.arange(24).reshape(3,2,4)+10
>>> for val in a:
...     print('item:', val)
item: [[10 11 12 13]
 [14 15 16 17]]
item: [[18 19 20 21]
 [22 23 24 25]]
item: [[26 27 28 29]
 [30 31 32 33]] 

平面迭代

ndarray.flat 数组的一维迭代器。

如前所述,ndarray 对象的 flat 属性返回一个迭代器,该迭代器将以 C 风格连续顺序循环遍历整个数组。

>>> for i, val in enumerate(a.flat):
...     if i%5 == 0: print(i, val)
0 10
5 15
10 20
15 25
20 30 

在这里,我已经使用了内置的 enumerate 迭代器来返回迭代器索引以及值。

N 维枚举

ndenumerate(arr) 多维索引迭代器。

有时,在迭代时获取 N 维索引可能很有用。ndenumerate 迭代器可以实现这一点。

>>> for i, val in np.ndenumerate(a):
...     if sum(i)%5 == 0: print(i, val)
(0, 0, 0) 10
(1, 1, 3) 25
(2, 0, 3) 29
(2, 1, 2) 32 

广播迭代器

broadcast 生成一个模拟广播的对象。

使用 Python 也可以实现广播的一般概念,使用 broadcast 迭代器。该对象接受 (N) 个对象作为输入,并返回一个迭代器,该迭代器在广播结果中提供每个输入序列元素的元组。

>>> for val in np.broadcast([[1,0],[2,3]],[0,1]):
...     print(val)
(1, 0)
(0, 1)
(2, 0)
(3, 1) 

numpy.matrix.T

numpy.org/doc/1.26/reference/generated/numpy.matrix.T.html

属性

property matrix.T

返回矩阵的转置。

进行共轭!对于复共轭转置,请使用.H

参数:

返回:

ret矩阵对象

(非共轭)矩阵的转置。

另请参阅

transpose, getH

示例

>>> m = np.matrix('[1, 2; 3, 4]')
>>> m
matrix([[1, 2],
 [3, 4]])
>>> m.getT()
matrix([[1, 3],
 [2, 4]]) 

numpy.matrix.H

原文:numpy.org/doc/1.26/reference/generated/numpy.matrix.H.html

属性

property matrix.H

返回self的(复)共轭转置。

如果self是实数值,相当于np.transpose(self)

参数:

None

返回:

ret矩阵对象

self的复共轭转置

示例

>>> x = np.matrix(np.arange(12).reshape((3,4)))
>>> z = x - 1j*x; z
matrix([[  0\. +0.j,   1\. -1.j,   2\. -2.j,   3\. -3.j],
 [  4\. -4.j,   5\. -5.j,   6\. -6.j,   7\. -7.j],
 [  8\. -8.j,   9\. -9.j,  10.-10.j,  11.-11.j]])
>>> z.getH()
matrix([[ 0\. -0.j,  4\. +4.j,  8\. +8.j],
 [ 1\. +1.j,  5\. +5.j,  9\. +9.j],
 [ 2\. +2.j,  6\. +6.j, 10.+10.j],
 [ 3\. +3.j,  7\. +7.j, 11.+11.j]]) 

numpy.matrix.I

原文:numpy.org/doc/1.26/reference/generated/numpy.matrix.I.html

属性

property matrix.I

返回可逆self的(乘法)逆。

参数:

None

返回:

ret矩阵对象

如果self是非奇异的,则ret满足ret * self == self * ret == np.matrix(np.eye(self[0,:].size)) 的条件都返回True

引发:

numpy.linalg.LinAlgError: 奇异矩阵

如果self是奇异的。

参见

linalg.inv

示例

>>> m = np.matrix('[1, 2; 3, 4]'); m
matrix([[1, 2],
 [3, 4]])
>>> m.getI()
matrix([[-2\. ,  1\. ],
 [ 1.5, -0.5]])
>>> m.getI() * m
matrix([[ 1.,  0.], # may vary
 [ 0.,  1.]]) 

numpy.matrix.A

原文:numpy.org/doc/1.26/reference/generated/numpy.matrix.A.html

属性

property matrix.A

ndarray对象的形式返回自身

等同于np.asarray(self)

参数:

返回值:

retndarray

自身作为ndarray对象

示例

>>> x = np.matrix(np.arange(12).reshape((3,4))); x
matrix([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]])
>>> x.getA()
array([[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]]) 

numpy.matrix

原文:numpy.org/doc/1.26/reference/generated/numpy.matrix.html

class numpy.matrix(data, dtype=None, copy=True)

注意

不再建议使用这个类,甚至对于线性代数也不建议。请使用正规的数组。该类可能会在未来被移除。

从一个类似数组的对象或数据字符串返回一个矩阵。矩阵是一个专门化的保持其二维性质的二维数组。它有一些特殊的操作符,比如*(矩阵乘法)和**(矩阵幂)。

参数:

dataarray_like or string

如果data是一个字符串,它会被解释为以逗号或空格分隔列,用分号分隔行的矩阵。

dtype数据类型

输出矩阵的数据类型。

copybool

如果data已经是一个ndarray,则此标志确定数据是被复制(默认)还是被构建为视图。

另请参阅

array

示例

>>> a = np.matrix('1 2; 3 4')
>>> a
matrix([[1, 2],
 [3, 4]]) 
>>> np.matrix([[1, 2], [3, 4]])
matrix([[1, 2],
 [3, 4]]) 

属性:

A

作为一个ndarray对象返回 self

A1

self 作为一个扁平化的ndarray返回。

H

返回 self 的(复数)共轭转置。

I

返回 self 可逆的(乘法)逆。

T

返回矩阵的转置。

base

如果内存来自其他对象,则为基础对象。

ctypes

一个简化数组与 ctypes 模块交互的对象。

data

指向数组数据起始位置的 Python 缓冲对象。

dtype

数组元素的数据类型。

flags

数组的内存布局信息。

flat

数组的一维迭代器。

imag

数组的虚部。

itemsize

一个数组元素的字节长度。

nbytes

数组元素消耗的总字节数。

ndim

数组维度数量。

real

数组的实部。

shape

数组维度的元组。

size

数组中的元素数量。

strides

遍历数组时,在每个维度上的步长的字节元组。

方法

all([axis, out]) 测试沿给定轴的所有矩阵元素是否评估为 True。
any([axis, out]) 测试沿给定轴的任何数组元素是否评估为 True。
argmax([axis, out]) 沿轴的最大值的索引。
argmin([axis, out]) 沿轴的最小值的索引。
argpartition(kth[, axis, kind, order]) 返回将此数组分区的索引。
argsort([axis, kind, order]) 返回将此数组排序的索引。
astype(dtype[, order, casting, subok, copy]) 复制为指定类型的数组。
byteswap([inplace]) 交换数组元素的字节。
choose(choices[, out, mode]) 使用索引数组从一组选择中构造新数组。
clip([min, max, out]) 返回其值限制为[min, max]的数组。
compress(condition[, axis, out]) 沿给定轴返回此数组的选定切片。
conj() 复共轭所有元素。
conjugate() 返回元素的复共轭。
copy([order]) 返回数组的副本。
cumprod([axis, dtype, out]) 返回沿给定轴的元素的累积乘积。
cumsum([axis, dtype, out]) 返回沿给定轴的元素的累积和。
diagonal([offset, axis1, axis2]) 返回指定对角线。
dump(file) 将数组的 pickle 转储到指定的文件中。
dumps() 将数组的 pickle 作为字符串返回。
fill(value) 用标量值填充数组。
flatten([order]) 返回矩阵的平展副本。
getA() ndarray 对象的形式返回 self
getA1() self 作为一个平展的 ndarray 返回。
getH() 返回 self 的(复数)共轭转置。
getI() 返回可逆 self 的(乘法)逆。
getT() 返回矩阵的转置。
getfield(dtype[, offset]) 将给定数组的字段按特定类型返回。
item(*args) 将数组的一个元素复制到标准 Python 标量并返回。
itemset(*args) 将标量插入数组(如果可能,将标量强制转换为数组的 dtype)。
max([axis, out]) 返回沿某个轴的最大值。
mean([axis, dtype, out]) 返回沿给定轴的矩阵元素的平均值。
min([axis, out]) 返回沿某个轴的最小值。
newbyteorder([new_order]) 返回以不同字节顺序查看的相同数据的数组。
nonzero() 返回非零元素的索引。
partition(kth[, axis, kind, order]) 重新排列数组中的元素,使第 k 个位置的元素的值处于排序后数组中的位置。
prod([axis, dtype, out]) 返回沿给定轴的数组元素的乘积。
ptp([axis, out]) 沿给定轴的峰值到谷值(最大值 - 最小值)。
put(indices, values[, mode]) 对所有 n 在索引中的值,设置 a.flat[n] = values[n]
ravel([order]) 返回一个扁平化的矩阵。
repeat(repeats[, axis]) 重复数组的元素。
reshape(shape[, order]) 返回包含相同数据的新形状的数组。
resize(new_shape[, refcheck]) 就地更改数组的形状和大小。
round([decimals, out]) 返回每个元素四舍五入到给定小数位的 a
searchsorted(v[, side, sorter]) 查找应插入数组 a 中元素v 的索引,以保持顺序。
setfield(val, dtype[, offset]) 将一个值放入由数据类型定义的字段中的指定位置。
setflags([write, align, uic]) 分别设置数组标志 WRITEABLE、ALIGNED、WRITEBACKIFCOPY。
sort([axis, kind, order]) 就地��数组进行排序。
squeeze([axis]) 返回一个可能重新塑形的矩阵。
std([axis, dtype, out, ddof]) 返回沿给定轴的数组元素的标准偏差。
sum([axis, dtype, out]) 返回矩阵元素沿给定轴的和。
swapaxes(axis1, axis2) 返回数组的视图,axis1axis2 互换。
take(indices[, axis, out, mode]) 返回数组 a 中给定索引处的元素组成的数组。
tobytes([order]) 构建包含数组中原始数据字节的 Python 字节。
tofile(fid[, sep, format]) 将数组以文本或二进制(默认)形式写入文件。
tolist() 将矩阵返回为一个(可能嵌套的)列表。
tostring([order]) tobytes完全相同的行为的兼容别名。
trace([offset, axis1, axis2, dtype, out]) 返回数组对角线上的和。
transpose(*axes) 返回数组的轴被转置后的视图。
var([axis, dtype, out, ddof]) 返回矩阵元素沿给定轴的方差。
view([dtype][, type]) 具有相同数据的数组的新视图。
dot

numpy.asmatrix

原文:numpy.org/doc/1.26/reference/generated/numpy.asmatrix.html

numpy.asmatrix(data, dtype=None)

将输入解释为矩阵。

不像matrix,如果输入已经是矩阵或 ndarray,asmatrix不会创建副本。等同于matrix(data, copy=False)

参数:

data类似数组

输入数据。

dtype数据类型

输出矩阵的数据类型。

返回:

mat矩阵

data 解释为矩阵。

示例

>>> x = np.array([[1, 2], [3, 4]]) 
>>> m = np.asmatrix(x) 
>>> x[0,0] = 5 
>>> m
matrix([[5, 2],
 [3, 4]]) 

numpy.bmat

原文:numpy.org/doc/1.26/reference/generated/numpy.bmat.html

numpy.bmat(obj, ldict=None, gdict=None)

从字符串、嵌套序列或数组构建一个矩阵对象。

参数:

obj字符串或类数组

输入数据。如果是字符串,则可以通过名称引用当前范围内的变量。

ldict字典,可选

一个字典,用于替换当前帧中的局部操作数。如果obj不是字符串或gdict为 None,则忽略。

gdict字典,可选

一个字典,用于替换当前帧中的全局操作数。如果obj不是字符串,则忽略。

返回值:

out矩阵

返回一个矩阵对象,这是一个专门的二维数组。

另请参阅

block

对于 N 维数组的此函数的一般化,返回普通的 ndarray。

示例

>>> A = np.mat('1 1; 1 1')
>>> B = np.mat('2 2; 2 2')
>>> C = np.mat('3 4; 5 6')
>>> D = np.mat('7 8; 9 0') 

以下所有表达式构造相同的块矩阵:

>>> np.bmat([[A, B], [C, D]])
matrix([[1, 1, 2, 2],
 [1, 1, 2, 2],
 [3, 4, 7, 8],
 [5, 6, 9, 0]])
>>> np.bmat(np.r_[np.c_[A, B], np.c_[C, D]])
matrix([[1, 1, 2, 2],
 [1, 1, 2, 2],
 [3, 4, 7, 8],
 [5, 6, 9, 0]])
>>> np.bmat('A,B; C,D')
matrix([[1, 1, 2, 2],
 [1, 1, 2, 2],
 [3, 4, 7, 8],
 [5, 6, 9, 0]]) 

numpy.memmap

原文:numpy.org/doc/1.26/reference/generated/numpy.memmap.html

class numpy.memmap(filename, dtype=<class 'numpy.ubyte'>, mode='r+', offset=0, shape=None, order='C')

在磁盘上的二进制文件中创建一个数组的内存映射。

内存映射文件被用于访问磁盘上大文件的小片段,而不需要将整个文件读入内存。NumPy 的memmap是类似数组的对象。这与 Python 的mmap模块不同,后者使用类似文件的对象。

由于这个 ndarray 的子类与一些操作有一些不愉快的交互,因为它不太适合作为一个子类。使用这个子类的另一种方法是自己创建mmap对象,然后直接用ndarray.__new__创建一个 ndarray,将创建的对象传递给它的‘buffer=’参数。

这个类在一定时候可能会被转换成一个工厂函数,返回一个视图到一个mmap缓冲区。

刷新memmap实例以将更改写入文件。目前没有 API 来关闭底层的mmap。确保资源实际上关闭是有技巧的,因为它可能在不同的memmap实例之间共享。

参数:

文件名字符串,类似文件对象或 pathlib.Path 实例

用作数组数据缓冲区的文件名或文件对象。

dtype数据类型,可选

用于解释文件内容的数据类型。默认为uint8

模式,可选

以这种模式打开文件:

‘r’ 仅打开现有文件以供读取。
‘r+’ 打开现有文件以供读取和写入。
‘w+’ 创建或覆盖现有文件以供读取和写入。如果mode == 'w+',则必须同时指定shape
‘c’ 写时复制:赋值影响内存中的数据,但更改不会保存到磁盘上。文件是只读的。

默认为‘r+’。

偏移量整数,可选

在文件中,数组数据从这个偏移量开始。由于offset以字节为单位,所以通常应该是dtype的字节大小的倍数。当mode != 'r'时,甚至文件末尾之后的正偏移量也是有效的;文件将被扩展以容纳附加数据。默认情况下,memmap将从文件的开头开始,即使filename是文件指针fpfp.tell() != 0

形状元组,可选

数组的期望形状。如果mode == 'r'并且offset之后剩余的字节数不是dtype的字节大小的倍数,你必须指定shape。默认情况下,返回的数组将是 1-D 数组,其元素数量由文件大小和数据类型确定。

顺序,可选

指定 ndarray 内存布局的顺序:行优先、C 风格或列优先、Fortran 风格。只有在形状大于 1-D 时才会生效。默认顺序是 ‘C’。

另请参阅

lib.format.open_memmap

创建或加载一个内存映射的.npy文件。

注意事项

memmap 对象可用于任何接受 ndarray 的地方。给定一个 memmap fpisinstance(fp, numpy.ndarray) 返回 True

内存映射文件在 32 位系统上不能超过 2GB。

当 memmap 导致在文件系统中创建或扩展超出当前大小的文件时,新部分的内容是未指定的。在具有 POSIX 文件系统语义的系统上,扩展部分将填充为零字节。

示例

>>> data = np.arange(12, dtype='float32')
>>> data.resize((3,4)) 

此示例使用一个临时文件,以便 doctest 不会将文件写入您的目录。您会使用一个‘正常’的文件名。

>>> from tempfile import mkdtemp
>>> import os.path as path
>>> filename = path.join(mkdtemp(), 'newfile.dat') 

创建一个与我们的数据匹配的 dtype 和形状的 memmap:

>>> fp = np.memmap(filename, dtype='float32', mode='w+', shape=(3,4))
>>> fp
memmap([[0., 0., 0., 0.],
 [0., 0., 0., 0.],
 [0., 0., 0., 0.]], dtype=float32) 

将数据写入 memmap 数组:

>>> fp[:] = data[:]
>>> fp
memmap([[  0.,   1.,   2.,   3.],
 [  4.,   5.,   6.,   7.],
 [  8.,   9.,  10.,  11.]], dtype=float32) 
>>> fp.filename == path.abspath(filename)
True 

刷新内存更改以便读取它们

>>> fp.flush() 

加载 memmap 并验证数据已存储:

>>> newfp = np.memmap(filename, dtype='float32', mode='r', shape=(3,4))
>>> newfp
memmap([[  0.,   1.,   2.,   3.],
 [  4.,   5.,   6.,   7.],
 [  8.,   9.,  10.,  11.]], dtype=float32) 

只读 memmap:

>>> fpr = np.memmap(filename, dtype='float32', mode='r', shape=(3,4))
>>> fpr.flags.writeable
False 

写时复制 memmap:

>>> fpc = np.memmap(filename, dtype='float32', mode='c', shape=(3,4))
>>> fpc.flags.writeable
True 

可以将值分配给写时复制数组,但值仅写入数组的内存副本,而不写入磁盘:

>>> fpc
memmap([[  0.,   1.,   2.,   3.],
 [  4.,   5.,   6.,   7.],
 [  8.,   9.,  10.,  11.]], dtype=float32)
>>> fpc[0,:] = 0
>>> fpc
memmap([[  0.,   0.,   0.,   0.],
 [  4.,   5.,   6.,   7.],
 [  8.,   9.,  10.,  11.]], dtype=float32) 

磁盘上的文件保持不变:

>>> fpr
memmap([[  0.,   1.,   2.,   3.],
 [  4.,   5.,   6.,   7.],
 [  8.,   9.,  10.,  11.]], dtype=float32) 

memmap 的偏移:

>>> fpo = np.memmap(filename, dtype='float32', mode='r', offset=16)
>>> fpo
memmap([  4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.], dtype=float32) 

属性:

文件名str 或 pathlib.Path 实例

映射文件的路径。

偏移量int

文件中的偏移位置。

模式str

文件模式。

方法

flush() 将数组中的任何更改写入磁盘上的文件。

numpy.memmap.flush

原文:numpy.org/doc/1.26/reference/generated/numpy.memmap.flush.html

方法

memmap.flush()

将数组中的任何更改写入磁盘上的文件。

如需更多信息,请参阅memmap

参数:

另请参阅

memmap

numpy.chararray

numpy.org/doc/1.26/reference/generated/numpy.chararray.html

class numpy.chararray(shape, itemsize=1, unicode=False, buffer=None, offset=0, strides=None, order=None)

为字符串和 unicode 值的数组提供了便利的视图。

注意

chararray类是为了向后兼容 Numarray 而存在的,不建议用于新开发。从 numpy 1.4 开始,如果需要字符串数组,则建议使用dtypeobject_bytes_str_的数组,并使用numpy.char模块中的自由函数执行快速矢量化字符串操作。

与普通的类型为strunicode的 NumPy 数组相比,此类添加了以下功能:

  1. 索引时,对值自动从末尾去除空格
  2. 比较操作符在比较值时会自动从末尾去除空格。
  3. 提供矢量化字符串操作作为方法(例如endswith)和中缀运算符(例如+*%

应该使用numpy.char.arraynumpy.char.asarray来创建 chararrays,而不是直接使用该构造函数。

此构造函数创建数组,使用buffer(带有offsetstrides)如果不为None的话。如果bufferNone,则构造一个C 顺序的新数组,除非len(shape) >= 2order='F',在这种情况下,stridesFortran 顺序

参数:

shape元组

数组的形状。

itemsizeint,可选

每个数组元素的长度,以字符数表示。默认为 1。

unicodebool,可选

数组元素是 unicode(True)还是 string(False)。默认为 False。

buffer暴露缓冲接口的对象或 str,可选

数组数据的起始内存地址。默认为 None,此时将创建一个新数组。

offsetint,可选

从轴的起始处的固定步长位移?默认为 0。必须是>=0。

stridesint 的数组样式,可选

数组的步幅(完整说明请参见ndarray.strides)。默认为 None。

order,可选

数组数据在内存中存储的顺序:‘C’ -> “行优先”顺序(默认),‘F’ -> “列优先”(Fortran)顺序。

示例

>>> charar = np.chararray((3, 3))
>>> charar[:] = 'a'
>>> charar
chararray([[b'a', b'a', b'a'],
 [b'a', b'a', b'a'],
 [b'a', b'a', b'a']], dtype='|S1') 
>>> charar = np.chararray(charar.shape, itemsize=5)
>>> charar[:] = 'abc'
>>> charar
chararray([[b'abc', b'abc', b'abc'],
 [b'abc', b'abc', b'abc'],
 [b'abc', b'abc', b'abc']], dtype='|S5') 

属性:

T

转置数组的视图。

base

如果存储在内存中的数组来自其他对象,则为基础对象。

ctypes

一个用来简化数组与 ctypes 模块交互的对象。

data

指向数组数据起始位置的 Python 缓冲区对象。

dtype

数组元素的数据类型。

flags

有关数组内存布局的信息。

flat

数组的 1-D 迭代器。

imag

数组的虚部。

itemsize

每个数组元素的字节数。

nbytes

数组元素消耗的总字节数。

ndim

数组的维度数量。

real

数组的实部。

shape

数组的维度的元组。

size

数组中的元素数量。

strides

每个维度在遍历数组时的步长的元组。

方法

astype(数据类型[, 顺序, 强制转换, 可替代, 复制]) 数组的副本,转换为指定类型。
argsort([轴, 种类, 顺序]) 返回对数组排序后的索引。
copy([顺序]) 返回数组的副本。
count(子串[, 起始, 结束]) 返回数组中子串 sub 在区间 [start, end] 内非重叠出现的次数的数组。
decode([编码, 错误]) 逐元素调用 bytes.decode
dump(文件) 将数组的 pickle 转储到指定文件。
dumps() 返回数组的 pickle 格式字符串。
encode([encoding, errors]) 逐元素调用 str.encode
endswith(suffix[, start, end]) 返回布尔数组,在其中 self 中的字符串元素以 suffix 结尾则为 True,否则为 False
expandtabs([tabsize]) 返回将每个字符串元素中的所有制表符替换为一个或多个空格的副本。
fill(value) 用标量值填充数组。
find(sub[, start, end]) 对于每个元素,返回字符串中发现子字符串 sub 的最低索引。
flatten([order]) 返回压缩为一维的数组副本。
getfield(dtype[, offset]) 将给定数组的字段作为特定类型返回。
index(sub[, start, end]) 类似于 find,但在子字符串未找到时会引发 ValueError
isalnum() 如果字符串中的所有字符均为字母数字字符且至少有一个字符,则对每个元素返回 true,否则返回 false。
isalpha() 如果字符串中的所有字符均为字母字符且至少有一个字符,则对每个元素返回 true,否则返回 false。
isdecimal() 对于 self 中的每个元素,如果元素中只有十进制字符,则返回 True。
isdigit() 如果字符串中的所有字符均为数字字符且至少有一个字符,则对每个元素返回 true,否则返回 false。
islower() 如果字符串中的所有大小写字符均为小写字母且至少有一个大小写字符,则对每个元素返回 true,否则返回 false。
isnumeric() 对于 self 中的每个元素,如果元素中只有数值字符,则返回 True。
isspace() 对于每个元素,如果字符串中只包含空白字符并且至少有一个字符,则返回 true;否则返回 false。
istitle() 对于每个元素,如果该元素是一个标题化的字符串并且至少有一个字符,则返回 true;否则返回 false。
isupper() 对于每个元素,如果字符串中的所有字母都是大写字母并且至少有一个字符,则返回 true;否则返回 false。
item(*args) 将数组的一个元素复制到一个标准的 Python 标量并返回它。
join(seq) 返回一个由序列 seq 中的字符串连接而成的字符串。
ljust(width[, fillchar]) 返回一个将 self 中的元素左对齐到长度为 width 的字符串中的数组。
lower() 返回一个将 self 中的元素转换为小写的数组。
lstrip([chars]) 对于 self 中的每个元素,返回一个删除前导字符的副本。
nonzero() 返回非零元素的索引。
put(indices, values[, mode]) 对于所有 n 在索引中的元素,设置 a.flat[n] = values[n]
ravel([order]) 返回一个扁平化的数组。
repeat(repeats[, axis]) 重复数组的元素。
replace(old, new[, count]) 对于 self 中的每个元素,返回一个将所有子字符串 old 的出现替换为 new 的字符串副本。
reshape(shape[, order]) 返回一个包含相同数据但具有新形状的数组。
resize(new_shape[, refcheck]) 原地更改数组的形状和大小。
rfind(sub[, start, end]) 对于 self 中的每个元素,返回字符串中子字符串 sub 最高索引的位置,其中 sub 包含在 [start, end] 内。
rindex(sub[, start, end]) 类似于rfind,但在子字符串sub未找到时引发ValueError
rjust(width[, fillchar]) 返回一个将self中的元素右对齐在长度为width的字符串中的数组。
rsplit([sep, maxsplit]) self中的每个元素,使用sep作为分隔符,返回字符串中单词的列表。
rstrip([chars]) self中的每个元素,返回一个副本,其中移除了尾随字符。
searchsorted(v[, side, sorter]) 找到应该插入数组 a 的位置索引,以保持顺序。
setfield(val, dtype[, offset]) 在由数据类型定义的字段的指定位置放置一个值。
setflags([write, align, uic]) 设置数组标志 WRITEABLE、ALIGNED、WRITEBACKIFCOPY。
sort([axis, kind, order]) 对数组进行原地排序。
split([sep, maxsplit]) self中的每个元素,使用sep作为分隔符,返回字符串中单词的列表。
splitlines([keepends]) self中的每个元素,在换行符处中断,返回元素中的行列表。
squeeze([axis]) a中删除长度为一的轴。
startswith(prefix[, start, end]) 返回一个布尔数组,其中True表示self中的字符串元素以prefix开头,否则为False
strip([chars]) self中的每个元素,返回一个副本,其中移除了前导和尾随字符。
swapaxes(axis1, axis2) 返回一个将axis1axis2交换的数组视图。
swapcase() 对于self中的每个元素,返回将大写字符转换为小写字符,反之亦然的字符串副本。
take(indices[, axis, out, mode]) 返回从给定索引处的a的元素形成的数组。
title() 对于self中的每个元素,返回字符串的.title()版本:单词以大写字符开头,所有其余大小写字符为小写。
tofile(fid[, sep, format]) 将数组写入文件为文本或二进制(默认)。
tolist() 将数组作为 Python 标量的a.ndim级别深度嵌套列表返回。
tostring([order]) tobytes完全具有相同行为的兼容别名。
translate(table[, deletechars]) 对于self中的每个元素,返回删除了可选参数deletechars中出现的所有字符,并且剩余字符已通过给定的转换表映射的字符串副本。
transpose(*axes) 返回具有转置轴的数组的视图。
upper() 返回将self中的元素转换为大写的数组。
view([dtype][, type]) 具有相同数据的数组的新视图。
zfill(width) 返回长度为width的字符串中左侧用零填充的数字字符串。

numpy.core.defchararray.array

原文:numpy.org/doc/1.26/reference/generated/numpy.core.defchararray.array.html

core.defchararray.array(obj, itemsize=None, copy=True, unicode=None, order=None)

创建一个 chararray

注意

此类用于 numarray 的向后兼容性。新代码(不关心 numarray 兼容性)应使用类型为 bytes_str_ 的数组,并使用 numpy.char 中的免费函数进行快速的向量化字符串操作。

与类型为 strunicode 的常规 NumPy 数组相比,此类添加了以下功能:

  1. 在索引值时,自动从末尾删除空格
  2. 比较运算符在比较值时自动从末尾删除空格
  3. 向量化的字符串操作被提供为方法(例如 str.endswith)和中缀运算符(例如 +, *, %

参数:

obj字符串数组或类似 unicode 的对象

itemsize整数,可选

itemsize 是结果数组中每个标量的字符数。如果 itemsize 为 None,并且 obj 是对象数组或 Python 列表,则 itemsize 将自动确定。如果提供了 itemsize 并且 obj 的类型为 str 或 unicode,则 obj 字符串将被分成 itemsize 个片段。

copy布尔值,可选

如果为 true(默认值),则对象将被复制。否则,仅当 array 返回副本时,如果 obj 是嵌套序列,或者如果需要复制以满足其他要求(itemsize、unicode、order 等),则会进行复制。

unicode布尔值,可选

当为 true 时,结果的 chararray 可以包含 Unicode 字符;当为 false 时,仅包含 8 位字符。如果 unicode 为 None,并且 obj 是以下内容之一:

  • 一个 chararray,
  • 类型为 strunicode 的 ndarray
  • 一个 Python str 或 unicode 对象,

然后输出数组的 unicode 设置将自动确定。

order,可选

指定数组的顺序。如果顺序为‘C’(默认值),则数组将按 C 连续顺序排列(最后一个索引变化最快)。如果顺序为‘F’,则返回的数组将按 Fortran 连续顺序排列(第一个索引变化最快)。如果顺序为‘A’,则返回的数组可以按任何顺序排列(C 连续、Fortran 连续甚至不连续)。

numpy.recarray

原文:numpy.org/doc/1.26/reference/generated/numpy.recarray.html

class numpy.recarray(shape, dtype=None, buf=None, offset=0, strides=None, formats=None, names=None, titles=None, byteorder=None, aligned=False, order='C')

构造一个允许使用属性访问字段的 ndarray。

数组可能包含包含字段的数据类型,类似于电子表格中的列。一个示例是[(x, int), (y, float)],其中数组中的每个条目都是一个(int, float)的对。通常,这些属性通过字典查找来访问,例如arr['x']arr['y']。记录数组允许以数组的成员方式访问字段,使用arr.xarr.y

参数:

shape元组

输出数组的形状。

dtype数据类型,可选

所需数据类型。默认情况下,数据类型是根据formatsnamestitlesalignedbyteorder确定的。

formats数据类型列表,可选

包含不同列的数据类型的列表,例如['i4', 'f8', 'i4']formats不支持直接使用类型的新约定,即(int, float, int)。请注意,formats必须是列表,而不是元组。鉴于formats有些受限,我们建议优先指定dtype

names字符串元组,可选

每列的名称,例如('x', 'y', 'z')

buf缓冲区,可选

默认情况下,将创建一个给定形状和数据类型的新数组。如果指定了buf且是一个暴露缓冲区接口的对象,则数组将使用现有缓冲区的内存。在这种情况下,offsetstrides关键字可用。

返回:

recrecarray

给定形状和类型的空数组。

其他参数:

titles字符串元组,可选

列名称的别名。例如,如果names('x', 'y', 'z')titles('x_coordinate', 'y_coordinate', 'z_coordinate'),那么arr['x']等同于arr.xarr.x_coordinate

byteorder,可选

所有字段的字节顺序。

aligned布尔值,可选

将字段在内存中对齐,就像 C 编译器会做的那样。

strides整数元组,可选

缓冲区(buf)根据这些步幅解释(步幅定义了内存中每个数组元素、行、列等占用多少字节)。

offset整数,可选

从此偏移开始读取缓冲区(buf)。

order,可选

行主(C 样式)或列主(Fortran 样式)顺序。

参见

core.records.fromrecords

从数据构造一个记录数组。

record

recarray的基本数据类型。

format_parser

从格式、名称、标题确定数据类型。

注意

这个构造函数可以与empty进行比较:它创建一个新的记录数组,但不填充数据。要从数据创建记录数组,请使用以下方法之一:

  1. 创建一个标准的 ndarray 并将其转换为记录数组,使用arr.view(np.recarray)

  2. 使用buf关键字。

  3. 使用np.rec.fromrecords

示例

创建一个具有两个字段xy的数组:

>>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', '<f8'), ('y', '<i8')])
>>> x
array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')]) 
>>> x['x']
array([1., 3.]) 

以记录数组的形式查看数组:

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

创建一个新的空记录数组:

>>> np.recarray((2,),
... dtype=[('x', int), ('y', float), ('z', int)]) 
rec.array([(-1073741821, 1.2249118382103472e-301, 24547520),
 (3471280, 1.2134086255804012e-316, 0)],
 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '<i4')]) 

属性:

T

转置数组的视图。

base

如果内存来自其他对象,则为基本对象。

ctypes

一个方便与 ctypes 模块交互的对象。

data

指向数组数据起始位置的 Python 缓冲对象。

dtype

数组元素的数据类型。

flags

数组的内存布局信息。

flat

数组的 1-D 迭代器。

imag

数组的虚部。

itemsize

每个数组元素的字节长度。

nbytes

数组元素消耗的总字节数。

ndim

数组的维度数。

real

数组的实部。

shape

数组维度的元组。

size

数组中的元素个数。

strides

遍历数组时在每个维度上步进的字节数元组。

方法

all([axis, out, keepdims, where]) 如果所有元素求值为 True,则返回 True。
any([axis, out, keepdims, where]) 如果a中的任何元素求值为 True,则返回 True。
argmax([axis, out, keepdims]) 返回沿给定轴的最大值的索引。
argmin([axis, out, keepdims]) 返回沿给定轴的最小值的索引。
argpartition 返回将此数组划分的索引。
argsort 返回按顺序对此数组进行排序的索引。
astype 数组的副本,转换为指定的类型。
byteswap 交换数组元素的字节。
choose 使用索引数组从一组选择中构建新数组。
clip 返回其值限制为[min, max]的数组。
compress 返回沿着给定轴选定的数组切片。
conj 复共轭所有元素。
conjugate 返回复共轭,逐个元素。
copy 返回数组的副本。
cumprod 返回沿给定轴的元素的累积乘积。
cumsum 返回沿给定轴的元素的累积总和。
diagonal 返回指定的对角线。
dump 将数组的 pickle 转储到指定文件。
dumps 将数组的 pickle 作为字符串返回。
fill 使用标量值填充数组。
flatten 返回折叠成一维的数组的副本。
getfield 返回给定数组的字段作为某种类型。
item 将数组的元素复制到标准 Python 标量并返回。
itemset(*args) 将标量插入数组(如果可能,标量会被转换为数组的数据类型)。
max([axis, out, keepdims, initial, where]) 沿着给定轴返回最大值。
mean([axis, dtype, out, keepdims, where]) 返回沿给定轴的数组元素的平均值。
min([axis, out, keepdims, initial, where]) 沿着给定轴返回最小值。
newbyteorder([new_order]) 返回使用不同字节顺序查看的相同数据的数组。
nonzero() 返回非零元素的索引。
partition(kth[, axis, kind, order]) 通过重新排列数组中的元素,使得第 k 个位置的元素的值在排序数组中的位置处于预期位置。
prod([axis, dtype, out, keepdims, initial, ...]) 返回沿给定轴的数组元素的乘积。
ptp([axis, out, keepdims]) 沿着给定轴的峰值到峰值(最大值 - 最小值)。
put(indices, values[, mode]) 设置a.flat[n] = values[n],其中n为 indices 中的所有值。
ravel([order]) 返回一个扁平化的数组。
repeat(repeats[, axis]) 重复数组的元素。
reshape(shape[, order]) 返回一个包含与新形状相同数据的数组。
resize(new_shape[, refcheck]) 原地改变数组的形状和大小。
round([decimals, out]) 返回每个元素舍入到给定小数位数的a
searchsorted(v[, side, sorter]) 查找应该在数组 a 中插入 v 元素以保持顺序的索引。
setfield(val, dtype[, offset]) 在由数据类型定义的字段的指定位置放入一个值。
setflags([write, align, uic]) 分别设置数组标志 WRITEABLE、ALIGNED、WRITEBACKIFCOPY。
sort([axis, kind, order]) 原地对数组进行排序。
squeeze([axis]) a 中删除长度为一的轴。
std([axis, dtype, out, ddof, keepdims, where]) 返回给定轴上数组元素的标准差。
sum([axis, dtype, out, keepdims, initial, where]) 返回给定轴上数组元素的总和。
swapaxes(axis1, axis2) 返回一个轴 axis1axis2 互换的数组视图。
take(indices[, axis, out, mode]) 返回由给定索引处 a 元素组成的数组。
tobytes([order]) 构造包含数组中原始数据字节的 Python 字节。
tofile(fid[, sep, format]) 将数组以文本或二进制(默认)形式写入文件。
tolist() 将数组作为 Python 标量的 a.ndim 级深嵌套列表返回。
tostring([order]) 一个与 tobytes 完全相同行为的兼容别名。
trace([offset, axis1, axis2, dtype, out]) 返回数组对角线上的总和。
transpose(*axes) 返回数组的轴转置视图。
var([axis, dtype, out, ddof, keepdims, where]) 返回给定轴上数组元素的方差。
view([dtype][, type]) 具有相同数据的数组的新视图。
dot
field

numpy.record

原文:numpy.org/doc/1.26/reference/generated/numpy.record.html

class numpy.record

数据类型标量,允许通过属性查找进行字段访问。

属性:

T

与相应的数组属性相同的标量属性。

base

基本对象

data

数据开头的指针。

dtype

数据类型对象

flags

标志的整数值

flat

标量的 1-D 视图。

imag

标量的虚部。

itemsize

一个元素的字节长度。

nbytes

标量的字节长度。

ndim

数组维度的数量。

real

标量的实部。

shape

数组维度的元组。

size

元素数量的数量。

strides

字节步骤的元组,每个维度。

方法

all 与相应的数组属性相同的标量方法。
any 与相应的数组属性相同的标量方法。
argmax 与相应的数组属性相同的标量方法。
argmin 与相应的数组属性相同的标量方法。
argsort 与相应的数组属性相同的标量方法。
astype 与相应的数组属性相同的标量方法。
byteswap 与相应的数组属性相同的标量方法。
choose 与相应的数组属性相同的标量方法。
clip 与相应的数组属性相同的标量方法。
compress 标量方法,与相应的数组属性相同。
conjugate 标量方法,与相应的数组属性相同。
copy 标量方法,与相应的数组属性相同。
cumprod 标量方法,与相应的数组属性相同。
cumsum 标量方法,与相应的数组属性相同。
diagonal 标量方法,与相应的数组属性相同。
dump 标量方法,与相应的数组属性相同。
dumps 标量方法,与相应的数组属性相同。
fill 标量方法,与相应的数组属性相同。
flatten 标量方法,与相应的数组属性相同。
getfield 标量方法,与相应的数组属性相同。
item 标量方法,与相应的数组属性相同。
itemset 标量方法,与相应的数组属性相同。
max 标量方法,与相应的数组属性相同。
mean 标量方法,与相应的数组属性相同。
min 标量方法,与相应的数组属性相同。
newbyteorder([new_order]) 返回一个具有不同字节顺序的新dtype
nonzero 标量方法,与相应的数组属性相同。
pprint() 高亮打印所有字段。
prod 标量方法,与相应的数组属性相同。
ptp 与相应数组属性相同的标量方法。
put 与相应数组属性相同的标量方法。
ravel 与相应数组属性相同的标量方法。
repeat 与相应数组属性相同的标量方法。
reshape 与相应数组属性相同的标量方法。
resize 与相应数组属性相同的标量方法。
round 与相应数组属性相同的标量方法。
searchsorted 与相应数组属性相同的标量方法。
setfield 与相应数组属性相同的标量方法。
setflags 与相应数组属性相同的标量方法。
sort 与相应数组属性相同的标量方法。
squeeze 与相应数组属性相同的标量方法。
std 与相应数组属性相同的标量方法。
sum 与相应数组属性相同的标量方法。
swapaxes 与相应数组属性相同的标量方法。
take 与相应数组属性相同的标量方法。
tofile 与相应数组属性相同的标量方法。
tolist 与相应数组属性相同的标量方法。
tostring 与相应数组属性相同的标量方法。
trace 与相应数组属性相同的标量方法。
transpose 与相应的数组属性相同的标量方法。
var 与相应的数组属性相同的标量方法。
view 与相应的数组属性相同的标量方法。
结合
tobytes

numpy.lib.user_array.container

原文:numpy.org/doc/1.26/reference/generated/numpy.lib.user_array.container.html

class numpy.lib.user_array.container(data, dtype=None, copy=True)

用于简单多重继承的标准容器类。

方法

复制
转为字符串
字节交换
转换类型

numpy.ndarray.flat

原文:numpy.org/doc/1.26/reference/generated/numpy.ndarray.flat.html

属性

ndarray.flat

数组上的一维迭代器。

这是一个numpy.flatiter实例,表现类似于,但不是 Python 内置迭代器对象的子类。

另请参阅

flatten

返回数组折叠成一维后的副本。

flatiter

例子

>>> x = np.arange(1, 7).reshape(2, 3)
>>> x
array([[1, 2, 3],
 [4, 5, 6]])
>>> x.flat[3]
4
>>> x.T
array([[1, 4],
 [2, 5],
 [3, 6]])
>>> x.T.flat[3]
5
>>> type(x.flat)
<class 'numpy.flatiter'> 

一个赋值的示例:

>>> x.flat = 3; x
array([[3, 3, 3],
 [3, 3, 3]])
>>> x.flat[[1,4]] = 1; x
array([[3, 1, 3],
 [3, 1, 3]]) 

numpy.ndenumerate

原文:numpy.org/doc/1.26/reference/generated/numpy.ndenumerate.html

class numpy.ndenumerate(arr)

多维索引迭代器。

返回一个迭代器,产生数组坐标和值的对。

参数:

arrndarray

输入数组。

另请参阅

ndindex, flatiter

示例

>>> a = np.array([[1, 2], [3, 4]])
>>> for index, x in np.ndenumerate(a):
...     print(index, x)
(0, 0) 1
(0, 1) 2
(1, 0) 3
(1, 1) 4 

numpy.broadcast

原文:numpy.org/doc/1.26/reference/generated/numpy.broadcast.html

class numpy.broadcast

生成一个模拟广播的对象。

参数:

in1, in2, …array_like

输入参数。

返回:

b广播对象

将输入参数相互广播,并返回一个封装结果的对象。 其中包括shapend属性,并且可以用作迭代器。

另请参阅

broadcast_arrays

broadcast_to

broadcast_shapes

示例

手动添加两个向量,使用广播:

>>> x = np.array([[1], [2], [3]])
>>> y = np.array([4, 5, 6])
>>> b = np.broadcast(x, y) 
>>> out = np.empty(b.shape)
>>> out.flat = [u+v for (u,v) in b]
>>> out
array([[5.,  6.,  7.],
 [6.,  7.,  8.],
 [7.,  8.,  9.]]) 

与内置广播进行比较:

>>> x + y
array([[5, 6, 7],
 [6, 7, 8],
 [7, 8, 9]]) 

属性:

index

广播结果中的当前索引

iters

self“components”上的迭代器元组。

nd

广播结果的维度数。

ndim

广播结果的维度数。

numiter

广播结果拥有的迭代器数量。

shape

广播结果的形状。

size

广播结果的总大小。

方法

reset() 重置广播结果的迭代器。

掩码数组

原文:numpy.org/doc/1.26/reference/maskedarray.html

掩码数组是可能具有缺失或无效条目的数组。numpy.ma 模块提供了一个几乎与 numpy 类似的替代品,支持带有掩码的数据数组。

  • numpy.ma 模块

    • 原因

    • 什么是掩码数组?

    • numpy.ma 模块

  • 使用 numpy.ma

    • 构建掩码数组

    • 访问数据

    • 访问掩码

    • 仅访问有效条目

    • 修改掩码

    • 索引和切片

    • 掩码数组操作

  • 示例

    • 具有表示缺失数据的给定值的数据

    • 填充缺失数据

    • 数值操作

    • 忽略极端值

  • numpy.ma 模块的常量

    • masked

    • nomask

    • masked_print_option

  • MaskedArray

    • MaskedArray

    • 掩码数组的属性和特性

  • MaskedArray 方法

    • 转换

    • 形状操作

    • 项目选择和操作

    • 序列化和复制

    • 计算

    • 算术和比较操作

    • 表示

    • 特殊方法

    • 特定方法

  • 掩码数组操作

    • 常量

    • 创建

    • 检查数组

    • 操作 MaskedArray

    • 掩码操作

    • 转换操作

    • 掩码数组算术运算

numpy.ma 模块

原文:numpy.org/doc/1.26/reference/maskedarray.generic.html

原因

掩码数组是可能具有缺失或无效条目的数组。numpy.ma 模块提供了一个几乎与 numpy 类似的替代品,支持带有掩码的数据数组。

什么是掩码数组?

在许多情况下,数据集可能是不完整的或受到无效数据的影响。例如,传感器可能未能记录数据,或记录了无效值。numpy.ma 模块通过引入掩码数组提供了一种方便的解决此问题的方式。

掩码数组是标准numpy.ndarray和掩码的组合。掩码可以是nomask,表示关联数组的没有值是无效的,或者是一个布尔数组,用于确定关联数组的每个元素是否有效。当掩码的元素为False时,关联数组的相应元素是有效的,称为未掩码。当掩码的元素为True时,关联数组的相应元素被称为掩码(无效)。

该包确保不使用掩码条目进行计算。

举个例子,让我们考虑以下数据集:

>>> import numpy as np
>>> import numpy.ma as ma
>>> x = np.array([1, 2, 3, -1, 5]) 

我们希望将第四个条目标记为无效。最简单的方法是创建一个掩码数组:

>>> mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0]) 

现在我们可以计算数据集的均值,而不考虑无效数据:

>>> mx.mean()
2.75 

numpy.ma 模块

numpy.ma 模块的主要特点是MaskedArray 类,它是numpy.ndarray的子类。该类及其属性和方法在 MaskedArray 类部分中有更详细的描述。

numpy.ma 模块可以作为numpy的补充使用:

>>> import numpy as np
>>> import numpy.ma as ma 

要创建一个使第二个元素无效的数组,我们可以这样做:

>>> y = ma.array([1, 2, 3], mask = [0, 1, 0]) 

要创建一个所有接近 1.e20 的值无效的掩码数组,我们可以这样做:

>>> z = ma.masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20) 

有关创建掩码数组的方法的完整讨论,请参见构建掩码数组部分。

原因

掩码数组是可能具有缺失或无效条目的数组。numpy.ma 模块提供了一个几乎与 numpy 类似的替代品,支持带有掩码的数据数组。

什么是掩码数组?

在许多情况下,数据集可能是不完整的或受到无效数据的影响。例如,传感器可能未能记录数据,或记录了无效值。numpy.ma模块通过引入掩码数组提供了一种方便的解决此问题的方式。

一个掩码数组是标准numpy.ndarray和一个掩码的组合。掩码可以是nomask,表示关联数组的没有值是无效的,或者是一个布尔数组,用于确定关联数组的每个元素是否有效。当掩码的元素为False时,关联数组的相应元素是有效的,称为未掩码。当掩码的元素为True时,关联数组的相应元素被称为掩码(无效)。

该包确保不使用掩码条目进行计算。

举个例子,让我们考虑以下数据集:

>>> import numpy as np
>>> import numpy.ma as ma
>>> x = np.array([1, 2, 3, -1, 5]) 

我们希望将第四个条目标记为无效。最简单的方法是创建一个掩码数组:

>>> mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0]) 

现在我们可以计算数据集的均值,而不考虑无效数据:

>>> mx.mean()
2.75 

numpy.ma模块

numpy.ma模块的主要特点是MaskedArray类,它是numpy.ndarray的子类。该类及其属性和方法在 MaskedArray 类部分有更详细的描述。

numpy.ma模块可以作为numpy的补充使用:

>>> import numpy as np
>>> import numpy.ma as ma 

创建一个第二个元素无效的数组,我们可以这样做:

>>> y = ma.array([1, 2, 3], mask = [0, 1, 0]) 

创建一个掩码数组,其中所有接近1.e20的值都是无效的,我们可以这样做:

>>> z = ma.masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20) 

有关创建掩码数组的方法的完整讨论,请参见构建掩码数组部分。

使用 numpy.ma

构建掩码数组

有几种方法可以构建掩码数组。

  • 第一种可能性是直接调用MaskedArray类。

  • 第二种可能性是使用两个掩码数组构造函数,arraymasked_array

    array(data[, dtype, copy, order, mask, ...]) 一个可能包含掩码值的数组类。
    masked_array MaskedArray的别名
  • 第三个选项是查看现有数组的视图。在这种情况下,如果数组没有命名字段,则视图的掩码设置为nomask,否则为与数组结构相同的布尔数组。

    >>> x = np.array([1, 2, 3])
    >>> x.view(ma.MaskedArray)
    masked_array(data=[1, 2, 3],
     mask=False,
     fill_value=999999)
    >>> x = np.array([(1, 1.), (2, 2.)], dtype=[('a',int), ('b', float)])
    >>> x.view(ma.MaskedArray)
    masked_array(data=[(1, 1.0), (2, 2.0)],
     mask=[(False, False), (False, False)],
     fill_value=(999999, 1.e+20),
     dtype=[('a', '<i8'), ('b', '<f8')]) 
    
  • 另一种可能性是使用以下任一函数:

    asarray(a[, dtype, order]) 将输入转换为给定数据类型的掩码数组。
    asanyarray(a[, dtype]) 将输入转换为掩码数组,保留子类。
    fix_invalid(a[, mask, copy, fill_value]) 返回输入,使无效数据被掩码并替换为填充值。
    masked_equal(x, value[, copy]) 掩码一个等于给定值的数组。
    masked_greater(x, value[, copy]) 掩码一个大于给定值的数组。
    masked_greater_equal(x, value[, copy]) 掩码一个大于或等于给定值的数组。
    masked_inside(x, v1, v2[, copy]) 掩码在给定区间内的数组。
    masked_invalid(a[, copy]) 掩码出现无效值(NaN 或 infs)的数组。
    masked_less(x, value[, copy]) 掩码一个小于给定值的数组。
    masked_less_equal(x, value[, copy]) 掩码一个小于或等于给定值的数组。
    masked_not_equal(x, value[, copy]) 掩码一个等于给定值的数组。
    masked_object(x, value[, copy, shrink]) 掩码数组x,其中数据与值完全相等。
    masked_outside(x, v1, v2[, copy]) 掩码在给定区间外的数组。
    masked_values(x, value[, rtol, atol, copy, ...]) 使用浮点数相等性进行掩码。
    masked_where(condition, a[, copy]) 在满足条件的情况下掩盖数组。

访问数据

可以通过多种方式访问掩码数组的底层数据:

  • 通过data属性。输出为numpy.ndarray或其子类的视图,取决于掩码数组创建时底层数据的类型。

  • 通过__array__方法。然后输出为numpy.ndarray

  • 直接将掩码数组视为numpy.ndarray或其子类之一(实际上使用data属性时的操作)。

  • 通过getdata函数。

如果某些条目被标记为无效,则这些方法都不完全令人满意。一般规则是,在需要表示数组而没有任何掩码条目的情况下,建议使用filled方法填充数组。

访问掩码

掩码数组的掩码可通过其mask属性访问。必须记住,掩码中的True条目表示无效数据。

另一种可能性是使用getmaskgetmaskarray函数。如果x是掩码数组,则getmask(x)输出x的掩码,否则输出特殊值nomask。如果x没有无效条目或不是掩码数组,则该函数输出一个具有与x相同数量元素的False布尔数组。

仅访问有效条目

要仅检索有效条目,可以使用掩码的反向作为索引。可以使用numpy.logical_not函数或简单地使用~运算符来计算掩码的反向:

>>> x = ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]])
>>> x[~x.mask]
masked_array(data=[1, 4],
 mask=[False, False],
 fill_value=999999) 

另一种检索有效数据的方法是使用compressed方法,它返回一个一维ndarray(或其子类之一,取决于baseclass属性的值):

>>> x.compressed()
array([1, 4]) 

请注意,compressed的输出始终为 1D。

修改掩码

屏蔽条目

将一个或多个特定条目标记为无效的推荐方法是将特殊值masked分配给它们:

>>> x = ma.array([1, 2, 3])
>>> x[0] = ma.masked
>>> x
masked_array(data=[--, 2, 3],
 mask=[ True, False, False],
 fill_value=999999)
>>> y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> y[(0, 1, 2), (1, 2, 0)] = ma.masked
>>> y
masked_array(
 data=[[1, --, 3],
 [4, 5, --],
 [--, 8, 9]],
 mask=[[False,  True, False],
 [False, False,  True],
 [ True, False, False]],
 fill_value=999999)
>>> z = ma.array([1, 2, 3, 4])
>>> z[:-2] = ma.masked
>>> z
masked_array(data=[--, --, 3, 4],
 mask=[ True,  True, False, False],
 fill_value=999999) 

第二种可能性是直接修改mask,但不建议使用此方法。

注意

当使用简单的非结构化数据类型创建新的掩码数组时,掩码最初设置为特殊值nomask,大致对应于布尔值False。尝试设置nomask的元素将导致TypeError异常,因为布尔值不支持项目赋值。

可以通过将True赋值给掩码来一次性屏蔽数组的所有条目:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x.mask = True
>>> x
masked_array(data=[--, --, --],
 mask=[ True,  True,  True],
 fill_value=999999,
 dtype=int64) 

最后,可以通过将一系列布尔值赋给掩码来屏蔽和/或解除屏蔽特定条目:

>>> x = ma.array([1, 2, 3])
>>> x.mask = [0, 1, 0]
>>> x
masked_array(data=[1, --, 3],
 mask=[False,  True, False],
 fill_value=999999) 

解除掩码的条目

要解除一个或多个特定条目的掩码,我们只需将一个或多个新的有效值分配给它们:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

注意

直接赋值解除掩码的条目,如果掩码数组有掩码,则会悄悄失败,如hardmask属性所示。此功能是为了防止覆盖掩码。要强制解除数组具有硬掩码的条目的掩码,必须首先使用soften_mask方法软化掩码,然后再分配。可以使用harden_mask重新硬化:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True)
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.soften_mask()
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999)
>>> x.harden_mask()
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

要解除掩码数组的所有屏蔽条目(前提是掩码不是硬掩码),最简单的解决方案是将常量nomask分配给掩码:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.mask = ma.nomask
>>> x
masked_array(data=[1, 2, 3],
 mask=[False, False, False],
 fill_value=999999) 

索引和切片

由于MaskedArraynumpy.ndarray的子类,它继承了其用于索引和切片的机制。

当访问没有命名字段的掩码数组的单个条目时,输出要么是一个标量(如果掩码的对应条目为False),要么是特殊值masked(如果掩码的对应条目为True):

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x[0]
1
>>> x[-1]
masked
>>> x[-1] is ma.masked
True 

如果掩码数组具有命名字段,访问单个条目会返回一个numpy.void对象,如果没有字段被掩码,或者如果至少有一个字段被掩码,则返回一个与初始数组相同 dtype 的 0d 掩码数组。

>>> y = ma.masked_array([(1,2), (3, 4)],
...                mask=[(0, 0), (0, 1)],
...               dtype=[('a', int), ('b', int)])
>>> y[0]
(1, 2)
>>> y[-1]
(3, --) 

当访问切片时,输出是一个掩码数组,其data属性是原始数据的视图,其掩码要么是nomask(如果原始数组中没有无效条目),要么是原始掩码对应切片的视图。视图是必需的,以确保任何对掩码的修改传播到原始数据。

>>> x = ma.array([1, 2, 3, 4, 5], mask=[0, 1, 0, 0, 1])
>>> mx = x[:3]
>>> mx
masked_array(data=[1, --, 3],
 mask=[False,  True, False],
 fill_value=999999)
>>> mx[1] = -1
>>> mx
masked_array(data=[1, -1, 3],
 mask=[False, False, False],
 fill_value=999999)
>>> x.mask
array([False, False, False, False,  True])
>>> x.data
array([ 1, -1,  3,  4,  5]) 

使用带有结构化数据类型的掩码数组的字段会返回一个MaskedArray

掩码数组的操作

掩码数组支持算术和比较操作。尽可能地,掩码数组的无效条目不会被处理,这意味着相应的data条目在操作之前和之后应该是相同的。

警告

我们需要强调这种行为可能不是系统的,掩码数据在某些情况下可能会受到操作的影响,因此用户不应依赖于这些数据保持不变。

numpy.ma模块提供了大多数 ufunc 的特定实现。具有有效域的一元和二元函数(如logdivide)在输入被掩码或超出有效域时返回masked常量:

>>> ma.log([-1, 0, 1, 2])
masked_array(data=[--, --, 0.0, 0.6931471805599453],
 mask=[ True,  True, False, False],
 fill_value=1e+20) 

掩码数组还支持标准的 numpy ufuncs。输出结果是一个掩码数组。一元 ufunc 的结果在输入被掩码的地方也被掩码。二元 ufunc 的结果在任何输入被掩码的地方也被掩码。如果 ufunc 还返回可选的上下文输出(包含 ufunc 名称、其参数和其域的 3 元组),则上下文被处理,并且输出掩码数组的条目在对应的输入超出有效域的地方被掩码:

>>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1])
>>> np.log(x)
masked_array(data=[--, 0.0, --, 0.6931471805599453, --],
 mask=[ True, False,  True, False,  True],
 fill_value=1e+20) 

构建掩码数组

有几种构建掩码数组的方法。

  • 第一种可能性是直接调用MaskedArray类。

  • 第二种可能性是使用两个掩码数组构造函数,arraymasked_array

    array(data[, dtype, copy, order, mask, ...]) 具有可能存在掩码值的数组类。
    masked_array MaskedArray 的别名
  • 第三种选择是查看现有数组的视图。在这种情况下,如果数组没有命名字段,则视图的掩码设置为 nomask,否则��数组结构相同的布尔数组。

    >>> x = np.array([1, 2, 3])
    >>> x.view(ma.MaskedArray)
    masked_array(data=[1, 2, 3],
     mask=False,
     fill_value=999999)
    >>> x = np.array([(1, 1.), (2, 2.)], dtype=[('a',int), ('b', float)])
    >>> x.view(ma.MaskedArray)
    masked_array(data=[(1, 1.0), (2, 2.0)],
     mask=[(False, False), (False, False)],
     fill_value=(999999, 1.e+20),
     dtype=[('a', '<i8'), ('b', '<f8')]) 
    
  • 还有另一种可能性是使用以下任一函数之一:

    asarray(a[, dtype, order]) 将输入转换为给定数据类型的掩码数组。
    asanyarray(a[, dtype]) 将输入转换为掩码数组,保留子类。
    fix_invalid(a[, mask, copy, fill_value]) 返回通过掩码和用填充值替换的无效数据的输入。
    masked_equal(x, value[, copy]) 对等于给定值的数组进行掩码处理。
    masked_greater(x, value[, copy]) 对大于给定值的数组进行掩码处理。
    masked_greater_equal(x, value[, copy]) 对大于或等于给定值的数组进行掩码处理。
    masked_inside(x, v1, v2[, copy]) 对给定区间内的数组进行掩码处理。
    masked_invalid(a[, copy]) 对出现无效值(NaN 或 inf)的数组进行掩码处理。
    masked_less(x, value[, copy]) 对小于给定值的数组进行掩码处理。
    masked_less_equal(x, value[, copy]) 对小于或等于给定值的数组进行掩码处理。
    masked_not_equal(x, value[, copy]) 对不等于给定值的数组进行掩码处理。
    masked_object(x, value[, copy, shrink]) 对数组x中数据完全等于 value 的部分进行掩码处理。
    masked_outside(x, v1, v2[, copy]) 对给定区间外的数组进行掩码处理。
    masked_values(x, value[, rtol, atol, copy, ...]) 使用浮点数相等性进行掩码处理。
    masked_where(condition, a[, copy]) 在满足条件的情况下对数组进行掩码处理。

访问数据

掩码数组的底层数据可以通过多种方式访问:

  • 通过data属性。输出是数组的视图,作为numpy.ndarray或其子类之一,取决于创建掩码数组时底层数据的类型。

  • 通过__array__方法。输出是一个numpy.ndarray

  • 通过直接将掩码数组视为numpy.ndarray或其子类之一(实际上使用data属性时所做的操作)。

  • 通过使用getdata函数。

如果一些条目被标记为无效,则这些方法都不完全令人满意。一般规则是,在需要没有任何掩码条目的数组表示时,建议使用filled方法填充数组。

访问掩码

掩码数组的掩码通过其mask属性可访问。我们必须记住,掩码中的True条目表示无效数据。

另一种可能性是使用getmaskgetmaskarray函数。如果x是一个掩码数组,getmask(x)输出x的掩码,否则输出特殊值nomaskgetmaskarray(x)输出x的掩码,如果x是一个掩码数组。如果x没有无效条目或不是一个掩码数组,则该函数输出一个具有与x相同数量元素的False布尔数组。

仅访问有效条目

要仅检索有效条目,我们可以使用遮罩的反向作为索引。可以使用numpy.logical_not函数或简单地使用~运算符计算遮罩的反向:

>>> x = ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]])
>>> x[~x.mask]
masked_array(data=[1, 4],
 mask=[False, False],
 fill_value=999999) 

检索有效数据的另一种方法是使用compressed方法,该方法返回一个一维ndarray(或其子类之一,取决于baseclass属性的值):

>>> x.compressed()
array([1, 4]) 

请注意,compressed的输出始终为 1D。

修改遮罩

遮罩一个条目

推荐的标记一个或多个特定条目为无效的遮罩数组的方法是将特殊值masked分配给它们:

>>> x = ma.array([1, 2, 3])
>>> x[0] = ma.masked
>>> x
masked_array(data=[--, 2, 3],
 mask=[ True, False, False],
 fill_value=999999)
>>> y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> y[(0, 1, 2), (1, 2, 0)] = ma.masked
>>> y
masked_array(
 data=[[1, --, 3],
 [4, 5, --],
 [--, 8, 9]],
 mask=[[False,  True, False],
 [False, False,  True],
 [ True, False, False]],
 fill_value=999999)
>>> z = ma.array([1, 2, 3, 4])
>>> z[:-2] = ma.masked
>>> z
masked_array(data=[--, --, 3, 4],
 mask=[ True,  True, False, False],
 fill_value=999999) 

第二种可能性是直接修改mask,但不建议使用此用法。

注意

当使用简单的非结构化数据类型创建新的遮罩数组时,遮罩最初设置为特殊值nomask,大致对应于布尔值False。尝试设置nomask的元素将导致TypeError异常,因为布尔值不支持项目分配。

可以通过将True分配给遮罩一次性遮罩数组的所有条目:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x.mask = True
>>> x
masked_array(data=[--, --, --],
 mask=[ True,  True,  True],
 fill_value=999999,
 dtype=int64) 

最后,可以通过将一系列布尔值分配给掩码来遮罩和/或取消遮罩特定条目:

>>> x = ma.array([1, 2, 3])
>>> x.mask = [0, 1, 0]
>>> x
masked_array(data=[1, --, 3],
 mask=[False,  True, False],
 fill_value=999999) 

取消遮罩一个条目

要取消遮罩一个或多个特定条目,只需将一个或多个新的有效值分配给它们:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

注意

通过直接分配来取消遮罩一个条目,如果遮罩数组有遮罩,将会悄无声息地失败,如hardmask属性所示。此功能是为了防止覆盖遮罩而引入的。要强制取消遮罩一个条目,其中数组有硬遮罩,必须首先使用soften_mask方法软化遮罩,然后再分配。可以使用harden_mask重新硬化:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True)
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.soften_mask()
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999)
>>> x.harden_mask()
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

要取消遮罩遮罩数组的所有遮罩条目(假设遮罩不是硬遮罩),最简单的解决方案是将常量nomask分配给遮罩:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.mask = ma.nomask
>>> x
masked_array(data=[1, 2, 3],
 mask=[False, False, False],
 fill_value=999999) 

遮罩一个条目

将一个或多个特定条目标记为无效的推荐方法是将特殊值masked分配给它们:

>>> x = ma.array([1, 2, 3])
>>> x[0] = ma.masked
>>> x
masked_array(data=[--, 2, 3],
 mask=[ True, False, False],
 fill_value=999999)
>>> y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> y[(0, 1, 2), (1, 2, 0)] = ma.masked
>>> y
masked_array(
 data=[[1, --, 3],
 [4, 5, --],
 [--, 8, 9]],
 mask=[[False,  True, False],
 [False, False,  True],
 [ True, False, False]],
 fill_value=999999)
>>> z = ma.array([1, 2, 3, 4])
>>> z[:-2] = ma.masked
>>> z
masked_array(data=[--, --, 3, 4],
 mask=[ True,  True, False, False],
 fill_value=999999) 

第二种可能性是直接修改mask,但不建议这样使用。

注意

当使用简单的非结构化数据类型创建新的掩盖数组时,掩盖最初设置为特殊值nomask,大致对应于布尔值False。尝试设置nomask的元素将导致TypeError异常,因为布尔值不支持项目分配。

可以通过将True分配给掩盖来一次性掩盖数组的所有条目:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x.mask = True
>>> x
masked_array(data=[--, --, --],
 mask=[ True,  True,  True],
 fill_value=999999,
 dtype=int64) 

最后,可以通过将一系列布尔值分配给掩盖来掩盖和/或取消掩盖特定条目:

>>> x = ma.array([1, 2, 3])
>>> x.mask = [0, 1, 0]
>>> x
masked_array(data=[1, --, 3],
 mask=[False,  True, False],
 fill_value=999999) 

取消掩盖一个条目

要取消掩盖一个或多个特定条目,我们只需将一个或多个新的有效值分配给它们:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

注意

如果掩盖数组具有掩盖,则通过直接分配取消掩盖一个条目将悄悄失败,如hardmask属性所示。此功能旨在防止覆盖掩盖。要强制取消掩盖数组具有硬掩盖的条目,必须首先使用soften_mask方法软化掩盖,然后再分配。可以使用harden_mask重新硬化:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True)
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.soften_mask()
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x[-1] = 5
>>> x
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999)
>>> x.harden_mask()
masked_array(data=[1, 2, 5],
 mask=[False, False, False],
 fill_value=999999) 

要取消掩盖数组的所有掩盖条目(假设掩盖不是硬掩盖),最简单的解决方案是将常量nomask分配给掩盖:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data=[1, 2, --],
 mask=[False, False,  True],
 fill_value=999999)
>>> x.mask = ma.nomask
>>> x
masked_array(data=[1, 2, 3],
 mask=[False, False, False],
 fill_value=999999) 

索引和切片

由于MaskedArraynumpy.ndarray的子类,它继承了其用于索引和切片的机制。

当访问没有命名字段的掩盖数组的单个条目时,输出要么是标量(如果掩盖的相应条目为False),要么是特殊值masked(如果掩盖的相应条目为True):

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x[0]
1
>>> x[-1]
masked
>>> x[-1] is ma.masked
True 

如果掩盖数组具有命名字段,访问单个条目将返回一个numpy.void对象,如果没有字段被掩盖,或者如果至少有一个字段被掩盖,则返回一个与初始数组相同 dtype 的 0d 掩盖数组。

>>> y = ma.masked_array([(1,2), (3, 4)],
...                mask=[(0, 0), (0, 1)],
...               dtype=[('a', int), ('b', int)])
>>> y[0]
(1, 2)
>>> y[-1]
(3, --) 

当访问一个切片时,输出是一个掩码数组,其data属性是原始数据的视图,其掩码要么是nomask(如果原始数组中没有无效条目),要么是原始掩码对应切片的视图。视图是必需的,以确保任何对掩码的修改传播到原始数据。

>>> x = ma.array([1, 2, 3, 4, 5], mask=[0, 1, 0, 0, 1])
>>> mx = x[:3]
>>> mx
masked_array(data=[1, --, 3],
 mask=[False,  True, False],
 fill_value=999999)
>>> mx[1] = -1
>>> mx
masked_array(data=[1, -1, 3],
 mask=[False, False, False],
 fill_value=999999)
>>> x.mask
array([False, False, False, False,  True])
>>> x.data
array([ 1, -1,  3,  4,  5]) 

使用结构化数据类型的掩码数组访问字段会返回一个MaskedArray

操作掩码数组

掩码数组支持算术和比较运算。尽可能地,掩码数组的无效条目不会被处理,这意味着相应的data条目在操作前后应该是相同的。

警告

我们需要强调这种行为可能不是系统性的,在某些情况下掩码数据可能会受到操作的影响,因此用户不应依赖于这些数据保持不变。

numpy.ma 模块提供了大多数 ufuncs 的特定实现。具有有效域的一元和二元函数(如logdivide)在输入被掩码或超出有效域时返回masked常量:

>>> ma.log([-1, 0, 1, 2])
masked_array(data=[--, --, 0.0, 0.6931471805599453],
 mask=[ True,  True, False, False],
 fill_value=1e+20) 

掩码数组还支持标准的 numpy ufuncs。输出是一个掩码数组。一元 ufunc 的结果在输入被掩码的地方也被掩码。二元 ufunc 的结果在任何输入被掩码的地方也被掩码。如果 ufunc 还返回可选的上下文输出(包含 ufunc 名称、其参数和其域的 3 元组),则上下文会被处理,并且输出掩码数组的条目在对应输入超出有效域的地方也被掩码:

>>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1])
>>> np.log(x)
masked_array(data=[--, 0.0, --, 0.6931471805599453, --],
 mask=[ True, False,  True, False,  True],
 fill_value=1e+20) 

示例

具有表示缺失数据的给定值的数据

让我们考虑一个元素列表 x,其中值为 -9999 代表缺失数据。我们希望计算数据的平均值和异常向量(与平均值的偏差):

>>> import numpy.ma as ma
>>> x = [0.,1.,-9999.,3.,4.]
>>> mx = ma.masked_values (x, -9999.)
>>> print(mx.mean())
2.0
>>> print(mx - mx.mean())
[-2.0 -1.0 -- 1.0 2.0]
>>> print(mx.anom())
[-2.0 -1.0 -- 1.0 2.0] 

填补缺失数据

现在假设我们希望打印相同的数据,但将缺失值替换为平均值。

>>> print(mx.filled(mx.mean()))
[0\.  1\.  2\.  3\.  4.] 

数值运算

数值运算可以轻松进行,无需担心缺失值、除以零、负数的平方根等问题:

>>> import numpy.ma as ma
>>> x = ma.array([1., -1., 3., 4., 5., 6.], mask=[0,0,0,0,1,0])
>>> y = ma.array([1., 2., 0., 4., 5., 6.], mask=[0,0,0,0,0,1])
>>> print(ma.sqrt(x/y))
[1.0 -- -- 1.0 -- --] 

输出的四个值是无效的:第一个来自对负数取平方根,第二个来自除以零,最后两个是输入被掩码的情况。

忽略极端值

假设我们有一个浮点数数组d,取值范围在 0 到 1 之间。我们希望计算d的值的平均值,同时忽略范围在[0.2, 0.9]之外的任何数据:

>>> d = np.linspace(0, 1, 20)
>>> print(d.mean() - ma.masked_outside(d, 0.2, 0.9).mean())
-0.05263157894736836 

具有表示缺失数据的给定值的数据

假设我们有一个元素列表x,其中数值为-9999 表示缺失数据。我们希望计算数据的平均值和异常向量(与平均值的偏差):

>>> import numpy.ma as ma
>>> x = [0.,1.,-9999.,3.,4.]
>>> mx = ma.masked_values (x, -9999.)
>>> print(mx.mean())
2.0
>>> print(mx - mx.mean())
[-2.0 -1.0 -- 1.0 2.0]
>>> print(mx.anom())
[-2.0 -1.0 -- 1.0 2.0] 

填充缺失数据

现在假设我们希望打印相同的数据,但将缺失值替换为平均值。

>>> print(mx.filled(mx.mean()))
[0\.  1\.  2\.  3\.  4.] 

数值运算

数值运算可以轻松进行,无需担心缺失值、除以零、负数的平方根等:

>>> import numpy.ma as ma
>>> x = ma.array([1., -1., 3., 4., 5., 6.], mask=[0,0,0,0,1,0])
>>> y = ma.array([1., 2., 0., 4., 5., 6.], mask=[0,0,0,0,0,1])
>>> print(ma.sqrt(x/y))
[1.0 -- -- 1.0 -- --] 

输出的四个值是无效的:第一个来自于对负数开平方,第二个来自于除以零,最后两个是输入被屏蔽的情况。

忽略极端值

假设我们有一个浮点数数组d,取值范围在 0 到 1 之间。我们希望计算d的值的平均值,同时忽略范围在[0.2, 0.9]之外的任何数据:

>>> d = np.linspace(0, 1, 20)
>>> print(d.mean() - ma.masked_outside(d, 0.2, 0.9).mean())
-0.05263157894736836 
posted @ 2024-06-24 14:57  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报