Python 将函数应用于数据数组的0维 问题

Python 将函数应用于数据数组的0维 问题,python,function,numpy,multidimensional-array,vectorization,Python,Function,Numpy,Multidimensional Array,Vectorization,我有一个ndarray,由arr定义,它是一个n维立方体,每个维的长度m 我想通过沿维度n=0切片,并将每个n-1-dim切片作为函数的输入,来执行函数func 这似乎适用于map(),但我找不到合适的numpy变体

我有一个
ndarray
,由
arr
定义,它是一个
n
维立方体,每个维的长度
m

  • 我想通过沿维度
    n=0
    切片,并将每个
    n-1
    -dim切片作为函数的输入,来执行函数
    func

  • 这似乎适用于
    map()
    ,但我找不到合适的
    numpy
    变体<矢量化似乎将
    n-1
    -张量拆分为单个标量项。无论是
    沿_轴应用
    还是
    在_轴上应用
    似乎都不合适

    我的问题是,我需要将任意函数作为输入传递,因此我认为
    einsum
    的解决方案也不可行

    问题:
    • 您知道使用
      np.asarray(map(func,arr))的最佳
      numpy
      替代方法吗
    例子 我通过以下方式将示例数组,
    arr
    定义为
    4
    -dim立方体(或4-张量):

    我定义了一个示例函数
    f

    def f(x):
        """makes it obvious how the np.ndarray is being passed into the function"""
        try: # perform an op using x[0,0,0] which is expected to exist
            i = x[0,0,0]
        except:
            print '\nno element x[0,0,0] in x: \n{}'.format(x)
            return np.nan
        return x-x+i
    
    此函数的预期结果,
    res
    ,将保持相同的形状,但将满足以下要求:

    print all([(res[i] == i*m**(n-1)).all() for i in range(m)])
    
    这适用于默认的
    map()
    函数

    res = np.asarray(map(f, a))
    print all([(res[i] == i*m**(n-1)).all() for i in range(m)])
    True
    
    我希望
    np.vectorize
    的工作方式与
    map()
    相同,但它在标量条目中起作用:

    res = np.vectorize(f)(a)
    
    no element x[0,0,0] in x: 
    0
    ...
    

    考虑到
    arr
    是4d,并且您的
    fn
    可以在3d阵列上工作

    np.asarray(map(func, arr))
    
    看起来非常合理。我会使用列表理解表单,但这是编程风格的问题

    np.asarray([func(i) for i in arr])
    
    for i in arr
    arr
    的第一个维度上迭代。实际上,它将
    arr
    视为3d阵列的列表。然后它将结果列表重新组合成4d数组

    np.vectorize
    doc可以更明确地说明使用标量的函数。但是是的,它将值作为标量传递。请注意,
    np.vectorize
    没有传递迭代轴参数的规定。当函数从多个数组中获取值时,它最有用,例如

     [func(a,b) for a,b in zip(arrA, arrB)]
    
    它概括了
    zip
    ,因此允许广播。但除此之外,它是一个迭代的解决方案。它对
    func
    的本质一无所知,因此无法加快调用速度

    np.vectorize
    最终调用了
    np.frompyfunc
    ,它的通用性稍差,速度稍快。但它也将标量传递给func

    np.沿/在ax(e/i)上应用
    也在一个或多个轴上迭代。你可能会发现他们的代码很有启发性,但我同意他们不适用于这里

    map方法的一种变体是分配结果数组和索引:

    In [45]: res=np.zeros_like(arr,int)
    In [46]: for i in range(arr.shape[0]):
        ...:     res[i,...] = f(arr[i,...])
    
    如果需要在与第一个轴不同的轴上迭代,这可能会更容易

    你需要自己计时,看看哪个更快

    ========================

    在第一个维度上进行迭代并进行就地修改的示例:

    In [58]: arr.__array_interface__['data']  # data buffer address
    Out[58]: (152720784, False)
    
    In [59]: for i,a in enumerate(arr):
        ...:     print(a.__array_interface__['data'])
        ...:     a[0,0,:]=i
        ...:     
    (152720784, False)   # address of the views (same buffer)
    (152720892, False)
    (152721000, False)
    
    In [60]: arr
    Out[60]: 
    array([[[[ 0,  0,  0],
             [ 3,  4,  5],
             [ 6,  7,  8]],
    
            ...
    
           [[[ 1,  1,  1],
             [30, 31, 32],
             ...
    
           [[[ 2,  2,  2],
             [57, 58, 59],
             [60, 61, 62]],
           ...]]])
    

    当我迭代一个数组时,我得到一个从公共数据缓冲区上的连续点开始的视图。如果我修改视图,如上所述,甚至使用
    a[:]=…
    ,我会修改原始视图。我不必写任何回信。但是不要使用
    a=..
    ,这会断开与原始数组的链接。

    相关但没有解决问题:
    map()的问题
    和生成器表达式是为
    ndarray
    重新分配内存位置,这对于大型数组是浪费的-我希望有一个
    numpy
    函数,它只使用现有内存指针指向当前数组位置
    arr[I,]=f(arr[I,…)
    将子阵列放回原始阵列中。是否创建临时数组、副本或视图取决于
    f
    正在执行的操作的详细信息。但是我想知道你对numpy数组中
    内存指针的使用有多了解。谢谢你的提示!我想说,我比大多数python专家都更了解,但可能是平均水平较低——这让我了解了大部分情况,特别是第2.3节和第3.1.1节,我将尝试编写一个
    f
    的示例,该示例修改其输入
    x
    ,而不是最后一个赋值。谢谢-我正在处理大小为
    48**4
    或甚至
    48**7
    的晶格,但后者我可能会使用
    C
    Fortran
    所以内存使用是值得考虑的,即使它只是我代码中的一条注释作为提醒
    In [58]: arr.__array_interface__['data']  # data buffer address
    Out[58]: (152720784, False)
    
    In [59]: for i,a in enumerate(arr):
        ...:     print(a.__array_interface__['data'])
        ...:     a[0,0,:]=i
        ...:     
    (152720784, False)   # address of the views (same buffer)
    (152720892, False)
    (152721000, False)
    
    In [60]: arr
    Out[60]: 
    array([[[[ 0,  0,  0],
             [ 3,  4,  5],
             [ 6,  7,  8]],
    
            ...
    
           [[[ 1,  1,  1],
             [30, 31, 32],
             ...
    
           [[[ 2,  2,  2],
             [57, 58, 59],
             [60, 61, 62]],
           ...]]])