本地英文版地址: ../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禁用此行为