首页 > 解决方案 > 在图形和表格中识别时间序列何时通过阈值

问题描述

我有一个时间序列,将“实际”与“预测”通道值进行比较。(这是针对机器的预测性维护应用)

由于我在机器全新时训练模型,因此我对这些渠道之间的比较超过特定阈值的时间感兴趣。

理想情况下,我希望能够在图表和表格中识别这些事件。

下面是一些示例数据行以及我试图对图表执行的操作。

非常感谢您的帮助!

数据格式:

Index          Time         Actual  Predicted   Score
  1      6/10/2020 0:00      134       124       8.1%
  2      6/10/2020 1:00      135       127       6.3%
  3      6/10/2020 2:00      129       125       3.2%
  4      6/10/2020 3:00      134       130       3.1%
  5      6/10/2020 4:00      131       127       3.1%
  6      6/10/2020 5:00      134       127       5.5%
  7      6/10/2020 6:00      129       125       3.2%
  8      6/10/2020 7:00      130       133      -2.3%
  9      6/10/2020 8:00      133       138      -3.6%
 10      6/10/2020 9:00      125       129      -3.1%
 11      6/10/2020 10:00     125       131      -4.6%
 12      6/10/2020 11:00     126       136      -7.4%
 13      6/10/2020 12:00     128       136      -5.9%
 14      6/10/2020 13:00     133       138      -3.6%
 15      6/10/2020 14:00     134       130       3.1%
 16      6/10/2020 15:00     129       125       3.2%
 17      6/10/2020 16:00     129       125       3.2%

因此,在这种特殊情况下,我希望有一个表格,我可以在其中检测到它低于特定阈值并回升到它之上的时间。例如-4.0。

ID    EventType        EventTime
11       Red        6/10/2020 10:00
14      Green       6/10/2020 13:00

然后,理想情况下,我还希望能够在图表上突出显示此类事件。

图形

标签: pythonpython-3.xmatplotlibtime-series

解决方案


变化的计算略有不同。基本步骤是:

  1. 将分数转换为float(删除百分比)
  2. 标记低于 -4 截止值的所有分数(使用 1,否则为 0)。
  3. diff()重新标记的分数进行评分
  4. diff()根据您要分配的颜色替换这些值。

我将这些步骤分成不同的列,但如果您不想将垃圾添加到您的df

df['Score'] = df['Score'].str.replace('%','').astype(float)
df['Below_Cutoff'] = np.where(df['Score'] <= -4, 1, 0)
df["Changes"] = df['Below_Cutoff'].diff()
df['Change_Colors'] = df['Changes'].map({1:'Red', -1:'Green', 0:np.nan})

结果:

                 Time  Actual  Predicted  ...  Below_Cutoff  Changes  Change_Colors
1  2020-06-10 00:00:00     134        124  ...             0      NaN            NaN
2  2020-06-10 01:00:00     135        127  ...             0      0.0            NaN
3  2020-06-10 02:00:00     129        125  ...             0      0.0            NaN
4  2020-06-10 03:00:00     134        130  ...             0      0.0            NaN
5  2020-06-10 04:00:00     131        127  ...             0      0.0            NaN
6  2020-06-10 05:00:00     134        127  ...             0      0.0            NaN
7  2020-06-10 06:00:00     129        125  ...             0      0.0            NaN
8  2020-06-10 07:00:00     130        133  ...             0      0.0            NaN
9  2020-06-10 08:00:00     133        138  ...             0      0.0            NaN
10 2020-06-10 09:00:00     125        129  ...             0      0.0            NaN
11 2020-06-10 10:00:00     125        131  ...             1      1.0            Red
12 2020-06-10 11:00:00     126        136  ...             1      0.0            NaN
13 2020-06-10 12:00:00     128        136  ...             1      0.0            NaN
14 2020-06-10 13:00:00     133        138  ...             0     -1.0          Green
15 2020-06-10 14:00:00     134        130  ...             0      0.0            NaN
16 2020-06-10 15:00:00     129        125  ...             0      0.0            NaN
17 2020-06-10 16:00:00     129        125  ...             0      0.0            NaN

请注意,在上面,第一个条目diff()始终是 NaN;如果您只想检测阈值交叉,我认为这不是问题。但是如果第一个值低于 -4 并且您希望那里有一个红点,那么这将失败。

所以要得到一个只有截止的表,你可以这样做:

only_changes = df[~pd.isna(df['Change_Colors'])]

要进行绘图,您可以执行以下操作(这类似于 Andrea 的答案,但特定于我的示例):

fig, ax = plt.subplots(figsize=(7,3.5))

ax.plot(df['Time'], df['Score'])
ax.scatter(only_changes['Time'], only_changes['Score'], c=only_changes['Change_Colors'])
ax.xaxis.set_major_locator(mdates.HourLocator([0,4,8,12,16,20,24]))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
ax.hlines(-4, min(df['Time']), max(df['Time']), colors = 'r')

在此处输入图像描述


标记实际的交叉口

此外,如果您想插值以估计实际阈值交叉的标签,您可以创建更高频率的时间戳来插值:

minutes = pd.date_range(df['Time'].min(), df['Time'].max(), freq='1T')

interp = pd.Series(np.interp(minutes, df['Time'], df['Score']))
interp.index = minutes
only_changes = pd.Series(np.where(interp <= -4, 1, 0)).diff().map({1:'Red', -1:'Green', 0:np.nan})

only_changes = only_changes[~pd.isna(only_changes)]

然后将scatter上面相同绘图代码的调用替换为:

ax.scatter(interp.index[only_changes.index], interp[only_changes.index], c=only_changes)

在此处输入图像描述

但请注意,现在红/绿点并未放置在数据中的实际测量点上!


推荐阅读