代码之家  ›  专栏  ›  技术社区  ›  Sun Bear

如何基于另一个NumPy数组的值创建一个NumPy数组?

  •  0
  • Sun Bear  · 技术社区  · 6 年前

    我想创建一个NumPy数组。它的元素值取决于另一个NumPy数组中元素的值。目前,我必须在列表理解中使用for循环来遍历数组 a 得到 b .实现这一目标的最基本方法是什么?

    测试脚本:

    import numpy as np
    
    def get_b( a ):
        b_dict = {  1:10., 2:20., 3:30. }
        return b_dict[ a ]
    
    a = np.full( 10, 2 )
    print( f'a = {a}' )
    b = np.array( [get_b(i) for i in a] )
    print( f'b = {b}' )
    

    输出:

    a = [2 2 2 2 2 2 2 2 2 2]
    b = [20. 20. 20. 20. 20. 20. 20. 20. 20. 20.]
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   bigbounty    6 年前

    你可以用 np.vectorize 将字典值映射到数组的步骤

    In [6]: b_dict = {  1:10., 2:20., 3:30 }
    
    In [7]: a = np.full( 10, 2 )
    
    In [8]: np.vectorize(b_dict.get)(a)
    Out[8]: array([20., 20., 20., 20., 20., 20., 20., 20., 20., 20.])
    
    
        2
  •  1
  •   user2317421 user2317421    6 年前

    那么使用 map 和 np.fromiter ?

    def get_b( a ):
        b_dict = {  1:10., 2:20., 3:30. }
        return b_dict[ a ]
    
    a = np.full( 10, 2 )
    b = np.fromiter(map(get_b, a), dtype=np.float64)
    

    编辑1 :小时间比较:

    %timeit np.array( [get_b(i) for i in a] )
    5.58 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    %timeit np.fromiter(map(get_b, a), dtype=np.float64)
    5.77 µs ± 177 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    %timeit np.vectorize(b_dict.get)(a)
    12.9 µs ± 76.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    

    编辑2 :这个例子似乎太小了:

    a = np.full( 1000, 2 )
    
    %timeit np.array( [get_b(i) for i in a] )
    415 µs ± 9.13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit np.fromiter(map(get_b, a), dtype=np.float64)
    383 µs ± 2.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit np.vectorize(b_dict.get)(a)
    68.6 µs ± 625 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
        3
  •  1
  •   Ehsan    6 年前

    解决这个问题的另一种方法是:

    from operator import itemgetter
    np.array(itemgetter(*a)(b_dict))
    

    输出:

    [20., 20., 20., 20., 20., 20., 20., 20., 20., 20.]
    

    比较 :

    #@kmundnic solution
    def m1(a):
      def get_b(x):
        b_dict = {  1:10., 2:20., 3:30. }
        return b_dict[x]
      return np.fromiter(map(get_b, a),dtype=np.float)
    
    #@bigbounty solution
    def m2(a):
      b_dict = {  1:10., 2:20., 3:30. }
      return np.vectorize(b_dict.get)(a)
    
    #@Ehsan solution
    def m3(a):
      b_dict = {  1:10., 2:20., 3:30. }
      return np.array(itemgetter(*a)(b_dict))
    
    #@Sun Bear solution
    def m4(a):
      def get_b( a ):
        b_dict = {  1:10., 2:20., 3:30. }
        return b_dict[ a ]
      return np.array( [get_b(i) for i in a] )
    
    in_ = [np.full( n, 2 ) for n in [10,100,1000,10000]]
    

    对于 小词典 似乎 m2 在大输入和 m3 对于较小的。

    enter image description here

    为了一个 大字典 :

    b_dict = dict(zip(np.arange(100),np.arange(100)))
    in_ = [np.full(n,50) for n in [10,100,1000,10000]]
    

    m3 这是最快的方法。可以根据字典大小和键数组大小进行选择。

    enter image description here

        4
  •  0
  •   Sun Bear    6 年前

    @hpaulj 对我的问题的评论:

    做 b_dict 必须是一个口述者吗?如果你有一个数组,例如。 ref = np.array([0, 10,20,30]) 通过索引快速选择值, ref[a] . 在与numpy合作时,我会尽量避免使用dict。

    我发现,使用NumPy的索引将导致性能比使用python时快几个到几个数量级 dict .

    建立在 @Ehsan's solution 下面是一个进行这种比较的脚本。

    import numpy as np
    from operator import itemgetter
    import timeit
    import matplotlib.pyplot as plt
    
    
    #@kmundnic solution
    def m1(a):
        def get_b(x):
            b = {  1:10., 2:20., 3:30. }
            #b = dict( zip( np.arange(1,101),np.arange(10,1001,10) ) )
            return b[x]
        return np.fromiter(map(get_b, a),dtype=np.float)
    
    #@bigbounty solution
    def m2(a):
        b = {  1:10., 2:20., 3:30. }
        #b = dict( zip( np.arange(1,101),np.arange(10,1001,10) ) )
        return np.vectorize(b.get)(a)
    
    #@Ehsan solution
    def m3(a):
        b = {  1:10., 2:20., 3:30. }
        #b = dict( zip( np.arange(1,101),np.arange(10,1001,10) ) )
        return np.array(itemgetter(*a)(b))
    
    #@Sun Bear solution
    def m4(a):
        def get_b( a ):
            b = {  1:10., 2:20., 3:30. }
            #b = dict( zip( np.arange(1,101),np.arange(10,1001,10) ) )
            return b[ a ]
        return np.array( [get_b(i) for i in a] )
    
    #@hpaulj solution
    def m5(a):
        b = np.array([10, 20, 30])
        #b = np.arange(10,1001,10) 
        return b[a]
    
            
    sizes=[10,100,1000,10000]
    pm1 = []
    pm2 = []
    pm3 = []
    pm4 = []
    pm5 = []
    for size in sizes:
        a = np.full( size, 2 )
        pm1.append( timeit.timeit( 'm1(a)', number=1000, globals=globals() ) )
        pm2.append( timeit.timeit( 'm2(a)', number=1000, globals=globals() ) )
        pm3.append( timeit.timeit( 'm3(a)', number=1000, globals=globals() ) )
        pm4.append( timeit.timeit( 'm4(a)', number=1000, globals=globals() ) )
        pm5.append( timeit.timeit( 'm5(a)', number=1000, globals=globals() ) )
    
    print( 'm1 slower than m5 by :',np.array(pm1) / np.array(pm5) )
    print( 'm2 slower than m5 by :',np.array(pm2) / np.array(pm5) )
    print( 'm3 slower than m5 by :',np.array(pm3) / np.array(pm5) )
    print( 'm4 slower than m5 by :',np.array(pm4) / np.array(pm5) )
    
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot( sizes, pm1, label='m1' )
    ax.plot( sizes, pm2, label='m2' )
    ax.plot( sizes, pm3, label='m3' )
    ax.plot( sizes, pm4, label='m4' )
    ax.plot( sizes, pm5, label='m5' )
    ax.grid( which='both' )
    ax.set_xscale('log')
    ax.set_yscale('log')
    ax.legend()
    ax.get_xaxis().set_label_text( label='len(a)', fontweight='bold' )
    ax.get_yaxis().set_label_text( label='Runtime (sec)', fontweight='bold' )
    plt.show()
    

    结果:

    len(b)=3:

    m1 slower than m5 by : [  4.22462367  29.79407905  85.03454097 339.2915358 ]
    m2 slower than m5 by : [  8.64220685 11.57175871 13.76761749 46.1940683 ]
    m3 slower than m5 by : [  3.25785432  21.63131578  54.71305704 220.15777696 ]
    m4 slower than m5 by : [  4.60710166  30.93616607  91.8936744  371.00398273 ]
    

    len(b)=100:

    m1 slower than m5 by : [  218.98603678  1976.50128737  9697.76615006 17742.79151719 ]
    m2 slower than m5 by : [  41.76535891  53.85600913 109.35129345 164.13075291 ]
    m3 slower than m5 by : [  24.82715462  36.77830986  87.56253196 141.04493237 ]
    m4 slower than m5 by : [  222.04184193  2001.72120836  9775.22464369 18431.00155305 ]
    

    comparisons