本地英文版地址: ../en/search-aggregations-pipeline-movfn-aggregation.html
给定一系列有序的数据,移动函数(moving function)聚合将在数据上滑动一个窗口,并允许用户指定在每个数据窗口上执行自定义的脚本。 为方便起见,预定义了一些常用函数,如min/max、移动平均值(moving average)等。
这在概念上与移动平均(moving average)管道聚合非常相似,不过它提供了更多的功能。
一个单独的moving_fn
看起来像这样:
{ "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.min(values)" } }
表 19. moving_fn
参数
参数名称 | 描述 | 是否必需 | 默认值 |
---|---|---|---|
|
感兴趣的度量的路径(更多详情请参考 |
必需 |
|
|
在直方图上“滑动”的窗口大小。 |
必需 |
|
|
应该在每个数据窗口上执行的脚本 |
必需 |
|
|
窗口位置的移动(shift) |
可选 |
0 |
moving_fn
聚合必须嵌入histogram
或date_histogram
聚合中。
它们可以像任何其他度量聚合一样嵌入:
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movfn": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.unweightedAvg(values)" } } } } } }
一个名为“my_date_histo”的 |
|
|
|
最后,我们指定一个 |
移动平均值的构建,首先要指定一个字段的histogram
或date_histogram
聚合。
然后,你可以选择在那个直方图中添加数字度量,如sum
。
最后,moving_fn
被嵌入直方图中。
然后,buckets_path
参数用于“指向”直方图内的一个同级度量(有关buckets_path
语法的描述,请参见buckets_path
语法)。
来自上述聚合的示例响应可能如下所示:
{ "took": 11, "timed_out": false, "_shards": ..., "hits": ..., "aggregations": { "my_date_histo": { "buckets": [ { "key_as_string": "2015/01/01 00:00:00", "key": 1420070400000, "doc_count": 3, "the_sum": { "value": 550.0 }, "the_movfn": { "value": null } }, { "key_as_string": "2015/02/01 00:00:00", "key": 1422748800000, "doc_count": 2, "the_sum": { "value": 60.0 }, "the_movfn": { "value": 550.0 } }, { "key_as_string": "2015/03/01 00:00:00", "key": 1425168000000, "doc_count": 2, "the_sum": { "value": 375.0 }, "the_movfn": { "value": 305.0 } } ] } } }
移动函数聚合允许用户指定任意脚本来定义自定义逻辑。
每次收集新的数据窗口时都会调用该脚本。
这些值在values
变量中提供给脚本。
然后,脚本应该执行某种计算,并输出单个double
(双精度浮点数)作为结果。
虽然允许NaN
和 +/- Inf
,但不允许输出null
。
例如,下面这个脚本将只返回窗口中的第一个值,如果没有可用的值,则返回NaN
:
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "return values.length > 0 ? values[0] : Double.NaN" } } } } } }
默认情况下(shift = 0
),为计算提供的窗口是除当前桶之外的最后n
个值。
将shift
增加1
会将起始窗口位置向右移动1
。
-
要将当前桶包括到窗口中,请使用
shift = 1
。 -
对于中心对齐(当前桶前后的
n / 2
个值),使用shift = window / 2
。 -
对于右对齐(当前桶后的
n
个值),使用shift = window
。
如果任何一个窗口边缘移出了数据序列的边界,则窗口将收缩以只包含可用值。
为了方便起见,已经预先构建了许多函数,这些函数在moving_fn
脚本上下文中可用:
-
max()
-
min()
-
sum()
-
stdDev()
-
unweightedAvg()
-
linearWeightedAvg()
-
ewma()
-
holt()
-
holtWinters()
这些函数可从MovingFunctions
命名空间中获得。例如MovingFunctions.max()
该函数接受一个双精度值的集合,并返回该窗口中的最大值。
null
和NaN
值被忽略;最大值仅在实际值上计算。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_moving_max": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.max(values)" } } } } } }
该函数接受一个双精度值的集合,并返回该窗口中的最小值。
null
和NaN
值被忽略;最大值仅在实际值上计算。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_moving_min": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.min(values)" } } } } } }
该函数接受一个双精度值的集合,并返回该窗口中值的总和。
null
和NaN
值被忽略;总和仅在实际值上计算。
如果窗口为空,或者所有值都为null
或NaN
,则返回0.0
作为结果。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_moving_sum": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.sum(values)" } } } } } }
该函数接受双精度浮点数(double)和平均值(average)的集合,然后返回该窗口中值的标准差。
null
和NaN
值被忽略;平均值仅在实际值上计算。
如果窗口为空,或者所有值都为null
或NaN
,则返回0.0
作为结果。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_moving_sum": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.stdDev(values, MovingFunctions.unweightedAvg(values))" } } } } } }
avg
参数必须提供给标准差函数,因为可以在窗口上计算不同类型的平均值(简单(simple)、线性加权(linearly weighted)等)。
下面详述的各种移动平均值可用于计算标准差函数的平均值。
unweightedAvg
函数计算窗口中所有值的总和,然后除以窗口的大小。
它实际上是窗口的简单算术平均值。
简单移动平均值不执行任何时间相关的加权,这意味着simple
(简单)移动平均的值往往“滞后”于真实数据。
null
和NaN
值被忽略;平均值仅根据真实值计算得出。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
这意味着平均值计算中使用的计数是非null
、非NaN
值的计数。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.unweightedAvg(values)" } } } } } }
linearWeightedAvg
函数为序列中的点分配线性权重,使得“较老的”数据点(例如,窗口开始处的那些)对总平均值的贡献线性较小。
线性加权有助于减少数据均值的“滞后”,因为较老的数据点的影响较小。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.linearWeightedAvg(values)" } } } } } }
ewma
函数(也称为“单指数”)类似于linearMovAvg
函数,不同的是,旧的数据点的重要性是以指数级降低,而不是线性降低。
重要性衰减的速度可以通过设置alpha
来控制。
较小的值使权重缓慢衰减,这提供了更大的平滑度,并考虑了窗口的更大部分。
较大的值会使权重迅速衰减,从而降低较旧值对移动平均线的影响。
这往往会使移动平均线更密切地跟踪数据,但却不太平滑。
null
和NaN
值被忽略;平均值仅根据真实值计算得出。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
这意味着平均值计算中使用的计数是非null
、非NaN
值的计数。
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.ewma(values, 0.3)" } } } } } }
holt
函数(又名“二次指数”)结合了跟踪数据趋势的第二个指数项。
当数据具有潜在的线性趋势时,单指数表现不佳。
二次指数模型在内部计算两个值:“水平(level)”和“趋势(trend)”。
水平(level)计算类似于ewma
,是数据的指数加权视图。
不同之处在于,使用了之前平滑的值,而不是原始值,这使其接近原始序列。
趋势(trend)计算着眼于当前值和上一个值之间的差异(例如,平滑数据的斜率或趋势)。
趋势值也是指数加权的。
数值是由水平(level)分量和趋势(trend)分量相乘产生的。
null
和NaN
值被忽略;平均值仅根据真实值计算得出。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
这意味着平均值计算中使用的计数是非null
、非NaN
值的计数。
表 27. holt(double[] values, double alpha)
参数
参数名称 | 描述 |
---|---|
|
要计算总和的值的窗口 |
|
level 衰减值 |
|
trend 衰减值 |
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "MovingFunctions.holt(values, 0.3, 0.1)" } } } } } }
实际上,alpha
值在holtMovAvg
中的表现与ewmaMovAvg
非常相似:小值产生更多平滑和更多滞后,而大值产生更密切的跟踪和更少的滞后。
beta
的值往往很难看出。
较小的值强调长期趋势(例如整个系列中的恒定线性趋势),而较大的值强调短期趋势。
holtWinters
函数(又名“三级指数”)包含第三个指数项,用于跟踪数据的季节性。
因此,这种聚合基于三个要素进行平滑:“水平(level)”、“趋势(trend)”和“季节性(seasonality)”。
水平(level)和趋势(trend)计算与holt
里的相同。
季节性(seasonal)计算着眼于当前点与前一时段点之间的差异。
与其他移动平均线相比,holt-winters 需要更多的操作。
你需要指定数据的“周期(periodicity)”:例如,如果你的数据每7天有一个循环趋势,可以设置period: 7
。
类似地,如果有月趋势,你可以把它设置为30
。
目前没有周期性检测,尽管计划在未来进行增强。
null
和NaN
值被忽略;平均值仅根据真实值计算得出。
如果窗口为空,或者所有值都为null
或NaN
,则返回NaN
作为结果。
这意味着平均值计算中使用的计数是非null
、非NaN
值的计数。
表 28. holtWinters(double[] values, double alpha)
参数
参数名称 | 描述 |
---|---|
|
要计算总和的值的窗口 |
|
水平(level)衰减值 |
|
趋势(trend)衰减值 |
|
季节性(seasonality)衰减值 |
|
数据的周期性 |
|
为true时使用乘法holt-winters,为false时使用加法 |
POST /_search { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "calendar_interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_fn": { "buckets_path": "the_sum", "window": 10, "script": "if (values.length > 5*2) {MovingFunctions.holtWinters(values, 0.3, 0.1, 0.1, 5, false)}" } } } } } }
乘法霍尔特-温特斯的工作原理是将每个数据点除以季节值。
如果任何数据为零,或者数据中有间隙(因为这会导致除以零),那么这就有问题了。
为了解决这个问题,mult
Holt-Winters用一个非常小的量(1*10-10)填充所有的值,这样所有的值都是非零的。
这会影响结果,但影响很小。
如果你的数据是非零的,或者你希望在遇到零时看到NaN
,则可以使用pad: false
禁用此行为