在python中将FFT绘制为一组正弦波? [英] Plot FFT as a set of sine waves in python?

查看:131
本文介绍了在python中将FFT绘制为一组正弦波?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在演示中看到有人这样做,但是我很难重现他的能力.这是他的演讲的幻灯片:

I saw someone do this in a presentation but I'm having a hard time reproducing what he was able to do. Here's a slide from his presentation:

非常酷.他使用FFT分解了一个数据集,然后绘制了FFT指定的适当正弦波.

Pretty cool. He decomposed a dataset using FFT, then plotted the appropriate sine waves that the FFT specified.

因此,为了重新创建他所做的工作,我创建了一系列与两个正弦波组合相对应的点:

So in an effort to recreate what he did, I created a series of points that correspond to the combination of 2 sine waves:

import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

x = np.arange(0, 10, 0.01)
x2 = np.arange(0, 20, 0.02)
sin1 = np.sin(x)
sin2 = np.sin(x2)
x2 /= 2
sin3 = sin1 + sin2
plt.plot(x, sin3)
plt.show()

现在,我想将此波(或更确切地说,这些点所暗示的波)分解回原来的两个正弦波:

Now I want to decompose this wave (or rather, the wave that the points imply) back into the original 2 sine waves:

# goal: sin3 -> sin1, sin2
# sin3 
array([ 0.00000000e+00,  2.99985000e-02,  ... 3.68998236e-01])
# sin1 
array([ 0.        ,  0.00999983,  0.01999867,  ... -0.53560333])
# sin2 
array([ 0.        ,  0.01999867,  0.03998933, ... 0.90460157])

我首先导入numpy并获得sin3fft:

import numpy as np
fft3 = np.fft.fft(sin3)

好的,就我所知.现在,我得到了一个包含复数的数组:

ok, that's about as far as I get. Now I've got an array with complex numbers:

array([ 2.13316069e+02+0.00000000e+00j,  3.36520138e+02+4.05677438e+01j,...])

如果我天真地绘制它,我会看到:

and if I naively plot it I see:

plt.plot(fft3)
plt.show()

好吧,不确定该怎么做.

Ok, not sure what to do with that.

我想从这里转到看起来像sin1和sin2的数据集:

I want to get from here to the datasets that look like sin1 and sin2:

plt.plot(sin1)
plt.show()

plt.plot(sin2)
plt.show()

我了解fft3数据集中复数的实部和虚部,我只是不确定如何处理它们以从中导出sin1sin2数据集.

I understand the real and imaginary part of the complex numbers in the fft3 dataset, I'm just not sure what to do with them to derive sin1 and sin2 datasets from it.

我知道这与编程无关,而与数学有关,但是有人可以在这里给我一个提示吗?

I know this has less to do with programming and more to do with math, but could anyone give me a hint here?

更新马克·斯奈德的答案:

update on Mark Snyder's answer:

使用Mark的代码,我能够获得期望的结果,并最终使用此方法:

Using Mark's code I was able to get what I expected and ended up with this method:

def decompose_fft(data: list, threshold: float = 0.0):
    fft3 = np.fft.fft(data)
    x = np.arange(0, 10, 10 / len(data))
    freqs = np.fft.fftfreq(len(x), .01)
    recomb = np.zeros((len(x),))
    for i in range(len(fft3)):
        if abs(fft3[i]) / len(x) > threshold:
            sinewave = (
                1 
                / len(x) 
                * (
                    fft3[i].real 
                    * np.cos(freqs[i] * 2 * np.pi * x) 
                    - fft3[i].imag 
                    * np.sin(freqs[i] * 2 * np.pi * x)))
            recomb += sinewave
            plt.plot(x, sinewave)
    plt.show()

    plt.plot(x, recomb, x, data)
    plt.show()

稍后,我将使它返回重新组合的wave列表,但是现在我有点反常了,我不太了解.首先,我这样称呼它,只需传入数据集即可.

later I'll make it return the recombined list of waves, but for now I'm getting an anomalie I don't quite understand. First of all I call it like this, simply passing in a dataset.

decompose_fft(sin3, threshold=0.0)

但是看起来不错,但是我在y=0.2上看到这条怪异的行吗?有人知道这可能是什么或者是什么引起的吗?

But looks great but I get this weird line at y=0.2 Does anyone know what this could be or what's causing it?

马克在评论中回答了以上问题,谢谢!

The above question has been answered by Mark in the comments, thanks!

推荐答案

离散傅里叶变换为您提供了复杂指数的系数,这些系数相加后会产生原始离散信号.特别是,第k个傅里叶系数可为您提供有关在给定数量的样本上具有k个周期的正弦波振幅的信息.

The discrete Fourier transform gives you the coefficients of complex exponentials that, when summed together, produce the original discrete signal. In particular, the k'th Fourier coefficient gives you information about the amplitude of the sinusoid that has k cycles over the given number of samples.

请注意,由于您的正弦波在1000个样本中没有整数个周期,因此实际上您将无法使用FFT检索原始正弦波.取而代之的是,您将混合许多不同的正弦曲线,其中包括〜.4的常量.

Note that since your sines don't have integer numbers of cycles in 1000 samples, you actually will not be able to retrieve your original sine waves using an FFT. Instead you'll get a blend of many different sinusoids, including a constant component of ~.4.

您可以绘制各种分量正弦曲线,并使用以下代码观察它们的和为原始信号:

You can plot the various component sinusoids and observe that their sum is the original signal using the following code:

freqs = np.fft.fftfreq(len(x),.01)
threshold = 0.0
recomb = np.zeros((len(x),))
for i in range(len(fft3)):
    if abs(fft3[i])/(len(x)) > threshold:
        recomb += 1/(len(x))*(fft3[i].real*np.cos(freqs[i]*2*np.pi*x)-fft3[i].imag*np.sin(freqs[i]*2*np.pi*x))
        plt.plot(x,1/(len(x))*(fft3[i].real*np.cos(freqs[i]*2*np.pi*x)-fft3[i].imag*np.sin(freqs[i]*2*np.pi*x)))
plt.show()

plt.plot(x,recomb,x,sin3)
plt.show()

通过更改threshold,您还可以选择排除低功率的正弦波,并查看其如何影响最终的重建.

By changing threshold, you can also choose to exclude sinusoids of low power and see how that affects the final reconstruction.

上面的代码中有一些陷阱,尽管这没有错.它隐藏了DFT对于真实信号的固有对称性,并以其真实幅度的一半绘制了每个正弦曲线两次.该代码性能更高,并以正确的幅度绘制正弦曲线:

There's a bit of a trap in the above code, although it's not wrong. It hides the inherent symmetry of the DFT for real signals, and plots each of the sinusoids twice at half of their true amplitude. This code is more performant and plots the sinusoids at their correct amplitude:

freqs = np.fft.fftfreq(len(x),.01)
threshold = 0.0
recomb = np.zeros((len(x),))
middle = len(x)//2 + 1
for i in range(middle):
    if abs(fft3[i])/(len(x)) > threshold:
        if i == 0:
            coeff = 2
        else:
            coeff = 1
        sinusoid = 1/(len(x)*coeff/2)*(abs(fft3[i])*np.cos(freqs[i]*2*np.pi*x+cmath.phase(fft3[i])))
        recomb += sinusoid
        plt.plot(x,sinusoid)
plt.show()

plt.plot(x,recomb,x,sin3)
plt.show()

在一般情况下,如果您知道信号是由一些正弦波子集组成的,这些子集的频率可能与信号的长度未正确对齐,则可以通过零填充或扩展来识别频率你的信号.您可以在此处了解更多信息.如果信号是完全任意的,而您只是对查看分量正弦波感兴趣,则无需这样做.

If in the general case you know that the signal was composed of some subset of sinusoids with frequencies that may not line up correctly with the length of the signal, you may be able to identify the frequencies by either zero-padding or extending your signal. You can learn more about that here. If the signals are completely arbitrary and you're just interested in looking at component sinusoids, there's no need for that.

这篇关于在python中将FFT绘制为一组正弦波?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆