当前位置: 首页 > 面试题库 >

NumPy矩阵类的弃用状态

拓拔泓
2023-03-14
问题内容

matrixNumPy中该课程的状态是什么?

我一直被告知我应该改用ndarray该类。matrix在我编写的新代码中使用该类值得/安全吗?我不明白为什么我应该改用ndarrays。


问题答案:

tl;
DR:
numpy.matrix类是越来越过时。有一些备受瞩目的库将类作为依赖项(最大的库scipy.sparse),这妨碍了对类的适当短期弃用,但是强烈建议用户使用ndarray类(通常使用numpy.array便捷函数创建)。
。随着引入@用于矩阵乘法的算子,许多矩阵的相对优势已被消除。

为什么(不是)矩阵类?

numpy.matrix是的子类numpy.ndarray。它最初是为了方便在涉及线性代数的计算中使用,但与更通用的数组类的实例相比,它们在行为方式上既有局限性,又有令人惊讶的差异。行为基本差异的示例:

  • 形状:数组可以具有任意数量的维,范围从0到无穷大(或32)。矩阵始终是二维的。奇怪的是,虽然不能 创建 具有更大尺寸的矩阵,但可以将单例尺寸注入到矩阵中,从而在技术上最终获得多维矩阵:(np.matrix(np.random.rand(2,3))[None,...,None].shape == (1,2,3,1)这并不具有任何实际意义)。
  • 索引:索引数组可以为您提供任何大小的数组,具体取决于您对其进行索引的方式。在矩阵上建立索引表达式将始终为您提供矩阵。这意味着2d数组arr[:,0]arr[0,:]2d数组都为1d ndarray,同时mat[:,0]具有形状(N,1)mat[0,:]形状(1,M)(如果为)matrix
  • 算术运算:过去使用矩阵的主要原因是矩阵的算术运算(尤其是乘法和幂)执行矩阵运算(矩阵乘法和矩阵幂)。数组的相同结果会导致元素乘法和幂运算。因此mat1 * mat2,if是有效的mat1.shape[1] == mat2.shape[0],但是ifarr1 * arr2是有效的arr1.shape == arr2.shape(当然,结果意味着完全不同的东西)。而且,令人惊讶地,mat1 / mat2执行两个矩阵的 元素 除法。这种行为可能是继承ndarray矩阵,但对矩阵毫无意义,尤其是考虑到的含义*
  • 特殊属性:矩阵有一些方便的属性,除了什么阵列具有:mat.Amat.A1具有相同的值作为阵列的意见np.array(mat)np.array(mat).ravel()分别。mat.T并且mat.H是矩阵的转置和共轭转置(伴随);arr.T是该类存在的唯一此类属性ndarray。最后mat.I是的逆矩阵mat

编写适用于ndarray或矩阵的代码非常容易。但是,当这两个类有可能必须在代码中进行交互时,事情就会变得困难起来。特别是,许多代码对于的子类自然 可以
正常工作ndarray,但它们matrix是行为不端的子类,很容易破坏试图依赖鸭子类型的代码。考虑以下使用数组和形状矩阵的示例(3,4)

import numpy as np

shape = (3, 4)
arr = np.arange(np.prod(shape)).reshape(shape) # ndarray
mat = np.matrix(arr) # same data in a matrix
print((arr + mat).shape)           # (3, 4), makes sense
print((arr[0,:] + mat[0,:]).shape) # (1, 4), makes sense
print((arr[:,0] + mat[:,0]).shape) # (3, 3), surprising

根据我们沿切片的尺寸,将两个对象的切片相加会发生灾难性的变化。当形状相同时,对矩阵和数组的加法都会逐元素进行。上面的前两种情况很直观:我们添加两个数组(矩阵),然后分别添加两个行。最后一种情况确实令人惊讶:我们可能打算添加两列,最后得到一个矩阵。原因当然是arr[:,0]具有(3,)与形状兼容(1,3)mat[:.0]具有形状的形状(3,1)。两者一起广播以成形(3,3)

最后,当在python
3.5中引入了@matmul运算符时(首先在numpy
1.10中实现),消除了矩阵类的最大优点(即,可以简洁地公式化涉及许多矩阵乘积的复杂矩阵表达式的可能性)。比较简单的二次形式的计算:

v = np.random.rand(3); v_row = np.matrix(v)
arr = np.random.rand(3,3); mat = np.matrix(arr)

print(v.dot(arr.dot(v))) # pre-matmul style
# 0.713447037658556, yours will vary
print(v_row * mat * v_row.T) # pre-matmul matrix style
# [[0.71344704]]
print(v @ arr @ v) # matmul style
# 0.713447037658556

综上所述,很明显为什么矩阵类在处理线性代数方面广受青睐:infix*运算符使表达式的冗长程度降低,并且易于阅读。但是,@使用现代python和numpy的运算符具有相同的可读性。此外,请注意,矩阵案例为我们提供了形状(1,1)上应为标量的形状矩阵。这也意味着我们不能将列向量与这个“标量”相乘:(v_row * mat * v_row.T) * v_row.T在上面的示例中,由于矩阵的形状不同(1,1)(3,1)无法按此顺序相乘,因此会产生错误。

为了完整起见,应该注意的是,尽管matmul运算符修复了ndarrays与矩阵相比不是最理想的最常见方案,但是使用ndarrays优雅地处理线性代数仍然存在一些缺点(尽管人们仍然倾向于认为总体而言最好坚持使用后者)。一个这样的例子是矩阵幂:是矩阵mat ** 3的适当的第三矩阵幂(而它是ndarray的元素级立方)。不幸的numpy.linalg.matrix_power是更加冗长。此外,就地矩阵乘法仅适用于矩阵类。相比之下,虽然PEP
465和python语法都允许@=使用matmul进行增强分配,但从numpy 1.15开始,对于ndarrays尚未实现此功能。

弃用历史

考虑到上述有关matrix该类的复杂性,很长一段时间以来一直在反复讨论其可能被弃用。引入@infix运算符是此过程的巨大先决条件,发生于2015年9月。不幸的是,矩阵类在早期的优点意味着它的使用范围很广。有些库依赖于矩阵类(最重要的依赖项之一是scipy.sparse使用numpy.matrix语义,并且在进行密化时经常返回矩阵),因此完全弃用它们总是有问题的。

从2009年开始出现在一个小小的邮件列表线程中,我发现诸如

numpy是为满足通用计算需求而设计的,而不是数学的任何一个分支。nd数组对很多事情都非常有用。相比之下,例如Matlab最初被设计为线性代数封装的简单前端。就我个人而言,当我使用Matlab时,我发现这很尴尬-
我通常在写几行与线性代数无关的代码时,实际上每行几行都进行矩阵数学运算。因此,我更喜欢numpy的方式-代码的线性代数行越长越尴尬,但其余的则好得多。

Matrix类是这种情况的例外:编写该类是为了提供一种表达线性代数的自然方法。但是,当您将矩阵和数组混合使用时,事情变得有些棘手,即使坚持使用矩阵也存在困惑和局限性-
如何表达行向量与列向量?在矩阵上迭代时会得到什么?等等

关于这些问题的讨论很多,很多好主意,关于如何改进它的一些共识,但是没有一个熟练的人有足够的动力去做。

这些反映了矩阵类带来的好处和困难。我可以找到的最早的弃用建议是从2008年开始的,尽管部分原因是由于非直觉的行为,此行为自此发生了变化(特别是,对矩阵进行切片和迭代将产生(行)矩阵,这很可能是人们期望的)。该建议表明,这是一个极具争议的主题,并且矩阵乘法的中缀运算符至关重要。

我可以找到的下一个提及来自2014年,事实证明这是一个 非常
富有成果的话题。随后的讨论提出了一般情况下处理numpy子类的问题,该主题仍然很受关注。也有强烈的批评:

引发此讨论(在Github上)的是,不可能编写适用于以下情况的鸭子式代码:

  • ndarrays
  • 矩阵
  • 稀疏稀疏矩阵

这三个词的语义是不同的。scipy.sparse在矩阵和ndarray之间,某些东西像矩阵一样随机工作,而另一些则不然。

加上一些夸张的说法,可以说,从开发人员的角度来看,np.matrix正在做,并且已经通过捣乱Python中ndarray语义的未声明规则而已经作恶了。

其次是关于矩阵可能的未来的许多有价值的讨论。即使@当时没有运算符,对于矩阵类的弃用及其对下游用户的影响也有很多想法。据我所知,这种讨论直接导致了引入matmul的PEP
465的诞生。

在2015年初:

在我看来,np.matrix的“固定”版本应该(1)不是np.ndarray子类,并且(2)存在于第三方库中,而不是numpy本身。

我认为将np.matrix作为ndarray子类固定在当前状态下并不可行,但是即使固定的矩阵类也不真正属于numpy本身,而numpy的发布周期太长,并且实验的兼容性保证-
更不用说在numpy中仅存在矩阵类会导致新用户误入歧途。

一旦@操作员可以使用一段时间,关于折旧的讨论再次浮出水面,引发了关于矩阵折旧与矩阵关系的话题scipy.sparse

最终,于2017年11月下旬采取了第一个弃用numpy.matrix措施。关于班级的家属:

社区将如何处理scipy.sparse矩阵子类?这些仍然很常用。

他们已经很长时间没有去任何地方了(直到稀疏的ndarray至少实现了)。因此,np.matrix需要移动而不是删除。

(来源)和

虽然我想像任何人一样摆脱np.matrix,但很快就这样做 确实是 破坏性的。

  • 那里有很多不懂的人写的小脚本。我们确实希望他们学习不使用np.matrix,但是破坏所有脚本是一种痛苦的方式

  • 由于scipy.sparse,像scikit-learn这样的大型项目根本无法替代使用np.matrix。

所以我认为前进的方向是这样的:

  • 现在或每当有人聚集PR时:在np.matrix .__
    init__中发出PendingDeprecationWarning(除非它会降低scikit-
    learn和朋友的性能),并在文档顶部放置一个大警告框。这里的想法是实际上不破坏任何人的代码,而是开始传达出这样的信息:我们绝对不认为任何人如果有其他选择,都不应使用它。

  • 在使用scipy.sparse替代方法之后:增加警告,可能一直扩展到FutureWarning,以使现有脚本不会中断,但它们确实会发出嘈杂的警告

  • 最终,如果我们认为这样可以减少维护成本:将其拆分为一个子包

(来源)。

现状

截至2018年5月(numpy 1.15,相关的pull
request和commit),矩阵类docstring包含以下注释:

即使对于线性代数,也不再建议使用此类。而是使用常规数组。该类将来可能会被删除。

并且同时PendingDeprecationWarning已将添加到matrix.__new__。不幸的是,默认情况下,弃用警告(几乎总是)是静音的,因此大多数numpy的最终用户都不会看到此强烈提示。

最后,截至2018年11月的numpy路线图提到了多个相关主题,因为“ numpy社区将在以下方面投入资源和任务和功能之一

NumPy内部的某些内容实际上与NumPy的范围不匹配。

  • numpy.fft的后端系统(因此,例如fft-mkl不需要猴子补丁numpy)
  • 将掩码数组重写为不是ndarray子类-也许在单独的项目中?
  • MaskedArray为鸭子数组类型,和/或
  • 支持缺失值的dtypes
  • 编写有关如何处理linalg和fft的numpy和scipy之间的重叠的策略(并实现它)。
  • 弃用np.matrix

只要较大的库/许多用户(尤其是scipy.sparse)依赖于矩阵类,这种状态就可能保持不变。但是,正在进行讨论以转移scipy.sparse到其他方面,例如pydata/sparse。无论弃用过程的发展如何,用户都应ndarray在新代码中使用该类,并且如果可能的话,最好移植旧代码。最终,矩阵类可能最终会放在单独的程序包中,以消除因其以当前形式存在而造成的一些负担。



 类似资料:
  • 主要内容:逐元素矩阵乘法,矩阵乘积运算,矩阵点积矩阵乘法是将两个矩阵作为输入值,并将 A 矩阵的行与 B 矩阵的列对应位置相乘再相加,从而生成一个新矩阵,如下图所示: 注意:必须确保第一个矩阵中的行数等于第二个矩阵中的列数,否则不能进行矩阵乘法运算。 图1:矩阵乘法 矩阵乘法运算被称为向量化操作,向量化的主要目的是减少使用的 for 循环次数或者根本不使用。这样做的目的是为了加速程序的计算。 下面介绍 NumPy 提供的三种矩阵乘法,从而进一步

  • 主要内容:matlib.empty(),numpy.matlib.zeros(),numpy.matlib.ones(),numpy.matlib.eye(),numpy.matlib.identity(),numpy.matlib.rand()NumPy 提供了一个 矩阵库模块 ,该模块中的函数返回的是一个 matrix 对象,而非 ndarray 对象。矩阵由 m 行 n 列(m*n)元素排列而成,矩阵中的元素可以是数字、符号或数学公式等。 matlib.empty() matlib.emp

  • 问题内容: 我正在尝试创建具有混合数据类型(字符串,整数,整数)的NumPy数组/矩阵(Nx3)。但是,当我通过添加一些数据来添加此矩阵时,出现错误: TypeError:无效的类型提升 。拜托,有人可以帮我解决这个问题吗? 当我用示例数据创建一个数组时,NumPy将矩阵中的所有列都转换为一种“ S”数据类型。而且我无法为数组指定数据类型,因为当我执行此操作时, res = np.array([“

  • 我需要在灰度图像中分割出异常。在算法的某个地方,我计算一个矩阵,其中包含需要设置为零的已知像素强度。我该怎么做? 例如: 计算的像素强度:(数组([94,95,96,97,98,99,100,101,102,103,104,105,106,107、108,109,110,111、112、113、114、115、116、117、118、119、120、121、122、123、124、125、126、

  • 本文向大家介绍Python中的Numpy矩阵操作,包括了Python中的Numpy矩阵操作的使用技巧和注意事项,需要的朋友参考一下 Numpy 通过观察Python的自有数据类型,我们可以发现Python原生并不提供多维数组的操作,那么为了处理矩阵,就需要使用第三方提供的相关的包。 NumPy 是一个非常优秀的提供矩阵操作的包。NumPy的主要目标,就是提供多维数组,从而实现矩阵操作。 NumPy

  • 问题内容: 我有以下代码: 它创建一个填充零的矩阵。相反,我想知道是否有一种函数或方法可以将它们初始化为s,而方法很简单。 问题答案: 您很少需要在numpy中进行矢量操作循环。您可以创建一个未初始化的数组并立即分配给所有条目: 我已经在这里和Blaenk发布的时间安排了时间: 时序显示优先选择作为更快的替代方案。OTOH,我喜欢numpy的便捷实现,在该实现中您可以同时为整个slice分配值,代