scipy.signal.spectrogram() - 如何处理时间序列数据中的间隙

信息处理 Python 频谱图 stft 时频 scipy
2022-02-14 23:50:00

查看变星的大小 - 数据集来自这里:

https://dogwood.physics.mcmaster.ca/Cepheid/URL/MW/BD-10d4669.html

幅度图:

import pandas as pd
from matplotlib import pyplot as plt
from scipy import signal
import numpy as np
plt.rcParams["figure.figsize"] = (16, 9)

df = pd.read_table('BD-10d4669.p.1',
                   sep='   ',
                   engine='python',
                   names=['time', 'mag'])
df.plot(x='time', y='mag');

幅度图

很明显,时间线上存在差距。

但是我怎么知道signal.spectrogram()差距存在呢?在我看来,该函数假设时间序列永远不会有任何数据间隙。

这是未考虑间隙的朴素频谱图:

sig = np.array(df['mag'].tolist())
nseg = 20
f, t, Sxx = signal.spectrogram(sig, 1, nperseg=nseg, noverlap=nseg-1)
plt.pcolormesh(t, f, np.log10(Sxx), shading='auto');

频谱图

我希望频谱图的时间线至少部分匹配情节的时间线。

我知道由于存储桶大小(nperseg)可能存在问题,我也不知道如何处理。

3个回答

嗯,不仅仅是差距;您的数据也是非均匀采样的。

用于index_col将时间列用作数据框的索引:

df = pd.read_table('BD-10d4669.p.1',
                   sep='   ',
                   engine='python',
                   names=['time', 'mag'],
                   index_col=0)
df.plot(y='mag');
             mag
time            
38964.297  10.92
38965.349  10.86
38966.364  10.52
38968.293  11.38
38972.293  11.09
         ...
48098.300  11.26
48098.399  11.32
48099.308  11.38
48100.325  10.57
48179.272  10.97

[221 rows x 1 columns]

然后你可以看到样本之间的间距并不完全相同:

np.diff(df.index)[0:2]
Out[26]: array([1.052, 1.015])

signal.spectrogram仅对均匀采样的数据进行操作,并且您将使用df['mag'].tolist().

您可以使用signal.lombscargle以下示例查看非均匀采样数据的频谱:

.note 文件说:

Columns: 1) HJD-2,400,000
     2) Bpg

P0 = 4.84125 days
P1 = 3.38530 days

HJD 是日心儒略日期,因此采样时间以天为单位,我们想要测量几天的周期。

P0 = 4.84125  # days / cycle
P1 = 3.38530  # days / cycle

f0 = 1/P0  # cycles / day
f1 = 1/P1  # cycles / day

# But lombscargle uses angular frequency, so

w0 = f0 * 2*np.pi  # = 1.29784 radians / day
w1 = f1 * 2*np.pi  # = 1.85602 radians / day

w = np.linspace(0.01, 3, 100000)  # radians / day
pgram = signal.lombscargle(df.index, df['mag'], w, normalize=True)
plt.subplot(2, 1, 1)
plt.plot(df.index, df['mag'], 'b+')
plt.subplot(2, 1, 2)
plt.plot(w, pgram)
plt.show()

这没有显示角频率 ω0 = 1.29784 rad/day 的预期峰值,所以我很困惑:

数据的 Lomb-Scargle 周期图

我不知道为什么单位不工作。在示例中,时间变量以秒为单位,周期图的峰值为 1,表示 1 rad/sec。

因此,如果这里的时间变量以天为单位,我希望该w变量以弧度/天为单位。

无论如何,在弄清楚这些单位为什么不起作用(可能是我的错误)之后,您可以尝试将原始数据插入到缺失日期的具有 NaN 值的规则间隔时间戳中:

df.index = pd.to_datetime(df.index + 2_400_000, unit='D', origin='julian')
df.resample('1D')

但是数据太少了,它们之间的差距如此之大,以至于我认为您无法从中获得任何有意义的频谱图。

OP的时间向量是

我会做什么:

  1. 将其视为分段线性,即忽略时间向量不是均匀间隔的,除了跳跃。这应该可以合理地工作 - 但如果需要更高的准确性,有一个相关的查询
  2. 定义分隔每个“段”的“跳跃”阈值
  3. 从每一侧填充每个这样的段——也就是说,让左段的垫在中途与右段的垫相遇——我推荐reflect相关帖子
  4. 定义“不连续性”或“大跳跃”阈值,并reflect-zero填充它 - 即用 填充它的一部分,而用 填充reflect其他部分zero那将是上图中倒数第二个跳跃。这个想法是我们不想过多地估算。

这些都是“启发式”,但对于这个工程问题没有“正确”的答案。这是关于处理丢失的信息。

1 扩展

我允许的最大步长比是每个分段线性段内最大步长的 x0.5/x2。OP 的时间实例有时变化更大,这使近似值无效;以下是第三部分:

有了这么多的可变性,我会完全选择另一种方法,但如果我们坚持,它就会变成一个插值问题:上采样或下采样,直到时间向量足够均匀。

我也会使用一个窄窗口,它需要更少的点来符合给定 STFT 快照的一致性。然后它可以绘制为单个 2D 表示,但每个时间步可能编码不同的物理时间尺度,因此在解释时应谨慎。

我遇到了类似的问题,因此最近有一个关于Running window design for不规则或非均匀时间序列的问题。一种可能性是投资于不均匀或非均匀采样的频谱图,使用“最小二乘频谱分析”​​工具,例如移动窗口或运行框架方式的 Lomb-Scargle 周期图(我正在 Matlab 中重新开发)。然而,我仍然不知道一个规范的回应:如何处理任意间隙,如何在不均匀的时间范围内平衡权力。