好吧,如果我只是问一些愚蠢的话,我提前道歉,但我真的以为我理解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的列表理解然后再回到数组会很傻,但我还不确定另一种方法。我只是遗漏了一些明显的东西吗?
答案 0 :(得分:4)
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
速度较慢。
答案 1 :(得分:2)
问题似乎是apply_along_axis
使用isscalar
来确定返回的对象是否是标量,但isscalar
为用户定义的类返回False
。 apply_along_axis
的{{3}}说:
outarr的形状与arr的形状相同,除了沿着轴的尺寸,其中outarr的长度等于func1d的返回值的大小。
由于你的班级__len__
返回它所包含的数组的长度,numpy"扩展"将生成的数组转换为原始形状。如果您没有定义__len__
,则会收到错误,因为numpy并不认为用户定义的类型是标量,因此它仍然会尝试调用{{1}在它上面。
据我所知,没有办法让用户定义的类工作。你可以从len
返回1,但是你仍然会得到一个Nx1 2D结果,而不是长度为N的一维数组。我没有看到任何方法让Numpy看到用户定义的实例作为标量。
关于__len__
行为有documentation,但令人惊讶的是,我无法找到apply_along_axis
对非numpy对象返回False的基本问题的讨论。可能是numpy刚刚决定试图而不是猜测用户定义的类型是矢量还是标量。不过,在numpy列表中可能值得问这个问题,因为像isscalar
这样的东西返回False似乎很奇怪。
但是,如果你说你不关心表现,那真的不重要。只需使用列表理解的第一种方式,已经做了你想要的。