处理不规则时序数据
作者:Noah Crowley / 产品,应用场景,开发者
2018年10月26日
导航至
InfluxDB的一个优点是能够以不规则时序存储原始事件,这些事件可能以不同的间隔出现。然而,不规则时序数据会带来一些独特的挑战,在某些情况下,对数据进行常见操作可能根本不起作用。幸运的是,InfluxDB允许您在实时将不规则时序数据转换为规则时序数据,通过计算任意时间窗口内单个值的汇总来实现。这为您从系统中捕获事件并处理这些数据提供了最佳方案。
我们可以查看一些实际数据点,以便更好地了解在处理不规则时序数据时需要考虑哪些因素。为了举例说明,我们将使用五个数据点,并赋予它们值为10、20、30、40和50。
如果这些数据以规则间隔收集,那么计算这些值的平均值也将给出随时间变化的平均功耗。
然而,如果您的数据以不规则间隔收集,那么计算这些值的平均值将不会给出预期的结果。
将这些数据点添加到InfluxDB并进行操作将使我们更好地了解正在发生的情况。使用Influx CLI,我将这些五个值插入到名为m1
的测量中。
> select * from m1 where time > '2018-08-14T17:22:14Z' and time < '2018-08-14T17:23:20Z'
name: m1
time value
---- -----
2018-08-14T17:22:14.159637Z 10
2018-08-14T17:22:16.3561521Z 20
2018-08-14T17:22:18.2251241Z 30
2018-08-14T17:22:20.18086Z 40
2018-08-14T17:23:19.8976057Z 50
由于我是手动插入数据的,因此这些值的时戳不是均匀分布的,这正是我们在这个例子中所需要的。前四个发生在同一分钟内,而第五个则发生在近一分钟之后。我们可以计算这五个值的平均值。
> select mean(*) From m1
name: m1
time mean_value
---- ----------
1970-01-01T00:00:00Z 30
结果是30,结果中的时间戳值是我们计算平均值的时间窗口的开始。
如果这是一个常规的时间序列,那么30的平均值在直觉上是有意义的。但是,由于我们的例子是一个不规则的时间序列,因此在计算最终结果时,测量时间之间的间隔很重要。当可视化这些值时,这一点会更加清晰;下面是这些点在图上的样子。
我们可以看到,大部分时间都是在40和50的值之间度过的(我们在这些点之间进行线性插值以绘制图形)。因此,我们可以合理地推测,时间的平均值可能更接近40而不是30。
显然,直接计算不规则序列中所有值的平均值是不行的,因为这个操作忽略了值在时间上的分布。我们需要对我们的数据强加某种规律性。
为此,我们可以使用InfluxQL的GROUP BY
子句将我们的数据的时间范围划分为离散的单位,然后对那些窗口内的数据值进行聚合。并不是每个间隔都会有值,而且那些有值的间隔可能会有多个值。作为我们系统和操作员,我们需要决定如何处理这些情况。在处理数据时,人类的判断和解释往往起着重要的作用。
如果我们使用足够小的窗口,并使用mean()
函数来计算聚合值,我们应该可以得到一个相当准确的时间序列的表示。这是我们将点分组到10秒间隔并计算每个组的平均值后的数据。
> select mean(*) from m1 where time > '2018-08-14T17:22:14Z' and time < '2018-08-14T17:23:20Z' group by time(10s)
name: m1
time mean_value
---- ----------
2018-08-14T17:22:10Z 20
2018-08-14T17:22:20Z 40
2018-08-14T17:22:30Z
2018-08-14T17:22:40Z
2018-08-14T17:22:50Z
2018-08-14T17:23:00Z
2018-08-14T17:23:10Z 50
对于从2018-08-14T17:22:10Z开始的10秒周期,我们有三个值,10、20和30,它们的平均值是20。对于下一个窗口,我们只有一个值,然后我们看到了一个没有值的窗口,直到我们看到最终值50。和之前一样,我们将不得不做出判断,决定我们想要如何处理这些空窗口。
GROUP BY
的fill()
选项将允许我们填充任何空窗口的数据。fill()
可以接受选项null
、previous
、number
、none
或linear
。在我们的情况下,linear
是正确的选择,因为我们使用了线性插值来绘图,但如果这是真实数据,其他一些选项可能更合适。
这就是使用fill(linear)
后的数据。
> select mean(*) from m1 where time > '2018-08-14T17:22:14Z' and time < '2018-08-14T17:23:20Z' group by time(10s) fill(linear)
name: m1
time mean_value
---- ----------
2018-08-14T17:22:10Z 20
2018-08-14T17:22:20Z 40
2018-08-14T17:22:30Z 42
2018-08-14T17:22:40Z 44
2018-08-14T17:22:50Z 46
2018-08-14T17:23:00Z 48
2018-08-14T17:23:10Z 50
然后我们可以使用子查询计算我们新的、规则时间序列的平均值。
> select mean(*) from (select mean(*) from m1 where time > '2018-08-14T17:22:14Z' and time < '2018-08-14T17:23:20Z' group by time(10s) fill(linear))
name: m1
time mean_mean_value
---- ---------------
1970-01-01T00:00:00Z 41.42857142857143
但是,还有一个需要注意的问题:我们选择的窗口将影响我们的最终结果。如果我们使用1秒窗口而不是10秒窗口会怎样呢?
> select mean(*) from (select mean(*) from m1 where time > '2018-08-14T17:22:14Z' and time < '2018-08-14T17:23:20Z' group by time(1s) fill(linear))
name: m1
time mean_mean_value
---- ---------------
1970-01-01T00:00:00Z 42.95454545454546
我们得到了一个略微不同的结果!处理不规则的时间序列并不精确,你的方法将取决于具体数据。不同的聚合方法、时间间隔大小和插值方法可能比其他方法更合适。
如果上述示例中的数据是某种环境数据,例如环境温度,而我们序列的不规则性是由于数据收集不可靠,那么我们使用的方法可能适用。
如果数据是在状态改变时收集的,例如我们有许多LED灯,我们以十块的方式切换它们,那么我们在查询中执行的线性填充就不再合理;相反,根据我们数据要模拟的系统行为,使用 fill(previous)
可能更合适。
最终,这是处理不规则时间序列的最大挑战:你需要足够了解你的数据,以便可以就如何处理这些数据做出明智的决定。
如果你对你的不规则时间序列有疑问,我们的社区网站是获取InfluxData平台其他用户帮助的好地方,或者你可以直接在Twitter上联系我 @noahcrowley!