使用 numpy 数组乘法和 "appending" 更快地创建 FOR 循环

Make a FOR LOOP with numpy arrays multiplication and "appending" faster

这是我当前的代码。

当第二个“for循环”中的元素数量较少(大约10k)并且只需要几秒钟时它工作正常,但是当第二个“for循环”中的元素数量很高时(大约 40k) 所需的时间大约为 60 秒或更长时间:为什么?

例如:有时当第二个 for 循环有 28k 个元素时,执行它所花费的时间比它有 7k 个元素时要少。我不明白为什么时间与操作次数不是线性相关的。

还有,一般来说,代码运行的时间越长,循环次数就越大。

回顾,执行时间通常遵循以下规则:

.

from random import random
import numpy as np
import time
import gc
from collections import deque
import random

center_freq = 20000000 
smpl_time = 0.03749995312*pow(10,-6) 

mat_contents = []

for i in range(10):
    mat_contents.append(np.ones((3600, random.randint(3000,30000))))

tempo = np.empty([0,0])

for i in range(3600):
    tempo = np.append(tempo, center_freq*smpl_time*i)

ILO = np.cos(tempo) 

check = 0

for element in mat_contents:
    start_time = time.time()
    Icolumn = np.empty([0,0])
    Imatrix = deque([])
    gc.disable()

    for colonna in element.T:
        Imatrix.append(np.multiply(ILO, colonna))

    gc.enable()
    varI = np.var(Imatrix)
    tempImean = np.mean(Imatrix)

    print('\nSize: ', len(element.T))
    print("--- %s seconds ---" % (time.time() - start_time))
    print("---  ", check, ' ---')
    check += 1

你的代码终止了我的会话,大概是因为数组维度对于内存来说太大了。用较小的数字重试:

   In [1]: from collections import deque
   ...: import random
   ...: 
   ...: center_freq = 20000000
   ...: smpl_time = 0.03749995312*pow(10,-6)
   ...: 
   ...: mat_contents = []
   ...: 
   ...: for i in range(10):
   ...:     mat_contents.append(np.ones((36, random.randint(30,300))))
   ...: 
   ...: tempo = np.empty([0,0])
   ...: 
   ...: for i in range(3600):
   ...:     tempo = np.append(tempo, center_freq*smpl_time*i)
   ...: 
   ...: ILO = np.cos(tempo)
   ...: 
   ...: check = 0
In [2]: 
In [2]: tempo.shape
Out[2]: (3600,)
In [3]: ILO
Out[3]: 
array([ 1.        ,  0.73168951,  0.07073907, ..., -0.63602366,
       -0.99137119, -0.81472814])
In [4]: len(mat_contents)
Out[4]: 10

快速说明 - 虽然在 mat_contents 循环中添加列表相对较快,但下一个循环中的 np.append 很糟糕。不要那样用。

我不知道我现在是否有时间看下一个循环,但是,你为什么要在那里使用 deque?为什么不是列表?我没有使用 deque 太多,但我不认为它比右侧 append.

的列表有任何优势

更正ILO 缩小的形状

In [16]:         tempo = np.empty([0,0])
    ...:    ...:
    ...:    ...: for i in range(36):
    ...:    ...:     tempo = np.append(tempo, center_freq*smpl_time*i)
    ...: 
In [17]: tempo.shape
Out[17]: (36,)
In [18]: ILO = np.cos(tempo)

我怀疑循环可以用一个 numpy 表达式代替,但我不会深入研究它。

In [19]: %%timeit
    ...: for element in mat_contents:
    ...:     Icolumn = np.empty([0,0])
    ...:     Imatrix = deque([])
    ...:     for colonna in element.T:
    ...:         Imatrix.append(np.multiply(ILO, colonna))
    ...:     varI = np.var(Imatrix)
    ...:     tempImean = np.mean(Imatrix)
    ...: 
5.82 ms ± 6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

时间与 Imatrix = [] 相同,因此使用 deque 不会造成伤害。

mat_contents 包含不同形状的数组(第二维):

In [22]: [x.shape for x in mat_contents]
Out[22]: 
[(36, 203),
 (36, 82),
 (36, 194),
 (36, 174),
 (36, 119),
 (36, 190),
 (36, 272),
 (36, 68),
 (36, 293),
 (36, 248)]

现在让我们检查一下您担心的慢循环:

In [23]: ILO.shape
Out[23]: (36,)
In [24]: element=mat_contents[0]
In [25]: element.T.shape
Out[25]: (203, 36)
In [26]: Imatrix =[]
    ...: for colonna in element.T:
    ...:         Imatrix.append(np.multiply(ILO, colonna))
    ...: arr = np.array(Imatrix)
In [27]: arr.shape
Out[27]: (203, 36)

要将 (36,) 数组与 (203,36) 相乘,我们不需要循环。

In [28]: np.allclose(ILO*element.T, arr)
Out[28]: True
In [29]: %%timeit
    ...: Imatrix =[]
    ...: for colonna in element.T:
    ...:         Imatrix.append(np.multiply(ILO, colonna))
    ...: arr = np.array(Imatrix)
    ...: 
    ...: 
405 µs ± 2.72 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

whole-array操作快多了:

In [30]: timeit ILO*element.T
19.4 µs ± 14.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

在您最初的问题中,您加载了一个 .mat 文件。您要翻译 MATLAB 代码吗? numpy 类似于 old-time MATLAB,其中 whole-matrix 操作要快得多。 numpy 不执行 jit 循环编译。