【问题标题】:Confusion about numpy's apply along axis and list comprehensions关于 numpy 沿轴应用和列表理解的困惑
【发布时间】:2014-12-25 05:59:40
【问题描述】:

好的,所以如果我只是问一些愚蠢的问题,我提前道歉,但我真的认为我理解 apply_along_axis 的工作原理。我刚刚遇到了一些可能是我没有考虑过的边缘情况,但这让我感到困惑。简而言之,这是让我感到困惑的代码:

class Leaf(object):

    def __init__(self, location):
        self.location = location

    def __len__(self):
        return self.location.shape[0]

def bulk_leaves(child_array, axis=0):
    test = np.array([Leaf(location) for location in child_array])  # This is what I want
    check = np.apply_along_axis(Leaf, 0, child_array)  # This returns an array of individual leafs with the same shape as child_array
    return test, check

if __name__ == "__main__":
    test, check = bulk_leaves(np.random.ran(100, 50))
    test == check  # False

我总是觉得在 numpy 中使用列表推导然后再转换回数组很愚蠢,但我不确定另一种方法可以做到这一点。我只是错过了一些明显的东西吗?

【问题讨论】:

  • apply_along_axis 从不修改其输入。它返回一个新数组。
  • 措辞错误,我的意思是 check 与 child_array 的形状相同
  • 什么是child_array
  • Leaf 实例的数组将具有 dtype object。这些数组的内存效率并不比普通的 Python 列表高,执行任何计算的速度也不比使用普通 Python 列表的等效代码快(考虑到创建数组的成本,它通常更慢)。它提供的只是ndarray 索引syntax。你确定要使用 NumPy 数组吗?
  • @BrenBarn 只是一个随机数数组。一个np.random.rand(x, y)

标签: python object numpy vectorization


【解决方案1】:

apply_along_axis 是纯 Python,您可以自己查看和解码。在这种情况下,它基本上是:

check = np.empty(child_array.shape,dtype=object)
for i in range(child_array.shape[1]):
    check[:,i] = Leaf(child_array[:,i])

换句话说,它预先分配容器数组,然后用迭代填充值。这当然比附加到数组要好,但很少比将值附加到列表更好(这是理解所做的)。

您可以使用上述模板并对其进行调整以生成您真正想要的数组。

for i in range(check.shape[0]):
    check[i]=Leaf(child_array[i,:])

在快速测试中,此迭代时间与推导式相同。 apply_along_axis 除了错误之外,速度更慢。

【讨论】:

  • 感谢您浏览源代码。对性能是否会发生变化没有很好的直觉,但必须接受@BrenBard 的回答,以找到让我感到困惑的核心问题。
【解决方案2】:

问题似乎是apply_along_axis使用isscalar判断返回的对象是否为标量,但isscalar为用户定义的类返回Falsedocumentation 代表 apply_along_axis 说:

outarr 的形状与 arr 的形状相同,只是沿轴维度,outarr 的长度等于 func1d 的返回值的大小。

由于您的类的 __len__ 返回它包装的数组的长度,numpy 将结果数组“扩展”为原始形状。如果你没有定义__len__,你会得到一个错误,因为numpy不认为用户定义的类型是标量,所以它仍然会尝试调用len

据我所知,无法使用用户定义的类进行此操作。您可以从__len__ 返回 1,但是您仍然会得到 Nx1 2D 结果,而不是长度为 N 的 1D 数组。我看不出有任何方法可以让 Numpy 将用户定义的实例视为标量。

关于apply_along_axis 行为有a numpy bug,但令人惊讶的是,我找不到任何关于isscalar 为非numpy 对象返回False 的潜在问题的讨论。可能是 numpy 只是决定下注,而不是猜测用户定义的类型是向量还是标量。尽管如此,在 numpy 列表上询问这个问题可能还是值得的,因为在我看来,像 isscalar(object()) 这样的东西返回 False 似乎很奇怪。

但是,如果您说您无论如何都不关心性能,那也没关系。只需将您的第一种方式与列表理解一起使用,它已经可以满足您的需求。

【讨论】:

  • 啊,非常感谢您找到这个!是的,就像我说的,这不是一个真正的性能问题,只是让我感到莫名其妙。我的意思是,公平地说,我的对象不是标量,但我可能会运行一些快速基准测试来查看 len -> 1 hack 或下面@hpaulj 提出的解决方案是否有任何性能优势,但知道为什么会发生这种情况对我来说已经足够了。非常感谢!
  • 那个 numpy 错误现在已经在 master 中修复了,应该会发布 1.13 版本
  • @Eric:谢谢,这是个好消息!对于用户定义的类,isscalar 总是返回 false 的基本问题的 numpy 立场是什么?
  • @BrenBarn:“不要使用isscalar 似乎是解决这个问题的方法。如果您将isscalar 解释为“这是numpy.generic 的子类”,那么它会按预期工作。几乎总是,你想要np.ndim(x) == 0,而不是np.isscalar(x)
猜你喜欢
  • 1970-01-01
  • 2019-11-29
  • 1970-01-01
  • 2012-03-01
  • 2013-10-18
  • 2021-07-29
  • 2018-11-24
  • 1970-01-01
  • 2018-07-08
相关资源
最近更新 更多