【问题标题】:Computing Autocorrelation with FFT Using JTransforms Library使用 JTransforms 库通过 FFT 计算自相关
【发布时间】:2012-09-02 19:33:36
【问题描述】:

我正在尝试使用以下代码计算时间序列中示例窗口的自相关性。我将 FFT 应用于该窗口,然后计算实部和虚部的大小并将虚部设置为零,最后对其进行逆变换以获得自相关:

DoubleFFT_1D fft = new DoubleFFT_1D(magCnt);
fft.realForward(magFFT);

magFFT[0] = (magFFT[0] * magFFT[0]);
for (int i = 1; i < (magCnt - (magCnt%2)) / 2; i++) {
    magFFT[2*i] = magFFT[2*i] * magFFT[2*i] + magFFT[2*i + 1] * magFFT[2*i + 1];
    magFFT[2*i + 1] = 0.0;
}

if (magCnt % 2 == 0) {
    magFFT[1] = (magFFT[1] * magFFT[1]);
} else {
    magFFT[magCnt/2] = (magFFT[magCnt-1] * magFFT[magCnt-1] + magFFT[1] * magFFT[1]);
}

autocorr = new double[magCnt];
System.arraycopy(magFFT, 0, autocorr, 0, magCnt);
DoubleFFT_1D ifft = new DoubleFFT_1D(magCnt);
ifft.realInverse(autocorr, false);

for (int i = 1; i < autocorr.length; i++)
    autocorr[i] /= autocorr[0];
autocorr[0] = 1.0;

第一个问题是:可以看出这段代码将自相关结果映射到[0,1]范围,虽然相关性应该在-1和1之间。当然很容易将结果映射到[-1,1]范围,但我不确定这个映射是否正确。我们如何解释生成的 autocorr 数组中的值?

其次,使用此代码,我在某些周期性序列中得到了很好的结果,也就是说,我根据信号的周期为特定的自相关指数获得了更高的值。但是,当我将其应用于非周期性信号时,结果变得很奇怪:autocorr 数组中的所有值似乎都非常接近 1。这是什么原因?

【问题讨论】:

    标签: java fft time-series correlation


    【解决方案1】:

    要使基于 FFT 的算法正常工作,您必须特别注意定义,包括您使用的库的约定。您似乎混淆了 AC 的“信号处理”约定和“统计”约定。然后是 FFT 换行和零填充。

    这是一个适用于偶数 N 情况的代码,即信号处理约定。它针对蛮力包装的自相关进行了测试。 cmets 展示了如何将其转换为信号处理约定。对于统计 ac,减去数据的平均值。这可以通过将 FFT 的“0Hz”分量归零来完成。那么 ac 的第零个元素就是方差,你可以通过除以这个量来归一化。如您所说,结果值将落在-1..1。

    您的代码似乎在进行除法,但并未忽略数据的 0 Hz 分量。所以它正在计算某种约定的混搭。

    import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
    import java.util.Arrays;
    
    public class TestFFT {
    
        void print(String msg, double [] x) {
            System.out.println(msg);
            for (double d : x) System.out.println(d);
        }
    
        /**
         * This is a "wrapped" signal processing-style autocorrelation. 
         * For "true" autocorrelation, the data must be zero padded.  
         */
        public void bruteForceAutoCorrelation(double [] x, double [] ac) {
            Arrays.fill(ac, 0);
            int n = x.length;
            for (int j = 0; j < n; j++) {
                for (int i = 0; i < n; i++) {
                    ac[j] += x[i] * x[(n + i - j) % n];
                }
            }
        }
    
        private double sqr(double x) {
            return x * x;
        }
    
        public void fftAutoCorrelation(double [] x, double [] ac) {
            int n = x.length;
            // Assumes n is even.
            DoubleFFT_1D fft = new DoubleFFT_1D(n);
            fft.realForward(x);
            ac[0] = sqr(x[0]);
            // ac[0] = 0;  // For statistical convention, zero out the mean 
            ac[1] = sqr(x[1]);
            for (int i = 2; i < n; i += 2) {
                ac[i] = sqr(x[i]) + sqr(x[i+1]);
                ac[i+1] = 0;
            }
            DoubleFFT_1D ifft = new DoubleFFT_1D(n); 
            ifft.realInverse(ac, true);
            // For statistical convention, normalize by dividing through with variance
            //for (int i = 1; i < n; i++)
            //    ac[i] /= ac[0];
            //ac[0] = 1;
        }
    
        void test() {
            double [] data = { 1, -81, 2, -15, 8, 2, -9, 0};
            double [] ac1 = new double [data.length];
            double [] ac2 = new double [data.length];
            bruteForceAutoCorrelation(data, ac1);
            fftAutoCorrelation(data, ac2);
            print("bf", ac1);
            print("fft", ac2);
            double err = 0;
            for (int i = 0; i < ac1.length; i++)
                err += sqr(ac1[i] - ac2[i]);
            System.out.println("err = " + err);
        }
    
        public static void main(String[] args) {
            new TestFFT().test();
        }
    }
    

    【讨论】:

    • 在我得到另一个问题的答案 (dsp.stackexchange.com/questions/3337/…) 后,我也这么想。有需要消除的直流分量,但我不知道如何消除它。那么,您的意思是给第一个 FFT 系数分配零会消除直流分量吗?我现在没有机会尝试。
    • 是的。如果你看 fft 的定义,第 0 个分量是输入数据的平均值。因此,将此分量归零与在进行 FFT 之前从所有数据元素中减去平均值相同。上面注释掉的代码经过轻微测试。
    • @Gene before You do ifft.realInverse(ac, true); 你正在做一些看起来像计算功率谱的事情,但公式不应该是这样的:ac[i] = Math.sqrt(sqr(x[i]) + sqr(x[i+1])); 而不是同样的事情,但没有 Math.sqrt ?
    • @YuriyKravets 正如我在答案中所说,此代码对于带有环绕的自相关的通常信号处理定义是正确的。如果不是,它不会产生与蛮力计算相同的结果,它基本上实现了定义。如果你有不同的定义,当然你需要不同的代码。
    猜你喜欢
    • 2011-04-26
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    相关资源
    最近更新 更多