Python 关于numpy的困惑';s沿轴应用,并列出理解

Python 关于numpy的困惑';s沿轴应用,并列出理解,python,object,numpy,vectorization,Python,Object,Numpy,Vectorization,好吧,如果我只是问一些愚蠢的问题,我提前道歉,但我真的认为我理解了apply\u沿_轴的工作原理。我刚刚遇到了一个可能是我没想到的边缘案例,但这让我感到困惑。简而言之,这就是让我困惑的代码: class Leaf(object): def __init__(self, location): self.location = location def __len__(self): return self.location.shape[0] def

好吧,如果我只是问一些愚蠢的问题,我提前道歉,但我真的认为我理解了
apply\u沿_轴
的工作原理。我刚刚遇到了一个可能是我没想到的边缘案例,但这让我感到困惑。简而言之,这就是让我困惑的代码:

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中使用列表理解然后再回溯到数组很愚蠢,但我只是不确定是否有其他方法可以做到这一点。我是不是遗漏了一些明显的东西?

沿轴应用的
是纯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_沿_轴
,除了错误之外,速度较慢。

apply_沿_轴
是纯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\u-arou-arou-arou-araxis
除了错误之外,速度较慢。

问题似乎在于
apply\u-arou-araxis
使用
isscalar
来确定返回的对象是否是标量,但是
isscalar
对于用户定义的类返回
False
。for
沿_轴应用
表示:

outarr的形状与arr的形状相同,除了沿轴尺寸外,其中outarr的长度等于func1d返回值的大小

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

就我所见,没有办法使用用户定义的类来实现这一点。您可以从
\uuu len\uuuu
返回1,但仍然会得到一个Nx1 2D结果,而不是长度为N的1D数组。我看不到任何方法可以让Numpy将用户定义的实例视为标量


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


然而,如果正如你所说的,你根本不在乎性能,那也没关系。只需使用列表理解的第一种方法,它已经实现了您想要的功能。

问题似乎是
apply\u沿轴
使用
isscalar
来确定返回的对象是否是标量,但是
isscalar
对于用户定义的类返回
False
。for
沿_轴应用
表示:

outarr的形状与arr的形状相同,除了沿轴尺寸外,其中outarr的长度等于func1d返回值的大小

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

就我所见,没有办法使用用户定义的类来实现这一点。您可以从
\uuu len\uuuu
返回1,但仍然会得到一个Nx1 2D结果,而不是长度为N的1D数组。我看不到任何方法可以让Numpy将用户定义的实例视为标量


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


然而,如果正如你所说的,你根本不在乎性能,那也没关系。只需使用列表理解的第一种方法,它已经实现了您想要的功能。

apply\u沿\u轴
从不修改其输入。它返回一个新数组。如果用词不当,我的意思是check与shapeWhat
child\u array
?一个
Leaf
实例数组将具有dtype
object
。这些数组的内存效率并不比常规Python列表高,并且执行任何计算的速度都不会比使用常规Python列表的等效代码快(而且考虑到创建数组的成本,它通常会更慢)。它提供的只是ndarray索引语法。你确定要使用NumPy数组吗?@BrenBarn只是一个随机数数组。一个
np.random.rand(x,y)
apply\u沿轴
从不修改其输入。它返回一个新的数组。如果拼写错误,我的意思是检查是相同的