在Python中使用numpy对循环进行矢量化/优化

在Python中使用numpy对循环进行矢量化/优化,python,performance,numpy,vectorization,Python,Performance,Numpy,Vectorization,我正在写一个脚本来处理信号发生器函数中表示的传感器的一些数据。正如您在测试函数中看到的,它是非常循环的。因为这个函数被多次调用,所以它有点慢,如果朝着正确的方向推一推,优化它会很好 我已经读到可以用矢量数组交换for循环,但是我不能理解I_avg[I]行应该如何写,因为我们在np.cos中有单个元素y[I]乘以整个数组x,所有这些都只是I_avg的一个缺点 def testing(signal): y = np.arange(0.0108, 0.0135, 0.001) # this o

我正在写一个脚本来处理信号发生器函数中表示的传感器的一些数据。正如您在测试函数中看到的,它是非常循环的。因为这个函数被多次调用,所以它有点慢,如果朝着正确的方向推一推,优化它会很好

我已经读到可以用矢量数组交换for循环,但是我不能理解I_avg[I]行应该如何写,因为我们在np.cos中有单个元素y[I]乘以整个数组x,所有这些都只是I_avg的一个缺点

def testing(signal):
    y = np.arange(0.0108, 0.0135, 0.001) # this one changes over time, set 
    #to constant for easier reading
    x = np.arange(0, (len(signal)))
    I_avg = np.zeros(len(y))
    Q_avg = np.zeros_like(I_avg)
    for i in range(0, len(y)):
        I_avg[i] = np.array(signal * (np.cos(2 * np.pi * y[i] * x))).sum()
        Q_avg[i] = np.array(signal * (np.sin(2 * np.pi * y[i] * x))).sum()
    D = np.power(I_avg, 2) + np.power(Q_avg, 2)
    max_index = np.argmax(D)
    phaseOut = np.arctan2(Q_avg[max_index], I_avg[max_index])

#just a test signal
def signal_gen():
    signal = np.random.random(size=251)
    return signal

使用
矩阵乘法
替换嵌套循环的一种矢量化方法,给出
I_avg,Q_avg
,并合并从而实现更有效的解决方案,就是这样-

mult = 2*np.pi*y[:,None]*x
I_avg, Q_avg = np.cos(mult).dot(signal), np.sin(mult).dot(signal)
请注意,对于给定的示例,我们正在与循环版本竞争,该版本只需迭代
3
次(
y
长度
3
)。因此,我们不会在这里看到巨大的加速

运行时测试-

In [9]: #just a test signal
   ...: signal = np.random.random(size=251)
   ...: y = np.arange(0.0108, 0.0135, 0.001)
   ...: x = np.arange(0, (len(signal)))
   ...: 

# Original approach
In [10]: %%timeit I_avg = np.zeros(len(y))
    ...: Q_avg = np.zeros_like(I_avg)
    ...: for i in range(0, len(y)):
    ...:     I_avg[i] = np.array(signal * (np.cos(2 * np.pi * y[i] * x))).sum()
    ...:     Q_avg[i] = np.array(signal * (np.sin(2 * np.pi * y[i] * x))).sum()
    ...: 
10000 loops, best of 3: 68 µs per loop

# Proposed approach
In [11]: %%timeit mult = 2*np.pi*y[:,None]*x
    ...: I_avg, Q_avg = np.cos(mult).dot(signal), np.sin(mult).dot(signal)
    ...: 
10000 loops, best of 3: 34.8 µs per loop
您可以使用以下方式进行广播:

yx = 2*np.pi*np.einsum("i,j->ij", y, x)
I_avg = np.sin(yx) @ signal    
Q_avg = np.cos(yx) @ signal 

测试中是什么?一个输入错误,它应该说“信号”max_index是另一个输入错误。你测试过这段代码吗?我测试过,但我想当我更改了两个变量名称时,我太草率了。这两个变量名称非常有效,几乎将计算时间减少了一半,而且可读性非常好。谢谢我们也可以使用
np.newaxis
代替那里的硬编码
None
。只是一个建议,虽然两者本质上都是相同的same@Runsiv考虑接受其中的一个解决方案吗?在这里阅读更多信息:谢谢你的提醒!