【问题标题】:How to perform a numpy function with a numpy array of numpy array objects?如何使用 numpy 数组对象的 numpy 数组执行 numpy 函数?
【发布时间】:2018-02-18 15:14:13
【问题描述】:

假设我们有一个用于创建 numpy 数组对象的 numpy 数组的函数:

randarr = lambda shape: np.random.randint(0, 10, shape)
get_numpy_array_obj = lambda shapes: \
    np.array([randarr(shape) for shape in shapes])

还有数组的定义大小:

shapes = [(2, 3), (3, 4), (4, 2)]

因为数组的形状不一样,numpy把其他数组当成对象。

现在如果我们创建两个这样的数组

A = get_numpy_array_obj(shapes)
B = get_numpy_array_obj(shapes)

做简单的操作如+、-、**等都不是问题,例如:

C = A * B - (A + B)**2

当我想对这些数组(如 tanh、exp 等)应用 numpy 函数时,就会出现问题。

D = np.tanh(A)

这会返回给我以下错误:

AttributeError: 'numpy.ndarray' object has no attribute 'tanh'

我可以像这样将函数一个接一个地应用到每个数组上

D = np.array([np.tanh(a) for a in A])

我的想法是编写更少的代码并提高可读性。

np.vectorize(np.tanh)(A) 也无法正常工作。这将返回:

ValueError: setting an array element with a sequence.

还有其他可能的方法可以在 numpy 数组对象的数组上应用 numpy 函数吗?

【问题讨论】:

  • 不是真的,我不这么认为。从 Numpy 的角度来看,没有“数组数组”这样的东西。它只是将其视为(可能)异构的 Python 对象数组,因此它不一定知道如何对它们进行矢量化操作。您可能会考虑不同的数据结构,例如稀疏矩阵或其他东西,具体取决于您的应用程序。否则,您可以在对数组的应用操作中逐个迭代数组——在这种情况下没有真正的缺点。
  • 我需要这个专门用于机器学习(用于层之间的权重)。这种类型的操作被多次使用,也用于巨大的数组大小。除了我写的问题中的列表理解之外,我还能以某种方式获得更好的表现吗?
  • 试试 np.frompyfunc
  • 你的回答真的很有帮助!可以使用np.frompyfunc(np.tanh, 1, 1) 代替np.tanh。您能否详细回答这个问题或提供np.frompyfunc 的链接和示例? (接受答案)

标签: python arrays numpy


【解决方案1】:
In [99]: arr = np.array([np.ones(ij) for ij in [(1,3),(2,4),(3,2)]])
In [100]: arr
Out[100]: 
array([array([[1., 1., 1.]]),
       array([[1., 1., 1., 1.],
       [1., 1., 1., 1.]]),
       array([[1., 1.],
       [1., 1.],
       [1., 1.]])], dtype=object)

像'+'这样的操作符之所以有效,是因为数组有相应的方法:

In [101]: arr[0].__add__
Out[101]: <method-wrapper '__add__' of numpy.ndarray object at 0xb64897a0>
In [102]: arr+arr
Out[102]: 
array([array([[2., 2., 2.]]),
       array([[2., 2., 2., 2.],
       [2., 2., 2., 2.]]),
       array([[2., 2.],
       [2., 2.],
       [2., 2.]])], dtype=object)

但是像np.tanh 这样的函数没有数组方法。

frompyfunc 将数组元素传递给您的函数并返回一个对象 dtype 数组。通常这很痛苦,但在这种情况下,这正是我们想要的:

In [103]: np.frompyfunc(np.tan,1,1)(arr)
Out[103]: 
array([array([[1.55740772, 1.55740772, 1.55740772]]),
       array([[1.55740772, 1.55740772, 1.55740772, 1.55740772],
       [1.55740772, 1.55740772, 1.55740772, 1.55740772]]),
       array([[1.55740772, 1.55740772],
       [1.55740772, 1.55740772],
       [1.55740772, 1.55740772]])], dtype=object)

vectorize 也使用frompyfunc,但会尝试将结果转换为数值数组。我们可以通过指定 otypes 来跳过它:

In [104]: np.vectorize(np.tan,otypes='O')(arr)
Out[104]: 
array([array([[1.55740772, 1.55740772, 1.55740772]]),
       array([[1.55740772, 1.55740772, 1.55740772, 1.55740772],
       [1.55740772, 1.55740772, 1.55740772, 1.55740772]]),
       array([[1.55740772, 1.55740772],
       [1.55740772, 1.55740772],
       [1.55740772, 1.55740772]])], dtype=object)

对象数组的迭代速度介于列表迭代和数值数组迭代之间。它有一些阵列开销,但不是全部。元素是指针,就像在列表中一样。

frompyfunc 可能比显式迭代快一点(最多 2 倍)。 vectorize 有点慢,因为它有一些开销。这些函数的最大优点是它们可以处理广播。自己安排时间看看它们是否有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    • 2022-01-26
    • 2011-06-20
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 2020-08-04
    相关资源
    最近更新 更多