移动函数(moving_fn)聚合

给定一系列有序的数据,移动函数(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参数

参数名称 描述 是否必需 默认值

buckets_path

感兴趣的度量的路径(更多详情请参考 buckets_path语法)

必需

window

在直方图上“滑动”的窗口大小。

必需

script

应该在每个数据窗口上执行的脚本

必需

shift

窗口位置的移动(shift)

可选

0

moving_fn聚合必须嵌入histogramdate_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”的date_histogram聚合是在“timestamp”字段上构建的,间隔为一天

sum度量用于计算字段的总和。 这可以是任何数字度量(sum、min、max等)

最后,我们指定一个moving_fn聚合,它使用“the_sum”度量作为其输入。

移动平均值的构建,首先要指定一个字段的histogramdate_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"

默认情况下(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()

max 函数

该函数接受一个双精度值的集合,并返回该窗口中的最大值。 nullNaN值被忽略;最大值仅在实际值上计算。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。

表 20. max(double[] values)参数

参数名称 描述

values

用于查找最大值的值窗口

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)"
                    }
                }
            }
        }
    }
}

min 函数

该函数接受一个双精度值的集合,并返回该窗口中的最小值。 nullNaN值被忽略;最大值仅在实际值上计算。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。

表 21. min(double[] values)参数

参数名称 描述

values

用于查找最小值的值窗口

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)"
                    }
                }
            }
        }
    }
}

sum 函数

该函数接受一个双精度值的集合,并返回该窗口中值的总和。 nullNaN值被忽略;总和仅在实际值上计算。 如果窗口为空,或者所有值都为nullNaN,则返回0.0作为结果。

表 22. sum(double[] values)参数

参数名称 描述

values

要计算总和的值的窗口

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)"
                    }
                }
            }
        }
    }
}

stdDev 函数

该函数接受双精度浮点数(double)和平均值(average)的集合,然后返回该窗口中值的标准差。 nullNaN值被忽略;平均值仅在实际值上计算。 如果窗口为空,或者所有值都为nullNaN,则返回0.0作为结果。

表 23. stdDev(double[] values)参数

参数名称 描述

values

用于计算标准差的值窗口

avg

窗口的平均值

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 函数

unweightedAvg函数计算窗口中所有值的总和,然后除以窗口的大小。 它实际上是窗口的简单算术平均值。 简单移动平均值不执行任何时间相关的加权,这意味着simple(简单)移动平均的值往往“滞后”于真实数据。

nullNaN值被忽略;平均值仅根据真实值计算得出。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。 这意味着平均值计算中使用的计数是非null、非NaN值的计数。

表 24. unweightedAvg(double[] values)参数

参数名称 描述

values

要计算总和的值的窗口

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 函数

linearWeightedAvg函数为序列中的点分配线性权重,使得“较老的”数据点(例如,窗口开始处的那些)对总平均值的贡献线性较小。 线性加权有助于减少数据均值的“滞后”,因为较老的数据点的影响较小。

如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。

表 25. linearWeightedAvg(double[] values)参数

参数名称 描述

values

要计算总和的值的窗口

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 函数

ewma函数(也称为“单指数”)类似于linearMovAvg函数,不同的是,旧的数据点的重要性是以指数级降低,而不是线性降低。 重要性衰减的速度可以通过设置alpha来控制。 较小的值使权重缓慢衰减,这提供了更大的平滑度,并考虑了窗口的更大部分。 较大的值会使权重迅速衰减,从而降低较旧值对移动平均线的影响。 这往往会使移动平均线更密切地跟踪数据,但却不太平滑。

nullNaN值被忽略;平均值仅根据真实值计算得出。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。 这意味着平均值计算中使用的计数是非null、非NaN值的计数。

表 26. ewma(double[] values, double alpha)参数

参数名称 描述

values

要计算总和的值的窗口

alpha

指数衰减

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 函数

holt函数(又名“二次指数”)结合了跟踪数据趋势的第二个指数项。 当数据具有潜在的线性趋势时,单指数表现不佳。 二次指数模型在内部计算两个值:“水平(level)”和“趋势(trend)”。

水平(level)计算类似于ewma,是数据的指数加权视图。 不同之处在于,使用了之前平滑的值,而不是原始值,这使其接近原始序列。 趋势(trend)计算着眼于当前值和上一个值之间的差异(例如,平滑数据的斜率或趋势)。 趋势值也是指数加权的。

数值是由水平(level)分量和趋势(trend)分量相乘产生的。

nullNaN值被忽略;平均值仅根据真实值计算得出。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。 这意味着平均值计算中使用的计数是非null、非NaN值的计数。

表 27. holt(double[] values, double alpha)参数

参数名称 描述

values

要计算总和的值的窗口

alpha

level 衰减值

beta

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 函数

holtWinters函数(又名“三级指数”)包含第三个指数项,用于跟踪数据的季节性。 因此,这种聚合基于三个要素进行平滑:“水平(level)”、“趋势(trend)”和“季节性(seasonality)”。

水平(level)和趋势(trend)计算与holt里的相同。 季节性(seasonal)计算着眼于当前点与前一时段点之间的差异。

与其他移动平均线相比,holt-winters 需要更多的操作。 你需要指定数据的“周期(periodicity)”:例如,如果你的数据每7天有一个循环趋势,可以设置period: 7。 类似地,如果有月趋势,你可以把它设置为30。 目前没有周期性检测,尽管计划在未来进行增强。

nullNaN值被忽略;平均值仅根据真实值计算得出。 如果窗口为空,或者所有值都为nullNaN,则返回NaN作为结果。 这意味着平均值计算中使用的计数是非null、非NaN值的计数。

表 28. holtWinters(double[] values, double alpha)参数

参数名称 描述

values

要计算总和的值的窗口

alpha

水平(level)衰减值

beta

趋势(trend)衰减值

gamma

季节性(seasonality)衰减值

period

数据的周期性

multiplicative

为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禁用此行为

"cold start" (冷启动)

不幸的是,由于 holt-winters 的性质,它需要两个周期(period)的数据来“引导”该算法。 这意味着你的window值必须至少是周期(period)的两倍。 如果不是,将会抛出异常。 这也意味着holt-winters不会为第一个2 * period桶生成值;当前算法不进行回溯。

你会注意到在上面的例子中我们有一个if ()语句来检查值的大小。 这是在调用 holt-winters 函数之前,检查以确保我们有两个周期的数据(5 * 2,其中5是在holtWintersMovAvg函数中指定的周期)。