代码之家  ›  专栏  ›  技术社区  ›  user2994559

为什么numpy数组似乎没有比标准python列表快得多?

  •  4
  • user2994559  · 技术社区  · 6 年前

    据我所知,numpy数组可以比python列表更快地处理操作,因为它们是以并行方式而不是迭代方式处理的。为了好玩,我试着测试一下,但没发现有什么不同。

    我的考试有什么问题吗?是否只有比我使用的数组大得多的数组才有区别?我确保在每个函数中创建一个python列表和numpy数组,以消除创建一个与另一个可能产生的差异,但时间差似乎可以忽略不计。这是我的代码:

    我的最终输出是numpy函数:6.534756324786595s,列表函数:6.559365831783256s

    import timeit
    import numpy as np
    
    a_setup = 'import timeit; import numpy as np'
    
    std_fx = '''
    def operate_on_std_array():
        std_arr = list(range(0,1000000))
        np_arr = np.asarray(std_arr)
        for index,elem in enumerate(std_arr):
            std_arr[index] = (elem**20)*63134
        return std_arr
    '''
    parallel_fx = '''
    def operate_on_np_arr():
        std_arr = list(range(0,1000000))
        np_arr = np.asarray(std_arr)
        np_arr = (np_arr**20)*63134
        return np_arr
    '''
    
    def operate_on_std_array():
        std_arr = list(range(0,1000000))
        np_arr = np.asarray(std_arr)
        for index,elem in enumerate(std_arr):
            std_arr[index] = (elem**20)*63134
        return std_arr
    
    def operate_on_np_arr():
        std_arr = list(range(0,1000000))
        np_arr = np.asarray(std_arr)
        np_arr = (np_arr**20)*63134
        return np_arr
    
    
    print('std',timeit.timeit(setup = a_setup, stmt = std_fx, number = 80000000))
    print('par',timeit.timeit(setup = a_setup, stmt = parallel_fx, number = 80000000))
    
    
    
    #operate_on_np_arr()
    #operate_on_std_array()
    
    2 回复  |  直到 6 年前
        1
  •  4
  •   sudo    6 年前

    The timeit docs here 表明您传入的语句应该执行某些内容,但您传入的语句只是定义函数。我在想,在100万长的阵列上进行800万次试验应该需要更长的时间。

    您在测试中遇到的其他问题:

    • np_arr = (np_arr**20)*63134 可以创建np\U arr的副本,但您的Python列表等效项只会变异现有数组。
    • Numpy数学与Python数学不同。 100**20 在Python中,返回一个巨大的数字,因为Python具有无界长度的整数,但Numpy使用的是溢出的C风格固定长度整数。(一般来说,当您使用Numpy时,您必须想象在C中执行该操作,因为可能会应用其他非直观的东西,例如未初始化数组中的垃圾。)

    下面是一个测试,我对这两个值进行了就地修改,每次都乘以然后除以31,这样值就不会随时间变化或溢出:

    import numpy as np
    import timeit
    
    std_arr = list(range(0,100000))
    np_arr = np.array(std_arr)
    np_arr_vec = np.vectorize(lambda n: (n * 31) / 31)
    
    def operate_on_std_array():
        for index,elem in enumerate(std_arr):
            std_arr[index] = elem * 31
            std_arr[index] = elem / 31
        return std_arr
    
    def operate_on_np_arr():
        np_arr_vec(np_arr)
        return np_arr
    
    
    import time
    def test_time(f):
        count = 100
        start = time.time()
        for i in range(count):
            f()
        dur = time.time() - start
        return dur
    
    print(test_time(operate_on_std_array))
    print(test_time(operate_on_np_arr))
    

    结果:

    3.0798873901367188 # standard array time
    2.221336841583252 # np array time
    

    编辑:正如@user2357112所指出的,正确的Numpy方法是:

    def operate_on_np_arr():
        global np_arr
        np_arr *= 31
        np_arr //= 31 # integer division, not double
        return np_arr
    

    使速度更快。我懂了 0.1248

        2
  •  1
  •   hpaulj    6 年前

    以下是使用 ipython 初始化列表和/或数组的魔法。结果应侧重于计算:

    In [103]: %%timeit alist = list(range(10000))
         ...: for i,e in enumerate(alist):
         ...:    alist[i] = (e*3)*20
         ...: 
    4.13 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [104]: %%timeit arr = np.arange(10000)
         ...: z = (arr*3)*20
         ...: 
    20.6 µs ± 439 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    In [105]: %%timeit alist = list(range(10000))
         ...: z = [(e*3)*20 for e in alist]
         ...: 
         ...: 
    1.71 ms ± 2.69 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    查看阵列创建时间的影响:

    In [106]: %%timeit alist = list(range(10000))
         ...: arr = np.array(alist)
         ...: z = (arr*3)*20
         ...: 
         ...: 
    1.01 ms ± 43.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    好吧,计算结果不一样。如果我使用 **3 相反,所有时间都是2倍大。相同的相对关系。