标准数组子类

注解

子类a numpy.ndarray 是可能的,但是如果您的目标是使用 被改进的 行为,就像用于分布式计算的dask数组和用于基于GPU的计算的cupy数组一样,不鼓励子类化。相反,使用numpy的 dispatch mechanism 建议使用。

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

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

特殊属性和方法

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

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

1.13 新版功能.

任何类,无论是否是ndarray子类,都可以定义此方法或将其设置为None,以便重写NumPy的ufuncs的行为。这与Python的工作原理非常相似 __mul__ 以及其他二进制操作程序。

  • ufunc 是调用的UFUNC对象。

  • 方法 是一个字符串,指示调用了哪个ufunc方法(其中一个 "__call__""reduce""reduceat""accumulate""outer""inner"

  • 输入 是输入参数的元组 ufunc .

  • 关键字参数 是包含ufunc的可选输入参数的字典。如果给出,任何 out 位置参数和关键字参数都作为 tuple 在里面 关键字参数 . 请参阅中的讨论 通用函数 (ufunc ) 有关详细信息。

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

如果一个输入或输出参数具有 __array_ufunc__ 方法,执行 相反 关于UFUNC的。如果多个参数实现 __array_ufunc__ ,按照以下顺序进行尝试:子类在超类之前,输入在输出之前,否则从左到右。第一个程序返回除 NotImplemented 确定结果。如果所有的 __array_ufunc__ 操作返回 NotImplemented ,A TypeError 提高了。

注解

我们打算将numpy函数重新实现为(通用的)ufunc,在这种情况下,它们可能会被 __array_ufunc__ 方法。主要候选人是 matmul 它目前不是一个UFunc,但相对而言可以很容易地重写为一组通用的UFunc。同样的情况也可能发生在诸如 medianaminargsort .

就像在Python中的其他一些特殊方法一样,例如 __hash____iter__ ,可以指示您的班级 not 通过设置支持UFUNC __array_ufunc__ = None . 不明飞行物总是上升 TypeError 当对设置 __array_ufunc__ = None .

存在 __array_ufunc__ 也会影响 ndarray 处理二进制操作 arr + objarr < obj 什么时候? arr 是一个 ndarrayobj 是自定义类的实例。有两种可能性。如果 obj.__array_ufunc__ 是存在的,而不是没有 ndarray.__add__ 朋友们会委托给UFUNC机器,这意味着 arr + obj 变成 np.add(arr, obj) 然后 add 调用 obj.__array_ufunc__ . 如果您想定义一个像数组一样的对象,这是很有用的。

或者,如果 obj.__array_ufunc__ 设置为“无”,则作为特例,特殊方法 ndarray.__add__ 会注意到这个和 无条件地 提升 TypeError . 如果要创建通过二进制操作与数组交互但本身不是数组的对象,则此选项非常有用。例如,一个单元处理系统可能有一个对象 m 表示“米”单位,并希望支持语法 arr * m 表示阵列的单位为“米”,但不希望通过UFUNC或其他方式与阵列交互。这可以通过设置 __array_ufunc__ = None 定义 __mul____rmul__ 方法。(注意,这意味着 __array_ufunc__ 总会回来的 NotImplemented 与设置不完全相同 __array_ufunc__ = None :在前一种情况下, arr + obj 将提高 TypeError ,而在后一种情况下,可以定义 __radd__ 防止这种情况发生的方法。)

以上内容不适用于就地操作人员,因为 ndarray 永不回来 NotImplemented . 因此, arr += obj 总是会导致 TypeError . 这是因为对于就地数组,不能用简单的反向操作一般地替换操作。(例如,默认情况下, arr += obj 会被翻译成 arr = arr + obj ,即 arr 将被替换,与就地数组操作的预期相反。)

注解

如果你定义 __array_ufunc__

  • 如果您不是 ndarray ,我们建议您的类定义特殊方法,例如 __add____lt__ 就像恩达瑞一样,他是UFUNC的代表。一个简单的方法是从 NDArrayOperatorsMixin .

  • 如果你是子类 ndarray ,我们建议您将所有重写逻辑放入 __array_ufunc__ 也不重写特殊方法。这样可以确保类层次结构只在一个位置确定,而不是由ufunc机制和二进制操作规则(优先选择子类的特殊方法;强制一个仅在一个位置的层次结构的替代方法,即设置 __array_ufunc__ 对None来说,这似乎是非常意外的,因此很混乱,因为这样子类就根本不能与ufuncs一起工作了)。

  • ndarray 定义自己 __array_ufunc__ ,如果没有参数具有重写,则计算ufunc,并返回 NotImplemented 否则。这可能对以下子类很有用 __array_ufunc__ 将其自己类的任何实例转换为 ndarray :然后它可以使用 super().__array_ufunc__(*inputs, **kwargs) ,最后在可能的反向转换之后返回结果。这种做法的好处在于,它可以确保有一个扩展行为的子类层次结构。见 Subclassing ndarray 有关详细信息。

注解

如果类定义了 __array_ufunc__ 方法,这将禁用 __array_wrap____array_prepare____array_priority__ 下面描述的UFUNC机制(最终可能会被否决)。

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

1.16 新版功能.

注解

  • 在NUMPY1.17中,默认情况下启用该协议,但可以使用 NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0 .

  • 在numpy1.16中,需要设置环境变量 NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1 在导入NumPy之前使用NumPy函数覆盖。

  • 最终,我希望 __array_function__ 始终启用。

  • func 是NumPy的公共API公开的任意可调用函数,该函数以 func(*args, **kwargs) .

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

  • 元组 args 还有迪克特 kwargs 直接从原始调用传递。

为了方便 __array_function__ 实施者, types 为所有参数类型提供 '__array_function__' 属性。这使得实现人员能够快速确定他们应该遵从的案例 __array_function__ 其他参数的实现。实现不应该依赖于 types .

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

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

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

如果这些条件成立, __array_function__ 应该返回调用其实现的结果 func(*args, **kwargs) . 否则,它应该返回sentinel值 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_FUNCTIONS[func](*args, **kwargs)

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__ 要包括的实现 all 相应NumPy函数的可选参数(例如。, broadcast_to 上面省略了不相关的 subok 参数)。可选参数只传递给 __array_function__ 如果它们在NumPy函数调用中显式使用。

就像内置的特殊方法一样 __add__ ,正确书写 __array_function__ 方法应该总是返回 NotImplemented 遇到未知类型时。否则,如果操作还包含一个对象,则无法从另一个对象正确重写NumPy函数。

在很大程度上 __array_function__ 把这些和 __array_ufunc__ .特别地:

  • NumPy将收集 __array_function__ 从所有指定的输入并按顺序调用它们:子类在超类之前,否则从左到右。注意,在一些涉及子类的边缘情况下,这与 current behavior 关于 Python 。

  • 的实现 __array_function__ 指示它们可以通过返回除 NotImplemented .

  • 如果全部 __array_function__ 方法返回 NotImplemented ,NumPy将提高 TypeError .

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

与当前行为的一个偏差 __array_ufunc__ 是不是说NumPy只会打电话给你 __array_function__第一 每个唯一类型的参数。这和Python的吻合 rule for calling reflected methods ,这样可以确保即使存在大量重载参数,检查重载也具有可接受的性能。

class.__array_finalize__(obj)

每当系统内部从 obj 在哪里 obj 是的子类(子类型) ndarray . 它可用于更改 self 在构造之后(例如为了确保2-D矩阵),或者从“parent.”子类中更新元信息,子类继承了这个方法的默认实现,它什么也不做。

class.__array_prepare__(array, context=None)

在每个 ufunc ,对具有最高数组优先级的输入对象或指定了输出对象的输出对象调用此方法。将传入输出数组,并将返回的内容传递给ufunc。子类继承此方法的默认实现,它只返回未修改的输出数组。子类可以选择使用此方法将输出数组转换为子类的实例,并在将数组返回到ufunc进行计算之前更新元数据。

注解

对于不明飞行物,希望最终取消这种方法,以利于 __array_ufunc__ .

class.__array_wrap__(array, context=None)

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

注解

对于不明飞行物,希望最终取消这种方法,以利于 __array_ufunc__ .

class.__array_priority__

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

注解

对于不明飞行物,希望最终取消这种方法,以利于 __array_ufunc__ .

class.__array__([dtype])

如果一个类(ndarray子类或非ndarray子类)具有 __array__ 方法用作 ufunc ,结果将 not 写入返回的对象 __array__ . 这种做法会回来的 TypeError .

矩阵对象

注解

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

matrix 对象继承自ndarray,因此它们具有与ndarray相同的属性和方法。然而,矩阵对象有六个重要的区别,当使用矩阵时可能会导致意外的结果,但期望它们像数组一样工作:

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

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

  3. 矩阵对象乘乘乘乘法成为矩阵乘法。 对于您可能希望接收矩阵的函数,请确保您了解这一点。特别是当m是矩阵时,asAnyArray(m)返回一个矩阵。

  4. 矩阵对象的乘骑能力被矩阵提升到一个功率。关于在使用asAnyArray(…)获取数组对象的函数中使用power的警告同样适用于此事实。

  5. 默认值 __array_priority__ 矩阵对象的数量是10.0,因此与ndarrays的混合运算总是生成矩阵。

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

    matrix.T 

    返回矩阵的转置。

    matrix.H 

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

    matrix.I 

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

    matrix.A 

    返回 self 作为 ndarray 对象。

警告

矩阵对象过乘乘法 “和权力,” *,分别为矩阵乘法和矩阵幂。如果子例程可以接受子类,并且不转换为基类数组,则必须使用ufuncs乘法和幂来确保对所有输入执行正确的操作。

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

matrix [数据] [, dtype, copy] )

注解

不再建议使用此类,即使对于线性

asmatrix [数据] [, 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 (文件名) [, 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

注解

这个 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。但是,也可以使用 numpy.chararray 或通过 numpy.char.array 功能:

chararray [形状] [, itemsize, unicode, ...] )

提供字符串和Unicode值数组的方便视图。

core.defchararray.array [(Obj]) [, itemsize, ...] )

创建一个 chararray .

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

记录数组 (numpy.rec

numpy提供 recarray 类,它允许以属性的形式访问结构化数组的字段和相应的标量数据类型对象 record .

recarray [形状] [, 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 [数据] [, ...] )

标准容器类,便于多重继承。

数组迭代器

迭代器是数组处理的强大概念。实际上,迭代器实现了一个广义for循环。如果 迈特 是迭代器对象,然后是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

这里,我使用内置的枚举迭代器返回迭代器索引和值。

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 

生成模拟广播的对象。

通过使用 broadcast 迭代器。此对象接受 N 对象作为输入并返回迭代器,该迭代器返回提供广播结果中每个输入序列元素的元组。

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