【问题标题】:Using Numpy Vectorize on Functions that Return Vectors在返回向量的函数上使用 Numpy Vectorize
【发布时间】:2011-03-23 16:47:23
【问题描述】:

numpy.vectorize 接受一个函数 f:a->b 并将其转换为 g:a[]->b[]。

ab 是标量时,这可以正常工作,但我想不出为什么它不能与 b 作为ndarray 或列表一起使用,即 f:a->b[ ] 和 g:a[]->b[][]

例如:

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
print(g(a))

这会产生:

array([[ 0.  0.  0.  0.  0.],
       [ 1.  1.  1.  1.  1.],
       [ 2.  2.  2.  2.  2.],
       [ 3.  3.  3.  3.  3.]], dtype=object)

好的,这样给出了正确的值,但是错误的 dtype。更糟糕的是:

g(a).shape

产量:

(4,)

所以这个数组几乎没用。我知道我可以转换它:

np.array(map(list, a), dtype=np.float32)

给我我想要的:

array([[ 0.,  0.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)

但这既不高效也不pythonic。你们中的任何人都可以找到更清洁的方法吗?

提前致谢!

【问题讨论】:

  • 它不能按预期工作的原因是你传递了otypes=[np.ndarray]。所以你的结果是一个数组,其中的元素是数组,因此你得到dtype=object。你特地要求的。

标签: python arrays numpy vectorization


【解决方案1】:

np.vectorize 只是一个便利功能。它实际上不是make code run any faster。如果使用np.vectorize 不方便,只需编写自己的函数即可。

np.vectorize 的目的是将不支持 numpy 的函数(例如,将浮点数作为输入并返回浮点数作为输出)转换为可以操作(并返回)numpy 数组的函数。

您的函数f 已经支持numpy——它在其定义中使用了一个numpy 数组并返回一个numpy 数组。所以np.vectorize 不适合您的用例。

因此,解决方案就是推出你自己的函数f,它可以按照你想要的方式工作。

【讨论】:

  • 确实,“只是一个便利函数”描述了大多数 numpy API。这就是重点。太糟糕了,这个函数的行为不像人们期望的那样。
  • 大多数 NumPy 函数只比用 C 编写的等效函数慢一点。当 NumPy 函数只是 C(或 Fortran)函数的薄包装器时,这是正确的。相比之下,np.vectorized 函数仍然必须为数组中的每个元素调用一次 Python 函数,因此它的执行方式更像 Python 代码而不是 C 代码。 Python 的动态名称查找提供了更大的灵活性,但可能比 C 代码慢得多。
【解决方案2】:

1.12.0 中的新参数 signature 完全符合您的要求。

def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)

g = np.vectorize(f, signature='()->(n)')

然后g(np.arange(4)).shape 将给(4L, 5L)

这里指定了f 的签名。 (n) 是返回值的形状,() 是标量参数的形状。参数也可以是数组。如需更复杂的签名,请参阅Generalized Universal Function API

【讨论】:

    【解决方案3】:
    import numpy as np
    def f(x):
        return x * np.array([1,1,1,1,1], dtype=np.float32)
    g = np.vectorize(f, otypes=[np.ndarray])
    a = np.arange(4)
    b = g(a)
    b = np.array(b.tolist())
    print(b)#b.shape = (4,5)
    c = np.ones((2,3,4))
    d = g(c)
    d = np.array(d.tolist())
    print(d)#d.shape = (2,3,4,5)
    

    这应该可以解决问题,并且无论您输入的大小如何,它都可以正常工作。 “地图”仅适用于一维输入。使用“.tolist()”并创建一个新的 ndarray 可以更完整、更好地解决问题(我相信)。希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      你想对函数进行矢量化

      import numpy as np
      def f(x):
          return x * np.array([1,1,1,1,1], dtype=np.float32)
      

      假设您想要获得单个 np.float32 数组作为结果,您必须将其指定为 otype。在您的问题中,您指定了otypes=[np.ndarray],这意味着您希望每个元素都是np.ndarray。因此,您正确地得到了dtype=object 的结果。

      正确的调用应该是

      np.vectorize(f, signature='()->(n)', otypes=[np.float32])
      

      对于这样一个简单的函数,最好利用numpy 的ufunctions; np.vectorize 只是循环它。所以在你的情况下,只需将你的函数重写为

      def f(x):
          return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))
      

      这样更快,并且产生的错误更少(但请注意,如果您传递复数或四精度数,结果dtype 将取决于x,结果也会如此)。

      【讨论】:

        【解决方案5】:

        我写了一个函数,看起来很符合你的需要。

        def amap(func, *args):
            '''array version of build-in map
            amap(function, sequence[, sequence, ...]) -> array
            Examples
            --------
            >>> amap(lambda x: x**2, 1)
            array(1)
            >>> amap(lambda x: x**2, [1, 2])
            array([1, 4])
            >>> amap(lambda x,y: y**2 + x**2, 1, [1, 2])
            array([2, 5])
            >>> amap(lambda x: (x, x), 1)
            array([1, 1])
            >>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4])
            array([[1, 9], [4, 16]])
            '''
            args = np.broadcast(None, *args)
            res = np.array([func(*arg[1:]) for arg in args])
            shape = args.shape + res.shape[1:]
            return res.reshape(shape)
        

        试一试

        def f(x):
                return x * np.array([1,1,1,1,1], dtype=np.float32)
        amap(f, np.arange(4))
        

        输出

        array([[ 0.,  0.,  0.,  0.,  0.],
               [ 1.,  1.,  1.,  1.,  1.],
               [ 2.,  2.,  2.,  2.,  2.],
               [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)
        

        为方便起见,您也可以使用 lambda 或 partial 包装它

        g = lambda x:amap(f, x)
        g(np.arange(4))
        

        注意vectorize 的文档字符串说

        提供vectorize 函数主要是为了方便,而不是为了 表现。该实现本质上是一个 for 循环。

        因此,我们预计这里的amapvectorize 具有相似的性能。我没查,欢迎大家做性能测试。

        如果性能真的很重要,你应该考虑其他的东西,例如使用reshapebroadcast 直接进行数组计算以避免纯python 中的循环(vectorizeamap 都是后一种情况)。

        【讨论】:

          【解决方案6】:

          解决此问题的最佳方法是使用二维 NumPy 数组(在本例中为列数组)作为 original 函数的输入,然后生成一个二维输出我相信你所期望的结果。

          下面是它在代码中的样子:

          import numpy as np
          def f(x):
              return x*np.array([1, 1, 1, 1, 1], dtype=np.float32)
          
          a = np.arange(4).reshape((4, 1))
          b = f(a)
          # b is a 2-D array with shape (4, 5)
          print(b)
          

          这是一种更简单且不易出错的方式来完成操作。此方法不是尝试使用 numpy.vectorize 转换函数,而是依赖于 NumPy 广播数组的自然能力。诀窍是确保至少一个维度在数组之间具有相等的长度。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-01-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-09-15
            • 2017-01-13
            相关资源
            最近更新 更多