我的功率谱是可信的吗? lomb-scargle和fft(scipy.signal和numpy.fft)之间的比较

时间:2013-12-18 12:32:56

标签: numpy scipy signals period

有人可以指出为什么我会得到非常不同的结果吗?
有许多峰值不应出现。事实上,应该只有一个峰值 我是一个python新手,欢迎所有关于我的代码的评论。

测试数据在这里。enter link description here
您可以直接wget https://clbin.com/YJkwr
它的第一列是一系列光子的到达时间。光源随机发射光子。 总时间为55736s,有67398个光子。 我试图检测某种光强度的周期性。
我们可以将时间和光强度与每个时间段中的光子数成比例。

我尝试了numpy.fft和scipy.signal的lomb-scargle来制作它的功率谱,但结果却截然不同。

FFT

 import pylab as pl
 import numpy as np

 timepoints=np.loadtxt('timesequence',usecols=(0,),unpack=True,delimiter=",")
 binshu=50000
 interd=54425./binshu  
 t=np.histogram(timepoints,bins=binshu)[0]
 sp = np.fft.fft(t)
 freq = np.fft.fftfreq(len(t),d=interd)  
 freqnum = np.fft.fftfreq(len(t),d=interd).argsort()  
 pl.xlabel("frequency(Hz)")
 pl.plot(freq[freqnum],np.abs(sp)[freqnum])

enter image description here

博士伦-scargle

 timepoints=np.loadtxt('timesequence',usecols=(0,),unpack=True,delimiter=",")
 binshu=50000
 intensity=np.histogram(timepoints,bins=binshu)[0].astype('float64') 
 middletime=np.histogram(timepoints,bins=binshu)[1][1:binshu+1]-np.histogram(timepoints,bins=binshu)[1][3]*0.5
 freq1=1./(timepoints.max()-timepoints.min())
 freq2=freq1*len(timepoints)
 freqs=np.linspace(freq1,freq2,1000)
 pm1=spectral.lombscargle(middletime,intensity,freqs)

 pl.xlabel("frequency(Hz)")
 pl.plot(freqs,pm1)
 pl.xlim(0.5*freq1,2*freq2)
 pl.ylim(0,250)
 pl.show()

enter image description here

**********
谢谢你,沃伦,我很感激。谢谢你的详细回复。

你是对的,有一个积分时间,这里是1.7s左右 有很多单次曝光。每次曝光费用为1.7s 在一次曝光中,我们无法准确地告知其到达时间。

如果时间序列如下:
0 1.7 3.4 8.5 8.5

最后两个光子的积分时间是1.7s,而不是(8.5-3.4)s。因此我将修改部分代码。

然而我的问题仍然存在。您可以调整几个参数以在某种程度上获得lomb-scargle峰值中的0.024Hz峰值。并使用它来指导fft中的参数。

如果您不知道号码0.024,也许您可​​以使用不同的参数来获得不同的最高峰值?

如何确保每次我们能够正确num_ls_freqs?你可以看看我们是否选择了不同的num_ls_freqs,即最高的峰值变化。

如果我有很多计时系列,每次我必须指定不同的参数?以及如何获得它们?

2 个答案:

答案 0 :(得分:4)

看来50000箱还不够。如果您更改binshu,您会看到尖峰的位置发生变化,而不仅仅是一点点。

在深入了解FFT或Lomb-Scargle之前,我发现首先调查原始数据很有用。以下是文件的开头和结尾:

0,1,3.77903
0,1,4.96859
0,1,1.69098
1.74101,1,4.87652
1.74101,1,5.15564
1.74101,1,2.73634
3.48202,1,3.18583
3.48202,1,4.0806
5.2229,1,1.86738
6.96394,1,7.27398
6.96394,1,3.59345
8.70496,1,4.13443
8.70496,1,2.97584
...
55731.7,1,5.74469
55731.7,1,8.24042
55733.5,1,4.43419
55733.5,1,5.02874
55735.2,1,3.94129
55735.2,1,3.54618
55736.9,1,3.99042
55736.9,1,5.6754
55736.9,1,7.22691

相同的到达时间的集群。 (这是光子探测器的原始输出,还是以某种方式对此文件进行了预处理?)

因此,第一步是将这些聚类一次合并为一个数字,因此数据看起来像(时间点,计数),例如:

0, 3
1.74101, 3
3.48202, 2
5.2229, 1
etc

以下将完成计算:

times, inv = np.unique(timepoints, return_inverse=True)
counts = np.bincount(inv)

现在times是一组增加的唯一时间值,counts是当时检测到的光子数。 (请注意,我猜这是对数据的正确解释!)

让我们看一下采样是否接近均匀。到达时间的数组是

dt = np.diff(times)

如果采样完全一致,dt中的所有值都是相同的。我们可以使用上面timepoints上使用的相同模式来查找dt中的唯一值(及其出现频率):

dts, inv = np.unique(dt, return_inverse=True)
dt_counts = np.bincount(inv)

例如,如果我们打印它,dts就是这样:

[  1.7       1.7       1.7       1.7       1.74      1.74      1.74      1.74
   1.74      1.74      1.74088   1.741     1.741     1.741     1.741
   1.741     1.741     1.741     1.74101   1.74102   1.74104   1.7411
   1.7411    1.7411    1.7411    1.7411    1.742     1.742     1.742
   1.746     1.75      1.8       1.8       1.8       1.8       3.4       3.4
   3.4       3.4       3.48      3.48      3.48      3.48      3.48      3.482
   3.482     3.482     3.482     3.482     3.4821    3.483     3.483     3.49
   3.49      3.49      3.49      3.49      3.5       3.5       5.2       5.2
   5.2       5.2       5.22      5.22      5.22      5.22      5.22      5.223
   5.223     5.223     5.223     5.223     5.223     5.23      5.23      5.23
   5.3       5.3       5.3       5.3       6.9       6.9       6.9       6.9
   6.96      6.96      6.964     6.964     6.9641    7.        8.7       8.7
   8.7       8.7       8.7       8.71     10.4      10.5      12.2    ]

(明显的重复值实际上是不同的。未显示数字的完整精度。)如果采样完全一致,则该列表中只有一个数字。相反,我们看到数字集群,没有明显的主导价值。 (有一个1.74倍数的优势 - 这是探测器的一个特征吗?)

根据这一观察,我们将从Lomb-Scargle开始。下面的脚本包括用于计算和绘制(timescounts)数据的Lomb-Scargle周期图的代码。给lombscargle的频率从1/trange变化到trange,其中1/dt.min()是数据的完整时间范围。频率数为16000(脚本中为num_ls_freqs)。一些反复试验表明,这大约是解决频谱所需的最小数量。不到这一点,峰值开始四处移动。不仅如此,频谱几乎没有变化。计算表明,在0.0242 Hz处存在峰值。其他峰值是此频率的谐波。

Lomb-Scargle spectrum plot

现在我们估计了基频,我们可以用它来指导在timepoints的直方图中选择bin大小以用于FFT计算。我们将使用一个bin大小,导致基频过采样8倍。(在下面的脚本中,这是m。)也就是说,我们

m = 8
nbins = int(m * ls_peak_freq * trange + 0.5)
hist, bin_edges = np.histogram(timepoints, bins=nbins, density=True)

timepoints是从文件中读取的原始时间;它包含许多重复的时间值,如上所述。)

然后我们将FFT应用于hist。 (我们实际上会使用numpy.fft.rfft。)以下是使用FFT计算的频谱图:

enter image description here

正如预期的那样,峰值为0.0242 Hz。

这是脚本:

import numpy as np
from scipy.signal import lombscargle
import matplotlib.pyplot as plt


timepoints = np.loadtxt('timesequence', usecols=(0,), delimiter=",")

# Coalesce the repeated times into the `times` and `counts` arrays.
times, inv = np.unique(timepoints, return_inverse=True)
counts = np.bincount(inv)

# Check the sample spacing--is the sampling uniform?
dt = np.diff(times)
dts, inv = np.unique(dt, return_inverse=True)
dt_counts = np.bincount(inv)
print dts
# Inspection of `dts` shows that the smallest dt is about 1.7, and there
# are many near multiples of 1.74,  but the sampling is not uniform,
# so we'll analyze the spectrum using lombscargle.


# First remove the mean from the data.  This is not essential; it just
# removes the large value at the 0 frequency that we don't care about.
counts0 = counts - counts.mean()

# Minimum interarrival time.
dt_min = dt.min()

# Total time span.
trange = times[-1] - times[0]

# --- Lomb-Scargle calculation ---
num_ls_freqs = 16000
ls_min_freq = 1.0 / trange
ls_max_freq = 1.0 / dt_min
freqs = np.linspace(ls_min_freq, ls_max_freq, num_ls_freqs)
ls_pgram = lombscargle(times, counts0, 2*np.pi*freqs)

ls_peak_k = ls_pgram.argmax()
ls_peak_freq = freqs[ls_peak_k]
print "ls_peak_freq  =", ls_peak_freq


# --- FFT calculation of the binned data ---
# Assume the Lomb-Scargle calculation gave a good estimate
# of the fundamental frequency.  Use a bin size for the histogram
# of timepoints that oversamples that period by m.
m = 8
nbins = int(m * ls_peak_freq * trange + 0.5)
hist, bin_edges = np.histogram(timepoints, bins=nbins, density=True)
delta = bin_edges[1] - bin_edges[0]

fft_coeffs = np.fft.rfft(hist - hist.mean())
fft_freqs = np.fft.fftfreq(hist.size, d=delta)[:fft_coeffs.size]
# Hack to handle the case when hist.size is even.  `fftfreq` puts
# -nyquist where we want +nyquist.
fft_freqs[-1] = abs(fft_freqs[-1])

fft_peak_k = np.abs(fft_coeffs).argmax()
fft_peak_freq = fft_freqs[fft_peak_k]
print "fft_peak_freq =", fft_peak_freq


# --- Lomb-Scargle plot ---
plt.figure(1)
plt.clf()
plt.plot(freqs, ls_pgram)
plt.title('Spectrum computed by Lomb-Scargle')
plt.annotate("%6.4f Hz" % ls_peak_freq, 
             xy=(ls_peak_freq, ls_pgram[ls_peak_k]),
             xytext=(10, -10), textcoords='offset points')
plt.xlabel('Frequency (Hz)')
plt.grid(True)


# --- FFT plot ---
plt.figure(2)
plt.clf()
plt.plot(fft_freqs, np.abs(fft_coeffs)**2)
plt.annotate("%6.4f Hz" % fft_peak_freq,
             xy=(fft_peak_freq, np.abs(fft_coeffs[fft_peak_k])**2),
             xytext=(10, -10), textcoords='offset points')
plt.title("Spectrum computed by FFT")
plt.grid(True)

plt.show()

答案 1 :(得分:0)

功率谱是傅立叶变换平方的绝对值。如果最重要的是你摆脱了FFT中的DC值,只绘制结果的正半部分,并从np.histogram的输出计算bin宽度,而不是使用一些硬编码的幻数,这就是你得到:

timepoints=np.loadtxt('timesequence',usecols=(0,),unpack=True,delimiter=",")
binshu=50000
t, _=np.histogram(timepoints,bins=binshu)
interd = _[1] - _[0]
sp = np.fft.fft(t)
freq = np.fft.fftfreq(len(t),d=interd)
n = len(freq)
pl.xlabel("frequency(Hz)")
pl.plot(freq[1:n//2],(sp*sp.conj())[1:n//2])

enter image description here

这看起来很像你的lombscargle周期图。大峰的位置不匹配,在基于FFT的频谱中约为0.32,而在你的lombscargle输出中更像是0.4x。不确定那里发生了什么,但我不完全理解你的频率计算,特别是在lombscargle案例中......