python中的FFT无法绘制正确的频率

时间:2014-03-19 01:26:13

标签: python fft

import datetime
import shlex

import numpy as np
from scipy.fftpack import rfft, irfft, fftfreq

strWeatherFile = "data/weather.dat"

signal = []

with open(strWeatherFile, 'rt') as f:
    for line in f:
        arrDat = shlex.split(line)
        d = float(arrDat[3])
        if d < -30:  # if it's dummy data
            d = signal[len(signal) - 1]
        signal.append(d)

size = len(signal)

time   = np.linspace(1,size,size)

f_signal = rfft(signal)


import pylab as plt
plt.subplot(221)
plt.plot(time,signal)
plt.subplot(222)
plt.plot(time,f_signal)
plt.xlim(0,size)

plt.show()

这是天气数据,因此它应该显示频率为365的峰值。但结果并不像预期的那样。 代码有什么问题吗?

数据来自以下链接: http://academic.udayton.edu/kissock/http/Weather/gsod95-current/CALOSANG.txt

3 个答案:

答案 0 :(得分:2)

你的FFT应该根据频率而不是时间来绘制。

import shlex
import pylab as plt
import numpy as np
from scipy.fftpack import rfft, rfftfreq

strWeatherFile = "data/weather.dat"

signal = []

with open(strWeatherFile, 'rt') as f:
    for line in f:
        arrDat = shlex.split(line)
        d = float(arrDat[3])
        if d < -30:  # if it's dummy data
            d = signal[len(signal) - 1]
        signal.append(d)

size = len(signal)

time = np.arange(1,size+1)

f_signal = rfft(signal)
freq = rfftfreq(size, d=1.)


plt.ion()
plt.close("all")
plt.figure()
plt.plot(time,signal)
plt.figure()
plt.plot(freq,abs(f_signal))
plt.show()

你不会期望看到365的飙升,因为这是时间价值。对于周期为365的信号的相应频率将是1/365或.00274

答案 1 :(得分:2)

数据文件包含14个错误样本,值为-99。我使用最近的好样本用线性插值替换坏样本。文件中的样本总数为7006.该数据用于未知的每日天气参数。

正如Ben所说,频率单位是1 /天而不是天,所以你不会看到365频率单位的峰值(假设数据是周期性的365天)。

下图显示了输入FFT的数据。使用线性插值程序删除了不良样本。右端的零值由FFT自动添加,以便将输入样本的数量填充到下一个更高的2的幂。

Time Domain graph - sooeet.com FFT calculator

下图显示了输入到FFT的数据,并删除了DC偏移(62.3127)。

Time Domain graph, DC offset removed - sooeet.com FFT calculator

下图显示了满量程的FFT输出。 FFT输入包括DC偏移。

FFT spectrum graph - sooeet.com FFT calculator

下图放大了FFT输出的低频端。在图的左侧附近可以看到峰值。该峰值对应于您要查找的频率。 FFT输入包括DC偏移。

FFT spectrum graph zoomed - sooeet.com FFT calculator

下图显示了感兴趣的频率峰值。峰值频率单位为0.0026855(22/8192),对应的时间值为372天(1 / 0.0026855)。 FFT输入包括DC偏移。

FFT spectrum peak - sooeet.com FFT calculator

下图显示了使用Blackman-Harris 92 dB高分辨率窗口输入数据后的FFT。感兴趣的频率峰值比周围背景高18 dB。在这种情况下,窗口显着减少了光谱泄漏。窗口化后,峰值保持在0.0026855(22/8192)频率单位,这对应于372天的时间值(1 / 0.0026855)。 FFT输入包括DC偏移。

FFT window function applied - spectrum peak - sooeet.com FFT calculator

下图显示了从输入数据中删除DC偏移(62.3127)后的FFT,并且没有应用于输入数据的窗口。

FFT spectrum, DC offset removed - sooeet.com FFT calculator

下图显示了从输入数据中移除DC偏移(62.3127)并应用Blackman-Harris 92 dB高分辨率窗口后的FFT。

FFT spectrum, DC offset removed, window function applied - sooeet.com FFT calculator

使用Sooeet FFT calculator

完成FFT和图表

答案 2 :(得分:0)

我不一定在这里有一个好的答案,但我想让你的代码很容易为其他人复制,所以他们不必动手下载和文件:

import datetime
import urllib2
import numpy as np    
from scipy.fftpack import rfft, irfft, fftfreq    

url = 'http://academic.udayton.edu/kissock/http/Weather/gsod95-current/CALOSANG.txt'
response = urllib2.urlopen(url)
html = response.read()
rows = [[float(c) if '.' in c else int(c)
         for c in row.split()] 
         for row in html.splitlines()]

signal = []
for line in rows:
    d = float(line[3])
    if d < -30:  # if it's dummy data
        d = signal[len(signal) - 1]
    signal.append(d)

size = len(signal)
time   = np.linspace(1,size,size)
f_signal = rfft(signal)
import pylab as plt
plt.subplot(221)
plt.plot(time,signal)
plt.subplot(222)
plt.plot(time,f_signal)
plt.xlim(0,size)
plt.show()