ฉันเขียนคำตอบเพิ่มเติมนี้เพื่ออธิบายที่มาของการแพร่กระจายของ 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)
) แต่มีความแตกต่างเล็กน้อย:
- ตัวอย่าง scipy.fftpack ดั้งเดิม
- ตัวอย่าง scipy.fftpack ดั้งเดิมที่มีจำนวนคาบสัญญาณจำนวนเต็ม (
tmax=1.0
แทนที่จะ0.75
หลีกเลี่ยงการตัดการแพร่กระจาย)
- ตัวอย่าง scipy.fftpack ดั้งเดิมที่มีจำนวนช่วงเวลาสัญญาณจำนวนเต็มและวันที่และความถี่ถูกนำมาจากทฤษฎี FFT
รหัส:
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
N = 600
tmax = 3/4
T = tmax / N
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)
tmax = 1
T = tmax / N
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)
tmax = 1
T = tmax / N
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()
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
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 พูดสั้น ๆ เกี่ยวกับนามแฝงเช่นและสามารถพูดได้มากมายเกี่ยวกับการกำหนดหน้าต่าง ) ดังนั้นฉันจะหยุด ฉันคิดว่าการเข้าใจหลักการของการแปลงฟูเรียร์แบบไม่ต่อเนื่องเป็นสิ่งสำคัญมากเมื่อนำไปใช้เพราะเราทุกคนรู้ว่ามีคนจำนวนมากเพิ่มปัจจัยที่นี่และที่นั่นเมื่อนำไปใช้เพื่อให้ได้มาซึ่งสิ่งที่พวกเขาต้องการ