我应该如何将 scipy.fftpack 输出向量相乘?

Cor*_*one 5 python scipy complex-numbers fftpack

该scipy.fftpack.rfft函数将 DFT 作为浮点向量返回,在实部和复部之间交替。这意味着要一起乘以 DFT(对于卷积),我将不得不“手动”进行复杂的乘法,这似乎非常棘手。这一定是人们经常做的事情 - 我假设/希望有一个简单的技巧可以有效地做到这一点,但我没有发现?

基本上我想修复此代码,以便两种方法都给出相同的答案:

import numpy as np
import scipy.fftpack as sfft

X = np.random.normal(size = 2000)
Y = np.random.normal(size = 2000)
NZ = np.fft.irfft(np.fft.rfft(Y) * np.fft.rfft(X))
SZ = sfft.irfft(sfft.rfft(Y) * sfft.rfft(X))    # This multiplication is wrong

NZ
array([-43.23961083,  53.62608086,  17.92013729, ..., -16.57605207,
     8.19605764,   5.23929023])
SZ
array([-19.90115323,  16.98680347,  -8.16608202, ..., -47.01643274,
    -3.50572376,  58.1961597 ])
Run Code Online (Sandbox Code Playgroud)

N.B. I am aware that fftpack contains a convolve function, but I only need to fft one half of the transform - my filter can be fft'd once in advance and then used over and over again.

小智 4

您不必再翻回np.float64和hstack。您可以创建一个空的目标数组,其形状与sfft.rfft(Y)和相同sfft.rfft(X),然后创建np.complex128它的视图并用乘法结果填充该视图。这将根据需要自动填充目标数组。
如果我重新举你的例子:

import numpy as np
import scipy.fftpack as sfft

X = np.random.normal(size = 2000)
Y = np.random.normal(size = 2000)
Xf = np.fft.rfft(X)
Xf_cpx = Xf[1:-1].view(np.complex128)
Yf = np.fft.rfft(Y)
Yf_cpx = Yf[1:-1].view(np.complex128)

Zf = np.empty(X.shape)
Zf_cpx = Zf[1:-1].view(np.complex128)

Zf[0] = Xf[0]*Yf[0]

# the [...] is important to use the view as a reference to Zf and not overwrite it
Zf_cpx[...] = Xf_cpx * Yf_cpx 

Zf[-1] = Xf[-1]*Yf[-1]

Z = sfft.irfft.irfft(Zf)
Run Code Online (Sandbox Code Playgroud)

就是这样!如果您希望代码更通用并处理奇数长度(如 Jaime 的答案中所述),您可以使用简单的 if 语句。这是一个可以完成您想要的功能的函数:

def rfft_mult(a,b):
    """Multiplies two outputs of scipy.fftpack.rfft"""
    assert a.shape == b.shape
    c = np.empty( a.shape )
    c[...,0] = a[...,0]*b[...,0]
    # To comply with the rfft support of multi dimensional arrays
    ar = a.reshape(-1,a.shape[-1])
    br = b.reshape(-1,b.shape[-1])
    cr = c.reshape(-1,c.shape[-1])
    # Note that we cannot use ellipses to achieve that because of 
    # the way `view` work. If there are many dimensions, one should 
    # consider to manually perform the complex multiplication with slices.
    if c.shape[-1] & 0x1: # if odd
        for i in range(len(ar)):
            ac = ar[i,1:].view(np.complex128)
            bc = br[i,1:].view(np.complex128)
            cc = cr[i,1:].view(np.complex128)
            cc[...] = ac*bc
    else:
        for i in range(len(ar)):
            ac = ar[i,1:-1].view(np.complex128)
            bc = br[i,1:-1].view(np.complex128)
            cc = cr[i,1:-1].view(np.complex128)
            cc[...] = ac*bc
        c[...,-1] = a[...,-1]*b[...,-1]
    return c
Run Code Online (Sandbox Code Playgroud)