【问题标题】:Numpy 1-dim array vs 2-dim array with one of the dimension having length 1Numpy 1-dim 数组与 2-dim 数组,其中一个维度的长度为 1
【发布时间】:2021-06-18 11:17:15
【问题描述】:

在 Matlab 中没有一维数组的概念。所有数组至少有两个维度。所有“向量”都是“行向量”(1xn 数组)或“列向量”(nx1 数组)。

另一方面,在 NumPy 中,数组也可以是一维的。于是就有了“纯向量”(维度n)、“行向量”(维度1xn)和“列向量”(维度nx1)的概念。

现在我正在从 Matlab 迁移到 Python,这让我很头疼。

作为一个例子,考虑我必须移动 nxk(n 通常很大,但 k 可以是 1)矩阵的行的情况,称之为 A,向下移动一行,然后添加一行零第一行。

在 Matlab 中我会这样做

[n, k] = size(A);
B      = [zeros(1,k); A(1:end-1,:)];

在 Numpy 中,我希望它不仅适用于二维输入,而且适用于一维输入。 所以工作解决方案将是

import numpy as np

if A.ndim == 1:
    B = np.concatenate((np.zeros(1), A[:-1]), axis=0)

if A.ndim == 2:
    (n, k) = A.shape
    B = np.concatenate((np.zeros((1,k)), A[:-1,:]), axis=0)

但这太重了。有没有更好(更简洁)的方法?

更一般地说,我总是遇到这个问题:如果我编写了一个采用二维数组 (nxk) 的函数,称之为arr其中 k 很可能是 1,函数可能会在一维数组上失败(例如,如果我这样做 arr[0,:])。 但我希望它也适用于一维数组,因为它们在道德上与其中一个维度为 1 的二维数组相同。

当然,一种出路是放类似

if arr.ndim == 1
    arr = arr.reshape((arr.shape[0],1))

在函数的最开始,这样就保证了函数有一个二维数组可以使用。

但这并不完全令人满意。例如,我的函数可能会返回一个与输入 (nxk) 形状相同的数组。但如果输入是一维的,我希望它也返回一维的东西,而不是 (nx1)。因此,为了处理这种情况,我需要添加其他冗长的 if 语句和整形,这会使我的代码看起来更加沉重和丑陋。

最好的出路是什么?

【问题讨论】:

  • 虽然这些维度测试可能会增加代码的长度,但它们在运行时间上并没有太大的区别。您可以将该代码隐藏在函数中。 numpy 函数一直在进行这样的调整。以np.atleast_2d 为例,或者vstackhstackcolumn_stack 如何扩展基本的concatenate。像np.sum 这样的函数的keep_dims 参数可以很方便,以及了解使用0、[0]` 和0:1 进行索引之间的区别。出于某种原因,从 MATLAB 到 numpy 的转换对我来说并没有那么痛苦。
  • 在我阅读的时候(顺便提一下,写得很详细的问题)我打算建议你基于reshape 的方法,最后只有另一个reshape 才能恢复原始数量尺寸,您最初需要将其存储在变量中
  • like it to return something one dimensional as well, not a (nx1) - .squeeze() 函数末尾的额外维度。最好的出路可能是刚开始使用 Numpy 并习惯它的工作方式,除非你能找到另一个合适的包来完成你想要的。如果您正在寻求与 SO 无关的软件包/库建议。
  • @wwii “习惯 [Numpy] 的工作方式”正是我想要做的!!!如果你不这么认为,我会很高兴你告诉我一种更 NumPythonic 的思维方式!!!
  • @LuisMendo 谢谢!如果可能的话,我真的会尽量避免这种逻辑。它只会造成混乱......我敢肯定......

标签: python matlab numpy matrix multidimensional-array


【解决方案1】:

当涉及到数组大小时,我认为 numpy(与 MATLAB 相比)的“严格性”是一个优势,我觉得它让很多事情变得更可预测。

我针对您的第一个问题提出以下解决方案,其中可能包含一些有用的工具来解决未来的问题。第一个工具是"ellipsis" (...) 对象,可用于索引。当您在索引中看到这三个点时,您可以将其视为根据需要替换了尽可能多的:。例如,如果A.shape = (42,2021,69,7)A[..., 1, :]A[:, :, 1, :] 相同。在这里我要补充一点,很明显,每个索引表达式只能使用其中一个。为了编写处理任意维度数组的函数,拥有它是一件非常有用的事情。

第二个(这个答案不是必需的)是你可以将它用于np.onesnp.zeros等你总是有相应的功能np.ones_likenp.zeros_like这让你避免很多麻烦计算。

所以在下面我们使用它们首先创建一个具有正确形状的新数组,而无需进行任何算术运算。我们只是在我们想要替换的东西上调用np.zeros_like(在这种情况下,A 的最后一个“超行”,但它可以是其中的任何一个)。同时,省略号运算符可以方便地处理我们可能存在的任意数量的维度:

import numpy as np  
def f(A):
  u = np.zeros_like(A[-1:, ...]) # np.zeros(A[-1:, ...].shape)
  v = A[:-1,...]
  return np.concatenate([u,v], axis=0)

Try it online!

【讨论】:

  • 非常感谢您的回答。我有一些想法。a)为什么您将 numpy 的“严格性”视为优势。我很想看看我缺少什么,因为我认为这是一个劣势!
  • b) 我不知道的一件事是,你可以只用一个索引来索引一个 n dim 数组。这是我在玩你的答案时发现的。那么用 A[-1:] 而不是 A[-1:, ...] 和 A[:-1] 而不是 A[:-1, ...] 来修改你的答案会不会很好。我怀疑这将是完全等价的。我理解正确吗?
  • c) 你给了我一条鱼,但我仍然不能钓鱼(可能是因为我很笨)。例如,如果我想要一个函数 take 需要一个 nxk 矩阵并给你一个 nx1 矩阵,其中包含跨列的每一行的总和。如果我确定输入是 2-dim,我显然可以做 np.sum(A, axis=1)。但这会在我希望输出与输入相同的 1-dim 输入上再次失败。这里有什么优雅的解决方案吗?
  • @Chicken (a) 在matlab中,多维数组的前两个维度基本上得到了一些特殊规则的特殊处理。如果您最多只处理二维数组(当您使用“教科书”-线性代数工具时可能会出现这种情况),这些可能很有用。但是,如果您有任意数量的维度,那么前两个得到特殊处理会很麻烦,并且在 numpy 中,所有维度都是“平等的”,因此在这方面更加一致。
  • (b) 是正确的,只要您只访问前导维度,它就可以很好地工作。无论如何,我都喜欢使用省略号运算符来明确告诉读者存在更多维度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-03
相关资源
最近更新 更多