matrixNumPy中该课程的状态是什么?
matrix
我一直被告知我应该改用ndarray该类。matrix在我编写的新代码中使用该类值得/安全吗?我不明白为什么我应该改用ndarrays。
ndarray
tl; DR:在numpy.matrix类是越来越过时。有一些备受瞩目的库将类作为依赖项(最大的库scipy.sparse),这妨碍了对类的适当短期弃用,但是强烈建议用户使用ndarray类(通常使用numpy.array便捷函数创建)。 。随着引入@用于矩阵乘法的算子,许多矩阵的相对优势已被消除。
numpy.matrix
scipy.sparse
numpy.array
@
numpy.matrix是的子类numpy.ndarray。它最初是为了方便在涉及线性代数的计算中使用,但与更通用的数组类的实例相比,它们在行为方式上既有局限性,又有令人惊讶的差异。行为基本差异的示例:
numpy.ndarray
np.matrix(np.random.rand(2,3))[None,...,None].shape == (1,2,3,1)
arr[:,0]
arr[0,:]
mat[:,0]
(N,1)
mat[0,:]
(1,M)
mat1 * mat2
mat1.shape[1] == mat2.shape[0]
arr1 * arr2
arr1.shape == arr2.shape
mat1 / mat2
*
mat.A
mat.A1
np.array(mat)
np.array(mat).ravel()
mat.T
mat.H
arr.T
mat.I
mat
编写适用于ndarray或矩阵的代码非常容易。但是,当这两个类有可能必须在代码中进行交互时,事情就会变得困难起来。特别是,许多代码对于的子类自然 可以 正常工作ndarray,但它们matrix是行为不端的子类,很容易破坏试图依赖鸭子类型的代码。考虑以下使用数组和形状矩阵的示例(3,4):
(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)。
(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)无法按此顺序相乘,因此会产生错误。
(1,1)
(v_row * mat * v_row.T) * v_row.T
为了完整起见,应该注意的是,尽管matmul运算符修复了ndarrays与矩阵相比不是最理想的最常见方案,但是使用ndarrays优雅地处理线性代数仍然存在一些缺点(尽管人们仍然倾向于认为总体而言最好坚持使用后者)。一个这样的例子是矩阵幂:是矩阵mat ** 3的适当的第三矩阵幂(而它是ndarray的元素级立方)。不幸的numpy.linalg.matrix_power是更加冗长。此外,就地矩阵乘法仅适用于矩阵类。相比之下,虽然PEP 465和python语法都允许@=使用matmul进行增强分配,但从numpy 1.15开始,对于ndarrays尚未实现此功能。
mat ** 3
numpy.linalg.matrix_power
@=
考虑到上述有关matrix该类的复杂性,很长一段时间以来一直在反复讨论其可能被弃用。引入@infix运算符是此过程的巨大先决条件,发生于2015年9月。不幸的是,矩阵类在早期的优点意味着它的使用范围很广。有些库依赖于矩阵类(最重要的依赖项之一是scipy.sparse使用numpy.matrix语义,并且在进行密化时经常返回矩阵),因此完全弃用它们总是有问题的。
从2009年开始出现在一个小小的邮件列表线程中,我发现诸如
numpy是为满足通用计算需求而设计的,而不是数学的任何一个分支。nd数组对很多事情都非常有用。相比之下,例如Matlab最初被设计为线性代数封装的简单前端。就我个人而言,当我使用Matlab时,我发现这很尴尬- 我通常在写几行与线性代数无关的代码时,实际上每行几行都进行矩阵数学运算。因此,我更喜欢numpy的方式-代码的线性代数行越长越尴尬,但其余的则好得多。 Matrix类是这种情况的例外:编写该类是为了提供一种表达线性代数的自然方法。但是,当您将矩阵和数组混合使用时,事情变得有些棘手,即使坚持使用矩阵也存在困惑和局限性- 如何表达行向量与列向量?在矩阵上迭代时会得到什么?等等 关于这些问题的讨论很多,很多好主意,关于如何改进它的一些共识,但是没有一个熟练的人有足够的动力去做。
numpy是为满足通用计算需求而设计的,而不是数学的任何一个分支。nd数组对很多事情都非常有用。相比之下,例如Matlab最初被设计为线性代数封装的简单前端。就我个人而言,当我使用Matlab时,我发现这很尴尬- 我通常在写几行与线性代数无关的代码时,实际上每行几行都进行矩阵数学运算。因此,我更喜欢numpy的方式-代码的线性代数行越长越尴尬,但其余的则好得多。
Matrix类是这种情况的例外:编写该类是为了提供一种表达线性代数的自然方法。但是,当您将矩阵和数组混合使用时,事情变得有些棘手,即使坚持使用矩阵也存在困惑和局限性- 如何表达行向量与列向量?在矩阵上迭代时会得到什么?等等
关于这些问题的讨论很多,很多好主意,关于如何改进它的一些共识,但是没有一个熟练的人有足够的动力去做。
这些反映了矩阵类带来的好处和困难。我可以找到的最早的弃用建议是从2008年开始的,尽管部分原因是由于非直觉的行为,此行为自此发生了变化(特别是,对矩阵进行切片和迭代将产生(行)矩阵,这很可能是人们期望的)。该建议表明,这是一个极具争议的主题,并且矩阵乘法的中缀运算符至关重要。
我可以找到的下一个提及来自2014年,事实证明这是一个 非常 富有成果的话题。随后的讨论提出了一般情况下处理numpy子类的问题,该主题仍然很受关注。也有强烈的批评:
引发此讨论(在Github上)的是,不可能编写适用于以下情况的鸭子式代码: ndarrays 矩阵 稀疏稀疏矩阵 这三个词的语义是不同的。scipy.sparse在矩阵和ndarray之间,某些东西像矩阵一样随机工作,而另一些则不然。 加上一些夸张的说法,可以说,从开发人员的角度来看,np.matrix正在做,并且已经通过捣乱Python中ndarray语义的未声明规则而已经作恶了。
引发此讨论(在Github上)的是,不可能编写适用于以下情况的鸭子式代码:
这三个词的语义是不同的。scipy.sparse在矩阵和ndarray之间,某些东西像矩阵一样随机工作,而另一些则不然。
加上一些夸张的说法,可以说,从开发人员的角度来看,np.matrix正在做,并且已经通过捣乱Python中ndarray语义的未声明规则而已经作恶了。
其次是关于矩阵可能的未来的许多有价值的讨论。即使@当时没有运算符,对于矩阵类的弃用及其对下游用户的影响也有很多想法。据我所知,这种讨论直接导致了引入matmul的PEP 465的诞生。
在2015年初:
在我看来,np.matrix的“固定”版本应该(1)不是np.ndarray子类,并且(2)存在于第三方库中,而不是numpy本身。 我认为将np.matrix作为ndarray子类固定在当前状态下并不可行,但是即使固定的矩阵类也不真正属于numpy本身,而numpy的发布周期太长,并且实验的兼容性保证- 更不用说在numpy中仅存在矩阵类会导致新用户误入歧途。
在我看来,np.matrix的“固定”版本应该(1)不是np.ndarray子类,并且(2)存在于第三方库中,而不是numpy本身。
我认为将np.matrix作为ndarray子类固定在当前状态下并不可行,但是即使固定的矩阵类也不真正属于numpy本身,而numpy的发布周期太长,并且实验的兼容性保证- 更不用说在numpy中仅存在矩阵类会导致新用户误入歧途。
一旦@操作员可以使用一段时间,关于折旧的讨论再次浮出水面,引发了关于矩阵折旧与矩阵关系的话题scipy.sparse。
最终,于2017年11月下旬采取了第一个弃用numpy.matrix措施。关于班级的家属:
社区将如何处理scipy.sparse矩阵子类?这些仍然很常用。 他们已经很长时间没有去任何地方了(直到稀疏的ndarray至少实现了)。因此,np.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,以使现有脚本不会中断,但它们确实会发出嘈杂的警告 最终,如果我们认为这样可以减少维护成本:将其拆分为一个子包
虽然我想像任何人一样摆脱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的最终用户都不会看到此强烈提示。
PendingDeprecationWarning
matrix.__new__
最后,截至2018年11月的numpy路线图提到了多个相关主题,因为“ numpy社区将在以下方面投入资源和任务和功能之一 :
NumPy内部的某些内容实际上与NumPy的范围不匹配。 numpy.fft的后端系统(因此,例如fft-mkl不需要猴子补丁numpy) 将掩码数组重写为不是ndarray子类-也许在单独的项目中? MaskedArray为鸭子数组类型,和/或 支持缺失值的dtypes 编写有关如何处理linalg和fft的numpy和scipy之间的重叠的策略(并实现它)。 弃用np.matrix
NumPy内部的某些内容实际上与NumPy的范围不匹配。
只要较大的库/许多用户(尤其是scipy.sparse)依赖于矩阵类,这种状态就可能保持不变。但是,正在进行讨论以转移scipy.sparse到其他方面,例如pydata/sparse。无论弃用过程的发展如何,用户都应ndarray在新代码中使用该类,并且如果可能的话,最好移植旧代码。最终,矩阵类可能最终会放在单独的程序包中,以消除因其以当前形式存在而造成的一些负担。
pydata/sparse