代码之家  ›  专栏  ›  技术社区  ›  lo tolmencre

np.vectorize和np.apply_沿着_轴向映射函数传递相同参数两次

  •  1
  • lo tolmencre  · 技术社区  · 6 年前

    我想映射一个函数 f 在字符串数组上。我构造的矢量化版本 f 并将其应用于我的数组。但数组的第一个元素被传递两次:

    import numpy as np
    
    def f(string):
        print('called with', string)
    
    a = np.array(['110', '012'])
    
    fv = np.vectorize(f)
    np.apply_along_axis(fv, axis=0, arr=a)
    
    
    called with 110
    called with 110
    called with 012
    

    为什么会这样?我没想到 110 传递给 f 两次,我不明白为什么会这样。

    我的误解是什么 np.vectorize np.apply_along_axis ?

    2 回复  |  直到 6 年前
        1
  •  4
  •   hpaulj    6 年前
    In [145]: def f(string):
         ...:     print('called with', string)
         ...: 
         ...: a = np.array(['110', '012'])
         ...: 
         ...: fv = np.vectorize(f)
         ...: 
    In [146]: fv(a)
    called with 110
    called with 110
    called with 012
    Out[146]: array([None, None], dtype=object)
    

    只有一个print的函数返回 None . vectorized 调用它一次以确定返回的数据类型-在本例中,它推断 object .

    如果我们指定 otypes 喜欢 int ,我们得到一个错误:

    In [147]: fv = np.vectorize(f, otypes=[int])
    In [148]: fv(a)
    called with 110
    called with 012
    ---------------------------------------------------------------------------
    ...    
    TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
    

    类型 与返回的对象不兼容

    In [149]: fv = np.vectorize(f, otypes=[object])
    In [150]: fv(a)
    called with 110
    called with 012
    Out[150]: array([None, None], dtype=object)
    

    一个更好、更有意义的功能:

    In [151]: def f(string):
         ...:     print('called with', string)
         ...:     return len(string)
         ...: 
         ...: 
    In [152]: fv = np.vectorize(f, otypes=[int])
    In [153]: fv(a)
    called with 110
    called with 012
    Out[153]: array([3, 3])
    

    记住 vectorize 将标量值传递给函数。实际上,它评估输入数组的每个元素,返回具有匹配形状的数组:

    In [154]: fv(np.array([a,a,a]))
    called with 110
    called with 012
    called with 110
    called with 012
    called with 110
    called with 012
    Out[154]: 
    array([[3, 3],
           [3, 3],
           [3, 3]])
    

    与普通迭代相比,例如 np.array([f(i) for i in a]) ,速度较慢,但如果输入数组可以具有多个维度,则稍微方便一点,如果有多个数组需要相互广播,则更好。

    对于一个简单的单数组 a , np.vectorize 是多余的。


    矢量化 有另一个参数, cache 它可以避免这种双重调用,同时还允许自动检测数据类型:

    In [156]: fv = np.vectorize(f, cache=True)
    In [157]: fv(a)
    called with 110
    called with 012
    Out[157]: array([3, 3])
    

    自动数据类型检测有时会导致错误。例如,如果试用计算返回不同的数据类型:

    In [160]: def foo(var):
         ...:     if var<0:
         ...:         return -var
         ...:     elif var>0:
         ...:         return var
         ...:     else:
         ...:         return 0  
    
    In [161]: np.vectorize(foo)([0,1.2, -1.2])
    Out[161]: array([0, 1, 1])           # int dtype
    In [162]: np.vectorize(foo)([0.1,1.2, -1.2])
    Out[162]: array([0.1, 1.2, 1.2])     # float dtype
    

    apply_along_axis 接受接受一维数组的函数。它迭代所有其他维度,向函数传递一组一维切片。对于像你的 这没用。即使你的 是的,不会有多大帮助。你的 fv 不需要1d输入。

    它还进行了一次尝试计算,以确定返回数组的形状和数据类型。它会自动缓存结果。

    喜欢 矢量化 , 沿轴应用 是一个方便的工具,而不是性能工具。

    比较

    np.apply_along_axis(fv, axis=0, arr=[a,a,a])
    np.apply_along_axis(fv, axis=1, arr=[a,a,a])
    

    了解如何 apply_along 影响评估顺序。

    或者做一些整体的事情 row (或列)具有:

    np.apply_along_axis(lambda x: fv(x).mean(), axis=0, arr=[a,a,a])
    
        2
  •  4
  •   user2357112    6 年前

    docs :

    输出的数据类型 矢量化 通过使用输入的第一个元素调用函数来确定。这可以通过指定 类型 争论。

    额外的调用用于确定输出数据类型。