การวางแผนการแปลงฟูเรียร์อย่างรวดเร็วใน Python


95

ฉันสามารถเข้าถึง NumPy และ SciPy และต้องการสร้าง FFT ง่ายๆของชุดข้อมูล ฉันมีสองรายการรายการหนึ่งคือyค่าและอีกรายการคือการประทับเวลาสำหรับyค่าเหล่านั้น

วิธีที่ง่ายที่สุดในการป้อนรายการเหล่านี้เป็นวิธี SciPy หรือ NumPy และพล็อต FFT ที่ได้คืออะไร

ฉันได้ค้นหาตัวอย่าง แต่พวกเขาทั้งหมดอาศัยการสร้างชุดข้อมูลปลอมที่มีจุดข้อมูลจำนวนหนึ่งและความถี่ ฯลฯ และไม่ได้แสดงวิธีการทำเพียงชุดข้อมูลและการประทับเวลาที่เกี่ยวข้อง .

ฉันได้ลองตัวอย่างต่อไปนี้:

from scipy.fftpack import fft

# Number of samplepoints
N = 600

# Sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.grid()
plt.show()

แต่เมื่อฉันเปลี่ยนอาร์กิวเมนต์fftเป็นชุดข้อมูลของฉันและลงจุดฉันได้ผลลัพธ์ที่แปลกมากและดูเหมือนว่าการสเกลของความถี่อาจดับลง ฉันไม่แน่ใจ

นี่คือ pastebin ของข้อมูลที่ฉันพยายามจะ FFT

http://pastebin.com/0WhjjMkb http://pastebin.com/ksM4FvZS

เมื่อฉันใช้fft()กับทุกสิ่งมันก็มีการเพิ่มขึ้นอย่างมากที่ศูนย์และไม่มีอะไรอื่น

นี่คือรหัสของฉัน:

## Perform FFT with SciPy
signalFFT = fft(yInterp)

## Get power spectral density
signalPSD = np.abs(signalFFT) ** 2

## Get frequencies corresponding to signal PSD
fftFreq = fftfreq(len(signalPSD), spacing)

## Get positive half of frequencies
i = fftfreq>0

##
plt.figurefigsize = (8, 4));
plt.plot(fftFreq[i], 10*np.log10(signalPSD[i]));
#plt.xlim(0, 100);
plt.xlabel('Frequency [Hz]');
plt.ylabel('PSD [dB]')

ระยะห่างเท่ากับxInterp[1]-xInterp[0].


แสดงให้เราเห็นว่าคุณได้ลองทำอะไรล้มเหลวอย่างไรและตัวอย่างที่คุณกำลังดำเนินการ
Paul H

ฉันโพสต์ตัวอย่างที่ฉันลองและสิ่งที่ฉันคิดฉันคิดว่าฉันแค่สับสนกับวิธีการพล็อตผลลัพธ์อย่างถูกต้อง
user3123955

เป็นตัวอย่างที่ดี แต่ปัญหาคืออะไร? รหัสนั้นใช้งานได้ดีสำหรับฉัน พล็อตไม่ปรากฏหรือไม่?
Paul H

กล่าวคือคุณใช้ข้อโต้แย้งประเภทใด (เราต้องดูข้อมูลของคุณอย่างน้อยที่สุด)
Paul H

ฉันได้เพิ่ม pastebin ของแกน x และ y แล้วข้อมูล x เป็นวินาทีและข้อมูล y เป็นเพียงการอ่านเซ็นเซอร์ เมื่อฉันใส่รายการข้อมูลเหล่านี้ลงในตัวอย่าง fft มันก็มีการเพิ่มขึ้นอย่างมากที่ศูนย์
user3123955

คำตอบ:


99

ดังนั้นฉันจึงเรียกใช้รูปแบบที่เทียบเท่ากับการทำงานของรหัสของคุณในสมุดบันทึก IPython:

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

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
plt.show()

ฉันได้รับสิ่งที่ฉันเชื่อว่าเป็นผลลัพธ์ที่สมเหตุสมผลมาก

ป้อนคำอธิบายภาพที่นี่

เป็นเวลานานกว่าที่ฉันจะยอมรับตั้งแต่ฉันอยู่ในโรงเรียนวิศวกรรมที่คิดเกี่ยวกับการประมวลผลสัญญาณ แต่การเพิ่มขึ้นอย่างรวดเร็วที่ 50 และ 80 เป็นสิ่งที่ฉันคาดหวัง ปัญหาคืออะไร?

เพื่อตอบสนองต่อข้อมูลดิบและความคิดเห็นที่โพสต์

ปัญหาคือคุณไม่มีข้อมูลเป็นระยะ คุณควรตรวจสอบข้อมูลที่ป้อนลงในอัลกอริทึมทุกครั้งเพื่อให้แน่ใจว่าเหมาะสม

import pandas
import matplotlib.pyplot as plt
#import seaborn
%matplotlib inline

# the OP's data
x = pandas.read_csv('http://pastebin.com/raw.php?i=ksM4FvZS', skiprows=2, header=None).values
y = pandas.read_csv('http://pastebin.com/raw.php?i=0WhjjMkb', skiprows=2, header=None).values
fig, ax = plt.subplots()
ax.plot(x, y)

ป้อนคำอธิบายภาพที่นี่


1
ไม่ใช่ว่าตัวอย่างนั้นผิด แต่ฉันไม่รู้ว่าจะเอาอย่างไรและนำไปใช้กับข้อมูลของฉัน
user3123955

@ user3123955 ขวา. นั่นเป็นเหตุผลว่าทำไมเราจึงต้องดูข้อมูลของคุณและจะล้มเหลวได้อย่างไรหากคุณกำลังจะช่วยคุณ
Paul H

ฉันได้เพิ่ม pastebin แล้ว
user3123955

2
@ user3123955 แล้วคุณคาดหวังว่าอัลกอริทึม FFT จะทำอย่างไร คุณต้องล้างข้อมูลของคุณ
Paul H

6
@PaulH ไม่ควรกว้างที่ความถี่50 Hzเป็น1และที่ความถี่80 Hzจะเป็น0.5?
Furqan Hashim

24

สิ่งสำคัญเกี่ยวกับ fft คือสามารถใช้ได้กับข้อมูลที่การประทับเวลามีความสม่ำเสมอเท่านั้น ( เช่นการสุ่มตัวอย่างแบบสม่ำเสมอตามเวลาเช่นเดียวกับที่คุณได้แสดงไว้ด้านบน)

ในกรณีของการสุ่มตัวอย่างแบบไม่สม่ำเสมอโปรดใช้ฟังก์ชันเพื่อปรับข้อมูลให้เหมาะสม มีบทช่วยสอนและฟังก์ชั่นมากมายให้เลือก:

https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting http://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html

หากการปรับให้เหมาะสมไม่ใช่ตัวเลือกคุณสามารถใช้การแก้ไขบางรูปแบบเพื่อแก้ไขข้อมูลให้เป็นการสุ่มตัวอย่างแบบสม่ำเสมอได้โดยตรง:

https://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/interpolate.html

เมื่อคุณมีตัวอย่างที่สม่ำเสมอคุณจะต้องกังวลเกี่ยวกับเวลาเดลต้า ( t[1] - t[0]) ของตัวอย่างเท่านั้น ในกรณีนี้คุณสามารถใช้ฟังก์ชัน fft ได้โดยตรง

Y    = numpy.fft.fft(y)
freq = numpy.fft.fftfreq(len(y), t[1] - t[0])

pylab.figure()
pylab.plot( freq, numpy.abs(Y) )
pylab.figure()
pylab.plot(freq, numpy.angle(Y) )
pylab.show()

วิธีนี้จะช่วยแก้ปัญหาของคุณได้


2
ฉันได้แก้ไขข้อมูลของฉันสำหรับการเว้นระยะห่างคุณช่วยบอกฉันได้ไหมว่า fftfreq ทำอะไร? ทำไมต้องใช้แกน x ของฉัน ทำไมคุณถึงวางแผน abs ของ Y และมุม? มุมเป็นเฟสหรือไม่? เฟสสัมพันธ์กับอะไร? เมื่อฉันทำสิ่งนี้กับข้อมูลของฉันมันมีจุดสูงสุดขนาดยักษ์ที่ 0Hz และปิดท้ายอย่างรวดเร็ว แต่ฉันกำลังป้อนข้อมูลที่ไม่มีค่าชดเชยคงที่ (ฉันทำแบนด์พาสขนาดใหญ่บนข้อมูลที่มีขอบ 0.15 Gz ถึง 12Hz เพื่อกำจัดค่าชดเชยคงที่ข้อมูลของฉันไม่ควรมีขนาดใหญ่กว่า 4 Hz อยู่ดีดังนั้นวงดนตรีจะทำให้ฉันสูญเสียข้อมูล)
user3123955

2
1. fftfreqให้ส่วนประกอบความถี่ที่สอดคล้องกับข้อมูลของคุณ หากคุณลงจุดfreqคุณจะเห็นว่าแกน x ไม่ใช่ฟังก์ชันที่เพิ่มขึ้นเรื่อย ๆ คุณจะต้องแน่ใจว่าคุณมีส่วนประกอบความถี่ที่ถูกต้องในแกน x สามารถดูคู่มือได้ที่: docs.scipy.org/doc/numpy/reference/generated/…
ssm

2
2. คนส่วนใหญ่จะชอบดูขนาดและระยะของ fft มันยากที่จะอธิบายในประโยคเดียวว่าข้อมูลเฟสจะบอกอะไรคุณ แต่ทั้งหมดที่ฉันพูดได้ก็คือมันมีความหมายเมื่อคุณรวมสัญญาณ เมื่อคุณรวมสัญญาณที่มีความถี่เดียวกันซึ่งอยู่ในเฟสสัญญาณจะขยายในขณะที่สัญญาณเหล่านั้นอยู่นอกเฟส 180 องศาสัญญาณเหล่านั้นจะลดทอน สิ่งนี้มีความสำคัญเมื่อคุณออกแบบเครื่องขยายเสียงหรือสิ่งใด ๆ ที่มีข้อเสนอแนะ
ssm

2
3. โดยทั่วไปความถี่ต่ำสุดของคุณจะมีเฟสเป็นศูนย์จริงและอ้างอิงถึงสิ่งนี้ เมื่อสัญญาณเคลื่อนที่ผ่านระบบของคุณทุกความถี่จะเคลื่อนที่ด้วยความเร็วที่แตกต่างกัน นี่คือความเร็วเฟส พล็อตเฟสให้ข้อมูลนี้แก่คุณ ฉันไม่รู้ว่าคุณกำลังทำงานกับอะไรจึงไม่สามารถให้คำตอบที่แน่ชัดได้ สำหรับคำถามดังกล่าวจะเป็นการดีกว่าหากอ่านข้อมูลเกี่ยวกับการควบคุมป้อนกลับอิเลคทรอนิกส์แบบอะนาล็อกการประมวลผลสัญญาณดิจิทัลทฤษฎีสนามไฟฟ้า ฯลฯ หรือสิ่งที่เฉพาะเจาะจงมากขึ้นสำหรับระบบของคุณ
ssm

4
4. แทนที่จะใช้ของคุณt = linspace(0, 10, 1000); ys = [ (1.0/i)*sin(i*t) for i in arange(10)]; y = reduce(lambda m, n: m+n, ys)ข้อมูลทำไมคุณไม่เริ่มต้นด้วยการสร้างสัญญาณของคุณเอง: จากนั้นพล็อตแต่ละส่วนysและผลรวมyและรับ fft ของแต่ละองค์ประกอบ คุณจะได้รับความมั่นใจกับการเขียนโปรแกรมของคุณ จากนั้นคุณสามารถตัดสินความถูกต้องของผลลัพธ์ของคุณ หากสัญญาณที่คุณพยายามวิเคราะห์เป็นสัญญาณแรกที่คุณได้รับจากนั้นคุณจะรู้สึกว่าคุณทำอะไรผิดพลาดอยู่เสมอ ...
ssm

12

การเพิ่มขึ้นอย่างรวดเร็วที่คุณมีเกิดจากส่วน DC (ไม่แปรผันเช่น freq = 0) ของสัญญาณของคุณ มันเป็นปัญหาของขนาด หากคุณต้องการดูเนื้อหาความถี่ที่ไม่ใช่ DC สำหรับการแสดงภาพคุณอาจต้องพล็อตจากออฟเซ็ต 1 ไม่ใช่จากออฟเซ็ต 0 ของ FFT ของสัญญาณ

การแก้ไขตัวอย่างที่ให้ไว้ข้างต้นโดย @PaulH

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = 10 + np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

plt.subplot(2, 1, 1)
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.subplot(2, 1, 2)
plt.plot(xf[1:], 2.0/N * np.abs(yf[0:N/2])[1:])

แปลงผลลัพธ์: การพล็อตสัญญาณ FFT ด้วย DC จากนั้นเมื่อถอดออก (ข้าม freq = 0)

อีกวิธีหนึ่งคือการแสดงภาพข้อมูลในมาตราส่วนบันทึก:

ใช้:

plt.semilogy(xf, 2.0/N * np.abs(yf[0:N/2]))

จะแสดง: ป้อนคำอธิบายภาพที่นี่


ใช่มันเป็นเฮิร์ตซ์ ในโค้ดคำจำกัดความของการxfแมป fft bins กับความถี่
hesham_EE

1
ดี! แล้วแกน y ล่ะ? แอมพลิจูด? ขอบคุณมาก hesham_EE
Victor Aguiar

ใช่แกน y คือค่าสัมบูรณ์ของ fft เชิงซ้อน สังเกตการใช้np.abs()
hesham_EE

8

ฉันอยากจะชี้ให้เห็นว่าบ่อยครั้งที่การเล่นกับขนาดของถังขยะสำหรับ FFT เป็นสิ่งสำคัญ คุณควรทดสอบค่าต่างๆและเลือกค่าที่เหมาะสมกับแอปพลิเคชันของคุณมากขึ้น บ่อยครั้งที่มีขนาดเท่ากันของจำนวนตัวอย่าง นี่เป็นไปตามที่คาดเดาโดยคำตอบส่วนใหญ่ที่ได้รับและให้ผลลัพธ์ที่ดีและสมเหตุสมผล ในกรณีที่ต้องการสำรวจสิ่งนี้นี่คือรหัสรุ่นของฉัน:

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

fig = plt.figure(figsize=[14,4])
N = 600           # Number of samplepoints
Fs = 800.0
T = 1.0 / Fs      # N_samps*T (#samples x sample period) is the sample spacing.
N_fft = 80        # Number of bins (chooses granularity)
x = np.linspace(0, N*T, N)     # the interval
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)   # the signal

# removing the mean of the signal
mean_removed = np.ones_like(y)*np.mean(y)
y = y - mean_removed

# Compute the fft.
yf = scipy.fftpack.fft(y,n=N_fft)
xf = np.arange(0,Fs,Fs/N_fft)

##### Plot the fft #####
ax = plt.subplot(121)
pt, = ax.plot(xf,np.abs(yf), lw=2.0, c='b')
p = plt.Rectangle((Fs/2, 0), Fs/2, ax.get_ylim()[1], facecolor="grey", fill=True, alpha=0.75, hatch="/", zorder=3)
ax.add_patch(p)
ax.set_xlim((ax.get_xlim()[0],Fs))
ax.set_title('FFT', fontsize= 16, fontweight="bold")
ax.set_ylabel('FFT magnitude (power)')
ax.set_xlabel('Frequency (Hz)')
plt.legend((p,), ('mirrowed',))
ax.grid()

##### Close up on the graph of fft#######
# This is the same histogram above, but truncated at the max frequence + an offset. 
offset = 1    # just to help the visualization. Nothing important.
ax2 = fig.add_subplot(122)
ax2.plot(xf,np.abs(yf), lw=2.0, c='b')
ax2.set_xticks(xf)
ax2.set_xlim(-1,int(Fs/6)+offset)
ax2.set_title('FFT close-up', fontsize= 16, fontweight="bold")
ax2.set_ylabel('FFT magnitude (power) - log')
ax2.set_xlabel('Frequency (Hz)')
ax2.hold(True)
ax2.grid()

plt.yscale('log')

แปลงผลลัพธ์: ป้อนคำอธิบายภาพที่นี่


5

ฉันได้สร้างฟังก์ชันที่เกี่ยวข้องกับการวางแผน FFT ของสัญญาณจริง โบนัสพิเศษในฟังก์ชันของฉันเมื่อเทียบกับข้อความข้างต้นคือคุณได้รับแอมพลิจูดของสัญญาณจริง นอกจากนี้เนื่องจากสมมติฐานของสัญญาณจริง FFT จึงสมมาตรดังนั้นเราจึงสามารถพล็อตเฉพาะด้านบวกของแกน x:

import matplotlib.pyplot as plt
import numpy as np
import warnings


def fftPlot(sig, dt=None, plot=True):
    # here it's assumes analytic signal (real signal...)- so only half of the axis is required

    if dt is None:
        dt = 1
        t = np.arange(0, sig.shape[-1])
        xLabel = 'samples'
    else:
        t = np.arange(0, sig.shape[-1]) * dt
        xLabel = 'freq [Hz]'

    if sig.shape[0] % 2 != 0:
        warnings.warn("signal prefered to be even in size, autoFixing it...")
        t = t[0:-1]
        sig = sig[0:-1]

    sigFFT = np.fft.fft(sig) / t.shape[0]  # divided by size t for coherent magnitude

    freq = np.fft.fftfreq(t.shape[0], d=dt)

    # plot analytic signal - right half of freq axis needed only...
    firstNegInd = np.argmax(freq < 0)
    freqAxisPos = freq[0:firstNegInd]
    sigFFTPos = 2 * sigFFT[0:firstNegInd]  # *2 because of magnitude of analytic signal

    if plot:
        plt.figure()
        plt.plot(freqAxisPos, np.abs(sigFFTPos))
        plt.xlabel(xLabel)
        plt.ylabel('mag')
        plt.title('Analytic FFT plot')
        plt.show()

    return sigFFTPos, freqAxisPos


if __name__ == "__main__":
    dt = 1 / 1000

    # build a signal within nyquist - the result will be the positive FFT with actual magnitude
    f0 = 200  # [Hz]
    t = np.arange(0, 1 + dt, dt)
    sig = 1 * np.sin(2 * np.pi * f0 * t) + \
        10 * np.sin(2 * np.pi * f0 / 2 * t) + \
        3 * np.sin(2 * np.pi * f0 / 4 * t) +\
        7.5 * np.sin(2 * np.pi * f0 / 5 * t)

    # res in freqs
    fftPlot(sig, dt=dt)
    # res in samples (if freqs axis is unknown)
    fftPlot(sig)

ผลการวิเคราะห์ FFT


4

มีโซลูชันที่ยอดเยี่ยมอยู่แล้วในหน้านี้ แต่ทุกคนถือว่าชุดข้อมูลมีการสุ่มตัวอย่าง / กระจายอย่างสม่ำเสมอ / สม่ำเสมอ ฉันจะพยายามให้ตัวอย่างทั่วไปของข้อมูลที่สุ่มตัวอย่างแบบสุ่ม ฉันจะใช้บทช่วยสอน MATLAB นี้ด้วยเป็นตัวอย่าง:

การเพิ่มโมดูลที่ต้องการ:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
import scipy.signal

การสร้างข้อมูลตัวอย่าง:

N = 600 # number of samples
t = np.random.uniform(0.0, 1.0, N) # assuming the time start is 0.0 and time end is 1.0
S = 1.0 * np.sin(50.0 * 2 * np.pi * t) + 0.5 * np.sin(80.0 * 2 * np.pi * t) 
X = S + 0.01 * np.random.randn(N) # adding noise

การจัดเรียงชุดข้อมูล:

order = np.argsort(t)
ts = np.array(t)[order]
Xs = np.array(X)[order]

การสุ่มตัวอย่างใหม่:

T = (t.max() - t.min()) / N # average period 
Fs = 1 / T # average sample rate frequency
f = Fs * np.arange(0, N // 2 + 1) / N; # resampled frequency vector
X_new, t_new = scipy.signal.resample(Xs, N, ts)

การลงจุดข้อมูลและข้อมูลตัวอย่าง:

plt.xlim(0, 0.1)
plt.plot(t_new, X_new, label="resampled")
plt.plot(ts, Xs, label="org")
plt.legend()
plt.ylabel("X")
plt.xlabel("t")

ป้อนคำอธิบายภาพที่นี่

ตอนนี้กำลังคำนวณ fft:

Y = scipy.fftpack.fft(X_new)
P2 = np.abs(Y / N)
P1 = P2[0 : N // 2 + 1]
P1[1 : -2] = 2 * P1[1 : -2]

plt.ylabel("Y")
plt.xlabel("f")
plt.plot(f, P1)

ป้อนคำอธิบายภาพที่นี่

ป.ล.ในที่สุดฉันก็มีเวลาใช้อัลกอริทึมที่ยอมรับได้มากขึ้นเพื่อให้ได้การแปลงฟูริเยร์ของข้อมูลที่กระจายไม่สม่ำเสมอ คุณอาจเห็นรหัสคำอธิบายและยกตัวอย่างเช่น Jupyter โน๊ตบุ๊คที่นี่


ฉันไม่เห็นอะไรเลยในเอกสารที่แนะนำให้resampleจัดการกับเวลาตัวอย่างที่ไม่สม่ำเสมอ ยอมรับพารามิเตอร์เวลา (ซึ่งไม่ได้ใช้ในตัวอย่าง) แต่ดูเหมือนว่าจะถือว่าเวลาตัวอย่างสม่ำเสมอเช่นกัน
user2699

@ user2699 ตัวอย่างนี้อาจช่วยได้
Foad

@ user2699 แก้ไขรหัส จะขอบคุณถ้าคุณสามารถดูและแจ้งให้เราทราบว่าตอนนี้ใช้ได้หรือไม่
Foad

1
'scipy.signal.resample` ใช้วิธี FFT เพื่อสุ่มตัวอย่างข้อมูลอีกครั้ง ไม่มีเหตุผลที่จะใช้เพื่อสุ่มตัวอย่างข้อมูลที่ไม่สม่ำเสมอเพื่อให้ได้ FFT ที่สม่ำเสมอ
user2699

1
มีข้อดีและข้อเสียสำหรับวิธีการทั้งหมดที่คุณให้ไว้ (แม้ว่าโปรดทราบว่าsklearn.utils.resampleไม่มีการแก้ไข) หากคุณต้องการหารือเกี่ยวกับตัวเลือกในการค้นหาความถี่ของสัญญาณสุ่มตัวอย่างที่ไม่สม่ำเสมอหรือข้อดีของการแก้ไขประเภทต่างๆโปรดเริ่มคำถามใหม่ ทั้งสองเรื่องเป็นเรื่องที่น่าสนใจ แต่อยู่นอกเหนือขอบเขตของคำตอบเกี่ยวกับวิธีการวางแผน FFT
user2699

4

ฉันเขียนคำตอบเพิ่มเติมนี้เพื่ออธิบายที่มาของการแพร่กระจายของ spikes เมื่อใช้ fft และโดยเฉพาะอย่างยิ่งพูดถึงscipy.fftpackบทสอนซึ่งฉันไม่เห็นด้วยในบางประเด็น

tmax=N*T=0.75ในตัวอย่างนี้เวลาในการบันทึก สัญญาณคือsin(50*2*pi*x)+0.5*sin(80*2*pi*x). สัญญาณความถี่ควรมี 2 spikes ที่ความถี่50และ80มีแอมพลิจูด1และ0.5. อย่างไรก็ตามหากสัญญาณที่วิเคราะห์ไม่มีจำนวนเต็มของช่วงเวลาการแพร่กระจายอาจปรากฏขึ้นได้เนื่องจากการตัดทอนสัญญาณ:

  • หอก 1: 50*tmax=37.5=> ความถี่50ไม่ใช่ผลคูณของ1/tmax=> การปรากฏตัวของการแพร่กระจายเนื่องจากการตัดสัญญาณที่ความถี่นี้
  • หอก 2: 80*tmax=60=> ความถี่80เป็นผลคูณของ1/tmax=> ไม่มีการแพร่กระจายเนื่องจากการตัดสัญญาณที่ความถี่นี้

นี่คือรหัสที่วิเคราะห์สัญญาณเดียวกันกับในบทช่วยสอน ( sin(50*2*pi*x)+0.5*sin(80*2*pi*x)) แต่มีความแตกต่างเล็กน้อย:

  1. ตัวอย่าง scipy.fftpack ดั้งเดิม
  2. ตัวอย่าง scipy.fftpack ดั้งเดิมที่มีจำนวนคาบสัญญาณจำนวนเต็ม ( tmax=1.0แทนที่จะ0.75หลีกเลี่ยงการตัดการแพร่กระจาย)
  3. ตัวอย่าง scipy.fftpack ดั้งเดิมที่มีจำนวนช่วงเวลาสัญญาณจำนวนเต็มและวันที่และความถี่ถูกนำมาจากทฤษฎี FFT

รหัส:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# 1. Linspace
N = 600
# sample spacing
tmax = 3/4
T = tmax / N # =1.0 / 800.0
x1 = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x1) + 0.5*np.sin(80.0 * 2.0*np.pi*x1)
yf1 = scipy.fftpack.fft(y1)
xf1 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 2. Integer number of periods
tmax = 1
T = tmax / N # sample spacing
x2 = np.linspace(0.0, N*T, N)
y2 = np.sin(50.0 * 2.0*np.pi*x2) + 0.5*np.sin(80.0 * 2.0*np.pi*x2)
yf2 = scipy.fftpack.fft(y2)
xf2 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 3. Correct positionning of dates relatively to FFT theory (arange instead of linspace)
tmax = 1
T = tmax / N # sample spacing
x3 = T * np.arange(N)
y3 = np.sin(50.0 * 2.0*np.pi*x3) + 0.5*np.sin(80.0 * 2.0*np.pi*x3)
yf3 = scipy.fftpack.fft(y3)
xf3 = 1/(N*T) * np.arange(N)[:N//2]

fig, ax = plt.subplots()
# Plotting only the left part of the spectrum to not show aliasing
ax.plot(xf1, 2.0/N * np.abs(yf1[:N//2]), label='fftpack tutorial')
ax.plot(xf2, 2.0/N * np.abs(yf2[:N//2]), label='Integer number of periods')
ax.plot(xf3, 2.0/N * np.abs(yf3[:N//2]), label='Correct positionning of dates')
plt.legend()
plt.grid()
plt.show()

เอาท์พุต:

ดังที่เป็นไปได้ที่นี่แม้จะใช้จำนวนเต็มของช่วงเวลา แต่การแพร่กระจายก็ยังคงอยู่ พฤติกรรมนี้เกิดจากการจัดตำแหน่งวันที่และความถี่ที่ไม่ถูกต้องในบทแนะนำ scipy.fftpack ดังนั้นในทฤษฎีการแปลงฟูเรียร์แบบไม่ต่อเนื่อง:

  • สัญญาณควรมีการประเมิน ณ วันt=0,T,...,(N-1)*Tที่ T tmax=N*Tคือระยะเวลาการสุ่มตัวอย่างและระยะเวลารวมของสัญญาณ tmax-Tโปรดทราบว่าเราจะหยุดที่
  • ความถี่ที่เกี่ยวข้องเป็นf=0,df,...,(N-1)*dfที่df=1/tmax=1/(N*T)ความถี่การสุ่มตัวอย่าง ฮาร์มอนิกทั้งหมดของสัญญาณควรเป็นความถี่ในการสุ่มตัวอย่างหลายเท่าเพื่อหลีกเลี่ยงการแพร่กระจาย

ในตัวอย่างข้างต้นคุณจะเห็นว่าการใช้arangeแทนlinspaceทำให้สามารถหลีกเลี่ยงการแพร่กระจายเพิ่มเติมในสเปกตรัมความถี่ ยิ่งไปกว่านั้นการใช้linspaceเวอร์ชันนี้ยังนำไปสู่การหักล้างของเดือยที่ตั้งอยู่ที่ความถี่สูงกว่าที่ควรจะเป็นเล็กน้อยดังที่เห็นได้จากภาพแรกซึ่งแหลมอยู่เล็กน้อยทางด้านขวาของความถี่50และ80และ

ฉันจะสรุปว่าตัวอย่างการใช้งานควรถูกแทนที่ด้วยรหัสต่อไปนี้ (ซึ่งทำให้เข้าใจผิดน้อยกว่าในความคิดของฉัน):

import numpy as np
from scipy.fftpack import fft
# Number of sample points
N = 600
T = 1.0 / 800.0
x = T*np.arange(N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = 1/(N*T)*np.arange(N//2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()

เอาต์พุต (เข็มที่สองไม่กระจายอีกต่อไป):

ฉันคิดว่าคำตอบนี้ยังคงให้คำอธิบายเพิ่มเติมเกี่ยวกับวิธีการใช้การแปลงฟูเรียร์แบบแยกอย่างถูกต้อง เห็นได้ชัดว่าคำตอบของฉันยาวเกินไปและมีสิ่งอื่น ๆ ที่จะพูดเสมอ (@ewerlopes พูดสั้น ๆ เกี่ยวกับนามแฝงเช่นและสามารถพูดได้มากมายเกี่ยวกับการกำหนดหน้าต่าง ) ดังนั้นฉันจะหยุด ฉันคิดว่าการเข้าใจหลักการของการแปลงฟูเรียร์แบบไม่ต่อเนื่องเป็นสิ่งสำคัญมากเมื่อนำไปใช้เพราะเราทุกคนรู้ว่ามีคนจำนวนมากเพิ่มปัจจัยที่นี่และที่นั่นเมื่อนำไปใช้เพื่อให้ได้มาซึ่งสิ่งที่พวกเขาต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.