金融时间序列:python Matplotlib" specgram" y轴显示周期而不是频率

时间:2014-07-16 08:02:16

标签: matplotlib time-series data-visualization period spectrum

python Matplotlib显示频率(y轴)与时间(x轴)的热图的“specgram”显示对时间序列分析很有用,但我希望y轴以Period表示( = 1 /频率),而不是频率。我仍然在问是否有人有完整的工作解决方案来实现这一目标?

紧随其后的python代码使用“specgram”生成作者的原始图,并且(当前已注释掉)与使用“mlab.specgram”提供的建议解决方案进行比较。这个建议的解决方案成功,从频率到周期= 1 /频率的简单转换,但没有为作者示例生成可行的图。

from __future__ import division
from datetime import datetime
import numpy as np
from pandas import DataFrame, Series
import pandas.io.data as web
import pandas as pd
from pylab import plot,show,subplot,specgram
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

################################################
# obtain data:
ticker = "SPY"
source = "google"
start_date = datetime(1999,1,1)
end_date = datetime(2012,1,1)
qt = web.DataReader(ticker, source, start_date, end_date)
qtC = qt.Close

################################################
data = qtC
fs = 1          # 1 sample / day
nfft = 128

# display the time-series data
fig = plt.figure()
ax1 = fig.add_subplot(311)
ax1.plot(range(len(data)),data)

#----------------

# Original version
##################

# specgram (NOT mlab.specgram) --> gives direct plot, but in Frequency space (want plot in Period, not freq).
ax2 = fig.add_subplot(212)
spec, freq, t = specgram(data, NFFT=nfft, Fs=fs, noverlap=0)

 #----------------
"""
# StackOverflow version (with minor changes to axis titles)
########################

# calcuate the spectrogram
spec, freq, t = mlab.specgram(data, NFFT=nfft, Fs=fs, noverlap=0)

# calculate the bin limits in time (x dir)
# note that there are n+1 fence posts
dt = t[1] - t[0]
t_edge = np.empty(len(t) + 1)
t_edge[:-1] = t - dt / 2.
# however, due to the way the spectrogram is calculates, the first and last bins 
# a bit different:
t_edge[0] = 0
t_edge[-1] = t_edge[0] + len(data) / fs

# calculate the frequency bin limits:
df = freq[1] - freq[0]
freq_edge = np.empty(len(freq) + 1)
freq_edge[:-1] = freq - df / 2.
freq_edge[-1] = freq_edge[-2] + df

# calculate the period bin limits, omit the zero frequency bin
p_edge = 1. / freq_edge[1:]

# we'll plot both
ax2 = fig.add_subplot(312)
ax2.pcolormesh(t_edge, freq_edge, spec)
ax2.set_ylim(0, fs/2)
ax2.set_ylabel('freq.[day^-1]')

ax3 = fig.add_subplot(313)
# note that the period has to be inverted both in the vector and the spectrum,
# as pcolormesh wants to have a positive difference between samples 
ax3.pcolormesh(t_edge, p_edge[::-1], spec[:0:-1])
#ax3.set_ylim(0, 100/fs)
ax3.set_ylim(0, nfft)
ax3.set_xlabel('t [days]')
ax3.set_ylabel('period [days]')
"""

1 个答案:

答案 0 :(得分:0)

如果您只是询问如何以不同方式显示频谱图,那么它实际上相当简单。

需要注意的一点是,有两个名为specgram的函数:matplotlib.pyplot.specgrammatplotlib.mlab.specgram。这两者之间的区别在于,前者绘制了一个谱图,后者只计算一个(这就是我们想要的)。

唯一有点棘手的是计算颜色网格矩形边缘位置。我们从specgram

获得以下内容
  • t:及时的中心点
  • freq:垃圾箱的频率中心

对于时间维度,可以很容易地计算中心的箱限:

  • t_edge[n] = t[0] + (n - .5) * dt,其中dt是两个连续分档的时差

频率也同样简单:

  • f_edge[n] = freq[0] + (n - .5) * df

但我们想要使用句号而不是频率。这使得第一个bin无法使用,我们将不得不抛弃DC组件。

一些代码:

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

# create some data: (fs = sampling frequency)
fs = 2000.
ts = np.arange(10000) / fs
sig = np.sin(500 * np.pi * ts)
sig[5000:8000] += np.sin(200 * np.pi * (ts[5000:8000] + 0.0005 * np.random.random(3000)))

# calcuate the spectrogram
spec, freq, t = mlab.specgram(sig, Fs=fs)

# calculate the bin limits in time (x dir)
# note that there are n+1 fence posts
dt = t[1] - t[0]
t_edge = np.empty(len(t) + 1)
t_edge[:-1] = t - dt / 2.
# however, due to the way the spectrogram is calculates, the first and last bins 
# a bit different:
t_edge[0] = 0
t_edge[-1] = t_edge[0] + len(sig) / fs

# calculate the frequency bin limits:
df = freq[1] - freq[0]
freq_edge = np.empty(len(freq) + 1)
freq_edge[:-1] = freq - df / 2.
freq_edge[-1] = freq_edge[-2] + df

# calculate the period bin limits, omit the zero frequency bin
p_edge = 1. / freq_edge[1:]

# we'll plot both
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax1.pcolormesh(t_edge, freq_edge, spec)
ax1.set_ylim(0, fs/2)
ax1.set_ylabel('frequency [Hz]')

ax2 = fig.add_subplot(212)
# note that the period has to be inverted both in the vector and the spectrum,
# as pcolormesh wants to have a positive difference between samples 
ax2.pcolormesh(t_edge, p_edge[::-1], spec[:0:-1])
ax2.set_ylim(0, 100/fs)
ax2.set_xlabel('t [s]')
ax2.set_ylabel('period [s]')

这给出了:

enter image description here