【问题标题】:Finding the time in which a specific value is reached in time-series data when peaks are found找到峰值时在时间序列数据中达到特定值的时间
【发布时间】:2021-06-30 00:39:24
【问题描述】:

我想在有噪声的时间序列数据中找到达到某个值的时刻。如果数据中没有峰值,我可以在 MATLAB 中执行以下操作。

来自here的代码

% create example data 
d=1:100;
t=d/100;
ts = timeseries(d,t);
% define threshold
thr = 55;
data = ts.data(:);
time = ts.time(:);
ind = find(data>thr,1,'first');
time(ind) %time where data>threshold

但是当有噪音时,我不确定要做什么。

在上图中绘制的时间序列数据中,我想找到达到 y 轴值 5 的时间点。数据实际上在 t>=100 秒时稳定到 5。但由于数据中存在噪声,我们看到峰值在 20 秒左右达到 5。我想知道如何检测例如 100 seconds 而不是 20 s 是正确的时间。上面发布的代码只会给出 20 秒作为答案。一世 看到一个帖子here,它解释了使用滑动窗口来查找数据何时平衡。但是,我不确定如何实现相同的。建议会很有帮助。

上图中绘制的样本数据可以在here找到

关于如何在 Python 或 MATLAB 代码中实现的建议将非常有帮助。

编辑: 我不想捕捉峰值(/噪声/过冲)发生的时间。我想找到达到平衡的时间。例如,在 20 秒左右,曲线会上升并下降到 5 以下。大约 100 秒后,曲线会平衡到稳态值 5,并且不会下降或达到峰值。

【问题讨论】:

  • 这看起来更像是过冲,而不仅仅是噪音。他们都是那样的吗? Bessel filter 可能更合适。您也许还可以做一个幼稚的“第一个间隔开始,其中值高于 Y 超过 Z 秒”的方法。
  • @lan Mercer 有时会有多个峰值(过冲)。 “值高于 Y 超过 Z 秒的第一个间隔的开始”不幸的是,我有数千条这样的时间序列曲线,我不确定我是否可以从此时起为每条曲线指定第 Z 秒每条曲线都不同。
  • 从图中你怎么知道第一次超过5是噪声而不是有效信号?当信号升至 5 以上时,您链接到的数据不会显示任何其他时间(尽管它已接近)。信号从 0 上升后是否总是会出现额外的“嘈杂”颠簸,并且它是否总是持续相同的时间?如果是这样,您可以检测到从 0 开始的快速上升并忽略接下来的 25 秒左右。
  • Z 对于每条曲线不会有所不同 - 选择一个将过冲尖峰持续时间与稳定状态分开的值。需要多长时间才能算作稳态?
  • 这实际上更多是关于您的数据和您可以对其做出的安全假设的问题。因此,stackoverflow 可能不是问这个问题的最佳场所。

标签: algorithm time-series signal-processing data-analysis convergence


【解决方案1】:

精确的数据分析是一项严肃的事业(也是我的热情所在),需要对您正在研究的系统有深入的了解。这里是 cmets,不幸的是,我怀疑你的问题根本没有一个简单的好答案——你必须考虑一下。数据分析基本上总是需要“讨论”的。

首先是您的数据和一般问题:

  1. 当您谈论噪声时,在数据分析中,这意味着统计上的随机波动。最常见的是高斯分布(有时也是其他分布,例如泊松)。高斯噪声是 a) 每个 bin 中的随机噪声和 b) 在正负方向上对称的噪声。因此,您在约 20 秒的峰值观察到的不是噪音。与随机噪声相比,它具有非常不同、非常系统和扩展的特性。这是一件必须有来历的“神器”,但我们只能在此推测。在实际应用中,研究和移除此类工件是最昂贵和最耗时的任务。

  2. 查看您的数据,随机噪声可以忽略不计。这是非常精确的数据。例如,在约 150 秒及之后,直到小数点后四位都没有可见的随机波动。

  3. 在断定这不是常识中的噪音之后,它可能至少有两件事:a)您正在研究的系统的一个特征,因此,您可以开发一个模型/公式,并且您可以“适合”的数据。 b) 测量链某处带宽有限的特性,因此,这里是高频截止。参见例如https://en.wikipedia.org/wiki/Ringing_artifacts 。不幸的是,对于 a 和 b,没有包罗万象的通用解决方案。而且您的问题描述(即使是代码和数据)也不足以提出理想的方法。

  4. 现在花了大约一个小时处理您的数据并绘制了一些图表。我相信(推测)约 10 秒的极其尖锐的特征不能是数据的“物理”属性。它简直太极端/太陡了。这里从根本上发生了一些事情。我的猜测可能是某些设备刚刚打开(之前关闭)。因此,之前的数据是没有意义的,之后还有很短的时间来稳定系统。在这种情况下实际上没有其他选择,只能完全丢弃数据,直到系统稳定在 40 秒左右。这也使您的问题变得微不足道。只需删除前 40 秒,最大值就会变得明显。

那么您可以使用哪些技术解决方案,请不要太沮丧,因为您必须自己考虑这个问题并为您的案例组装最佳解决方案。我将您的数据复制到两个 numpy 数组 xy 中,并在 python 中运行以下测试:

去除不稳定时间

这是简单的解决方案——我更喜欢它。

plt.figure()
plt.xlabel('time')
plt.ylabel('signal')
plt.plot(x, y, label="original")

y_cut = y
y_cut[:40] = 0

plt.plot(x, y_cut, label="cut 40s")
plt.legend()
plt.grid()
plt.show()

注意只有在你有点疯狂(关于数据)的情况下才能继续阅读下面的内容。

滑动窗口

您提到了“滑动窗口”,它最适合随机噪声(您没有)或周期性波动(您也没有)。滑动窗口只是对连续的 bin 进行平均,平均掉随机波动。从数学上讲,这是一个卷积。

从技术上讲,您实际上可以像这样解决您的问题(自己尝试更大的 Nwindow 值):

Nwindow=10
y_slide_10 = np.convolve(y, np.ones((Nwindow,))/Nwindow, mode='same')
Nwindow=20
y_slide_20 = np.convolve(y, np.ones((Nwindow,))/Nwindow, mode='same')
Nwindow=30
y_slide_30 = np.convolve(y, np.ones((Nwindow,))/Nwindow, mode='same')

plt.xlabel('time')
plt.ylabel('signal')
plt.plot(x,y, label="original")
plt.plot(x,y_slide_10, label="window=10")
plt.plot(x,y_slide_20, label='window=20')
plt.plot(x,y_slide_30, label='window=30')
plt.legend()
#plt.xscale('log') # useful
plt.grid()
plt.show()

因此,从技术上讲,您可以成功压制最初的“驼峰”。但不要忘记这是一个手动调整的而不是通用的解决方案......

任何滑动窗口解决方案的另一个警告:这总是会扭曲您的时间。由于您根据上升或下降信号在一个时间间隔内进行平均,因此您的卷积轨迹会在时间上前后移动(轻微但显着)。在您的特定情况下,这不是问题,因为主信号区域基本上没有时间依赖性(非常平坦)。

频域

这应该是灵丹妙药,但对于您的示例来说,它也不能很好/容易地工作。这不能更好地工作的事实是对我的主要暗示,即前 40 年代的数据最好被丢弃....(即在科学工作中)

您可以使用快速傅里叶变换在频域中检查您的数据。

import scipy.fft

y_fft = scipy.fft.rfft(y)

# original frequency domain plot
plt.plot(y_fft, label="original")
plt.xlabel('frequency')
plt.ylabel('signal')
plt.yscale('log')
plt.show()

频率结构代表数据的特征。峰值为零是约 100 秒后的稳定区域,驼峰与(快速)时间变化有关。您现在可以四处玩转并更改频谱(--> 过滤器),但我认为频谱太人工了,因此在这里不会产生很好的结果。尝试使用其他数据,您可能会印象深刻!我尝试了两件事,首先将高频区域切掉(设置为零),其次,在频域中应用滑动窗口滤波器(将峰值保留在 0,因为无法触及。尝试一下,你就知道为什么了)。

# cut high-frequency by setting to zero
y_fft_2 = np.array(y_fft)
y_fft_2[50:70] = 0

# sliding window in frequency
Nwindow = 15
Start = 10
y_fft_slide = np.array(y_fft)
y_fft_slide[Start:] = np.convolve(y_fft[Start:], np.ones((Nwindow,))/Nwindow, mode='same')

# frequency-domain plot
plt.plot(y_fft, label="original")
plt.plot(y_fft_2, label="high-frequency, filter")
plt.plot(y_fft_slide, label="frequency sliding window")
plt.xlabel('frequency')
plt.ylabel('signal')
plt.yscale('log')
plt.legend()
plt.show()

将其转换回时域:

# reverse FFT into time-domain for plotting
y_filtered = scipy.fft.irfft(y_fft_2)
y_filtered_slide = scipy.fft.irfft(y_fft_slide)

# time-domain plot
plt.plot(x[:500], y[:500], label="original")
plt.plot(x[:500], y_filtered[:500], label="high-f filtered")
plt.plot(x[:500], y_filtered_slide[:500], label="frequency sliding window")
# plt.xscale('log') # useful
plt.grid()
plt.legend()
plt.show()

产量

这些解决方案存在明显的波动,这使得它们对您的目的基本上无用。这导致我进行最后的练习,再次在“频率滑动窗口”时域上应用滑动窗口滤波器

# extra time-domain sliding window
Nwindow=90
y_fft_90 = np.convolve(y_filtered_slide, np.ones((Nwindow,))/Nwindow, mode='same')

# final time-domain plot
plt.plot(x[:500], y[:500], label="original")
plt.plot(x[:500], y_fft_90[:500], label="frequency-sliding window, slide")
# plt.xscale('log') # useful
plt.legend()
plt.show()

我对这个结果很满意,但它仍然有非常小的振荡,因此不能解决你原来的问题。

结论

多么有趣。一小时浪费了。也许它对某人有用。也许对你来说娜塔莎。请不要让我生气……

【讨论】:

  • 很好的讨论。开始时的加速具有我的回答中提到的初始偏差问题的所有专用标记,如果这是模拟数据,则可能是由于初始状态,或者(如您所假设的)测量设备的实际预热(如果是观测数据)。
【解决方案2】:

假设您的数据在data 变量中,时间索引在time 中。那么

import numpy as np

threshold = 0.025
stable_index = np.where(np.abs(data[-1] - data) > threshold)[0][-1] + 1
print('Stabilizes after', time[stable_index], 'sec')

Stabilizes after 96.6 sec

这里data[-1] - datadata 的最后一个值与所有数据值之间的差异。这里假设data的最后一个值代表平衡点。

np.where( * > threshold )[0] 是所有data 值的索引,它们大于 大于阈值,仍未稳定。我们只取最后一个索引。下一个是时间序列被认为是稳定的,因此是+ 1

【讨论】:

    【解决方案3】:

    如果您正在处理最终单调收敛到某个固定值的确定性数据,那么问题就很简单了。您的最后一次观察应该是最接近极限的,因此您可以定义一个相对于最后一个数据点的可接受的容差阈值,并从后到前扫描您的数据以找出超出阈值的位置。

    在图片中添加随机噪声后,情况会变得更加糟糕,尤其是在存在序列相关性的情况下。这个问题在仿真建模中很常见(参见下面的(*)),并且被称为initial bias 的问题。它首先由Conway in 1963 确定,从那时起一直是一个活跃的研究领域,对于如何处理它没有普遍接受的明确答案。与确定性情况一样,最广泛接受的答案从数据集的右侧开始解决问题,因为这是数据最有可能处于稳定状态的位置。基于这种方法的技术使用数据集的末端来建立某种统计标准或基线,以衡量随着观察值通过向数据集的前面移动而开始出现显着不同的位置。序列相关的存在使这变得非常复杂。

    如果时间序列处于稳定状态,在covariance stationary 的意义上,那么数据的简单平均值是对其期望值的无偏估计,但估计平均值的标准误差在很大程度上取决于序列相关性.正确的标准误差平方不再是 s2/n,而是 (s2/n)*W,其中 W 是自相关值的适当加权和.在 1990 年代开发了一种称为 MSER 的方法,通过尝试确定标准误差最小化的位置来避免尝试正确估计 W 的问题。给定足够大的样本量,它将 W 视为事实上的常数,因此,如果您考虑两个标准误差估计值的比率,W 会被抵消,并且最小值出现在 s2/n 最小化的位置。 MSER 进行如下:

    • 从头开始,计算一半数据集的s2,建立基线。
    • 现在使用Welford's online algorithm 等有效技术一次更新一个观测值的 s2 估计值,计算 s2/n 其中 n 是到目前为止的观察结果。跟踪哪个 n 值产生最小的 s2/n。起泡、冲洗、重复。
    • 从后到前遍历整个数据集后,产生最小 s2/n 的 n 是从数据集末尾无法检测到的观察数由于受到起始条件的影响。

    理由 - 具有足够大的基线(数据的一半),只要时间序列保持稳定状态,s2/n 应该是相对稳定的。由于 n 是单调递增的,因此 s2/n 应继续减少,但受限于其作为估计值的可变性限制。但是,一旦您开始获取非稳定状态的观测值,均值和方差的漂移将使 s2/n 的分子膨胀。因此,最小值对应于没有非平稳性迹象的最后一次观察。更多详细信息可以在此proceedings paper 中找到。一个Ruby implementation is available on BitBucket

    您的数据的变化量非常小,以至于 MSER 得出结论认为它仍在趋于稳定状态。因此,我建议采用第一段中概述的确定性方法。如果您将来有嘈杂的数据,我绝对建议您试一试 MSER。


    (*) - 简而言之,仿真模型是一个计算机程序,因此必须将其状态设置为一组初始值。我们一般不知道系统状态从长远来看会是什么样子,因此我们将其初始化为任意但方便的一组值,然后让系统“预热”。问题是模拟的初始结果不是典型的稳态行为,因此在分析中包含该数据会使它们产生偏差。解决的办法是去掉数据中的有偏差的部分,但是应该去掉多少呢?

    【讨论】:

      猜你喜欢
      • 2022-11-02
      • 2014-11-28
      • 1970-01-01
      • 2016-07-08
      • 2019-07-31
      • 2012-08-28
      • 1970-01-01
      • 2021-08-29
      • 1970-01-01
      相关资源
      最近更新 更多