【问题标题】:Writing functions that accept both 1-D and 2-D numpy arrays?编写同时接受一维和二维 numpy 数组的函数?
【发布时间】:2012-01-07 09:25:48
【问题描述】:

我的理解是,numpy 中的一维数组可以解释为面向列的向量或面向行的向量。例如,形状为(8,) 的一维数组可以被视为形状为(1,8) 或形状为(8,1) 的二维数组,具体取决于上下文。

我遇到的问题是,我编写的用于操作数组的函数在二维情况下倾向于很好地泛化以处理向量和矩阵,但在一维情况下却不是很好。

因此,我的函数最终会执行以下操作:

if arr.ndim == 1:
    # Do it this way
else:
    # Do it that way

甚至这个:

# Reshape the 1-D array to a 2-D array
if arr.ndim == 1:
    arr = arr.reshape((1, arr.shape[0]))

# ... Do it the 2-D way ...

也就是说,我发现我可以泛化代码来处理二维情况 (r,1)(1,c)(r,c),但不能泛化处理没有分支或重塑的一维情况。

当函数对多个数组进行操作时,它会变得更加丑陋,因为我会检查并转换每个参数。

所以我的问题是:我错过了一些更好的成语吗?我上面描述的模式是 numpy 代码共有的吗?

另外,作为API设计原则的相关问题,如果调用者将一维数组传递给某个返回新数组的函数,并且返回值也是一个向量,那么通常的做法是重塑一个二维数组吗? D 向量 (r,1)(1,c) 返回一维数组,还是简单地记录该函数无论如何都返回一个二维数组?

谢谢

【问题讨论】:

    标签: python numpy vectorization api-design


    【解决方案1】:

    我认为一般来说 NumPy 函数需要一个形状为(r,c) 的数组,并没有特别考虑一维数组。相反,他们希望用户要么准确地传递一个形状为(r,c) 的数组,要么让用户传递一个broadcasts 形状为(r,c) 的一维数组。

    如果你向这样的函数传递一个形状为(c,) 的一维数组,它将广播到形状(1,c),因为广播会在左侧添加新的轴。它还可以广播为任意r 塑造(r,c)(取决于它正在与什么其他数组组合)。

    另一方面,如果你有一个形状为(r,) 的一维数组x,并且你需要它广播到形状(r,c),那么 NumPy 期望用户传递一个形状数组(r,1) 因为广播不会为您添加右侧的新轴。

    为此,用户必须传递x[:,np.newaxis] 而不仅仅是x


    关于返回值:我认为最好总是返回一个二维数组。如果用户知道输出的形状是(1,c),并且想要一个一维数组,让她自己切掉一维数组x[0]

    通过使返回值始终具有相同的形状,将更容易理解使用此函数的代码,因为输入的形状并不总是很明显。

    此外,广播模糊了形状为(c,) 的一维数组和形状为(r,c) 的二维数组之间的区别。如果您的函数在输入一维输入时返回一个一维数组,而在输入二维输入时返回一个二维数组,那么您的函数将区分严格而不是模糊。从风格上讲,这让我想起了检查if isinstance(obj,type),这与鸭子打字的纹理背道而驰。非必要就不要做。

    【讨论】:

      【解决方案2】:

      unutbu 的解释很好,但我不同意返回维度。

      函数内部模式取决于函数的类型。

      通常可以编写带有轴参数的归约操作,这样维度的数量就无关紧要了。

      Numpy 还有一个 atleast_2d(和 atleast_1d)函数,如果你需要一个显式的 2d 数组,它也很常用。在统计学中,我有时会使用像 atleast_2d_cols 这样的函数,它将 1d (r,) 重塑为 2d (r,1) 以用于需要 2d 的代码,或者如果输入数组是 1d,则解释和线性代数需要一个列向量。 (重塑很便宜,所以这不是问题)

      在第三种情况下,如果低维情况可以比高维情况更便宜或更简单,我可能会有不同的代码路径。 (例如:如果 2d 需要多个点积。)

      返回维度

      我认为不遵循返回维度的 numpy 约定可能会让用户对一般功能感到非常困惑。 (特定主题的功能可能不同。) 例如,reduce 操作会松散一维。

      对于许多其他函数,输出维度与输入维度相匹配。我认为一维输入应该具有一维输出,而不是额外的冗余维度。除了 linalg 中的函数,我不记得有任何函数会返回多余的额外维度。 (标量与 1 元素数组的情况并不总是一致的。)

      这让我想起了一个 isinstance 检查:

      如果您允许例如 numpy 矩阵和掩码数组,请尝试不使用它。你会得到不容易调试的有趣结果。虽然,对于大多数 numpy 和 scipy 函数,用户必须知道数组类型是否适用于它们,因为很少有 isinstance 检查并且 asarray 可能并不总是正确的。

      作为用户,我总是知道我有什么样的“array_like”,一个列表、元组或哪个数组子类,尤其是当我使用乘法时。

      np.array(np.eye(3).tolist()*3)
      np.matrix(range(3)) * np.eye(3)
      np.arange(3) * np.eye(3)
      

      另一个例子:这是做什么的?

      >>> x = np.array(tuple(range(3)), [('',int)]*3)
      >>> x
      array((0, 1, 2), 
            dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])
      >>> x * np.eye(3)
      

      【讨论】:

        【解决方案3】:

        这个问题已经有了很好的答案。在这里,我只想添加我通常做的事情(以某种方式总结其他人的反应),当我想编写接受广泛输入的函数而我对它们执行的操作需要二维行或列向量时。

        1. 如果我知道输入总是一维的(数组或列表):

          一个。如果我需要一行:x = np.asarray(x)[None,:]

          b.如果我需要专栏:x = np.asarray(x)[:,None]

        2. 如果输入可以是具有正确形状的 2d(数组或列表)或 1d(需要转换为 2d 行/列):

          一个。如果我需要一行:x = np.atleast_2d(x)

          b.如果我需要一个专栏:x = np.atleast_2d(np.asarray(x).T).Tx = np.reshape(x, (len(x),-1))(后者似乎更快)

        【讨论】:

        • np.reshape(x, (len(x),-1)) 是炸弹。谢谢。
        【解决方案4】:

        这对装饰器很有用

        def atmost_2d(func):
          def wrapr(x):
            return func(np.atleast_2d(x)).squeeze()
          return wrapr
        

        例如,此函数将挑选出其输入的最后一列。

        @atmost_2d
        def g(x):
          return x[:,-1]
        

        但是:它适用于:

        1d:

        In [46]: b
        Out[46]: array([0, 1, 2, 3, 4, 5])
        
        In [47]: g(b)
        Out[47]: array(5)
        

        二维:

        In [49]: A
        Out[49]:
        array([[0, 1],
               [2, 3],
               [4, 5]])
        
        In [50]: g(A)
        Out[50]: array([1, 3, 5])
        

        0d:

        In [51]: g(99)
        Out[51]: array(99)
        

        这个答案建立在前两个的基础上。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-08
          • 2012-08-29
          相关资源
          最近更新 更多