movavg-aggregation.asciidoc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. [[search-aggregations-pipeline-movavg-aggregation]]
  2. === Moving Average Aggregation
  3. Given an ordered series of data, the Moving Average aggregation will slide a window across the data and emit the average
  4. value of that window. For example, given the data `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`, we can calculate a simple moving
  5. average with windows size of `5` as follows:
  6. - (1 + 2 + 3 + 4 + 5) / 5 = 3
  7. - (2 + 3 + 4 + 5 + 6) / 5 = 4
  8. - (3 + 4 + 5 + 6 + 7) / 5 = 5
  9. - etc
  10. Moving averages are a simple method to smooth sequential data. Moving averages are typically applied to time-based data,
  11. such as stock prices or server metrics. The smoothing can be used to eliminate high frequency fluctuations or random noise,
  12. which allows the lower frequency trends to be more easily visualized, such as seasonality.
  13. ==== Syntax
  14. A `moving_avg` aggregation looks like this in isolation:
  15. [source,js]
  16. --------------------------------------------------
  17. {
  18. "moving_avg": {
  19. "buckets_path": "the_sum",
  20. "model": "holt",
  21. "window": 5,
  22. "gap_policy": "insert_zeros",
  23. "settings": {
  24. "alpha": 0.8
  25. }
  26. }
  27. }
  28. --------------------------------------------------
  29. // NOTCONSOLE
  30. .`moving_avg` Parameters
  31. |===
  32. |Parameter Name |Description |Required |Default Value
  33. |`buckets_path` |Path to the metric of interest (see <<buckets-path-syntax, `buckets_path` Syntax>> for more details |Required |
  34. |`model` |The moving average weighting model that we wish to use |Optional |`simple`
  35. |`gap_policy` |Determines what should happen when a gap in the data is encountered. |Optional |`insert_zeros`
  36. |`window` |The size of window to "slide" across the histogram. |Optional |`5`
  37. |`minimize` |If the model should be algorithmically minimized. See <<movavg-minimizer, Minimization>> for more
  38. details |Optional |`false` for most models
  39. |`settings` |Model-specific settings, contents which differ depending on the model specified. |Optional |
  40. |===
  41. `moving_avg` aggregations must be embedded inside of a `histogram` or `date_histogram` aggregation. They can be
  42. embedded like any other metric aggregation:
  43. [source,js]
  44. --------------------------------------------------
  45. POST /_search
  46. {
  47. "size": 0,
  48. "aggs": {
  49. "my_date_histo":{ <1>
  50. "date_histogram":{
  51. "field":"date",
  52. "interval":"1M"
  53. },
  54. "aggs":{
  55. "the_sum":{
  56. "sum":{ "field": "price" } <2>
  57. },
  58. "the_movavg":{
  59. "moving_avg":{ "buckets_path": "the_sum" } <3>
  60. }
  61. }
  62. }
  63. }
  64. }
  65. --------------------------------------------------
  66. // CONSOLE
  67. // TEST[setup:sales]
  68. <1> A `date_histogram` named "my_date_histo" is constructed on the "timestamp" field, with one-day intervals
  69. <2> A `sum` metric is used to calculate the sum of a field. This could be any metric (sum, min, max, etc)
  70. <3> Finally, we specify a `moving_avg` aggregation which uses "the_sum" metric as its input.
  71. Moving averages are built by first specifying a `histogram` or `date_histogram` over a field. You can then optionally
  72. add normal metrics, such as a `sum`, inside of that histogram. Finally, the `moving_avg` is embedded inside the histogram.
  73. The `buckets_path` parameter is then used to "point" at one of the sibling metrics inside of the histogram (see
  74. <<buckets-path-syntax>> for a description of the syntax for `buckets_path`.
  75. An example response from the above aggregation may look like:
  76. [source,js]
  77. --------------------------------------------------
  78. {
  79. "took": 11,
  80. "timed_out": false,
  81. "_shards": ...,
  82. "hits": ...,
  83. "aggregations": {
  84. "my_date_histo": {
  85. "buckets": [
  86. {
  87. "key_as_string": "2015/01/01 00:00:00",
  88. "key": 1420070400000,
  89. "doc_count": 3,
  90. "the_sum": {
  91. "value": 550.0
  92. }
  93. },
  94. {
  95. "key_as_string": "2015/02/01 00:00:00",
  96. "key": 1422748800000,
  97. "doc_count": 2,
  98. "the_sum": {
  99. "value": 60.0
  100. },
  101. "the_movavg": {
  102. "value": 550.0
  103. }
  104. },
  105. {
  106. "key_as_string": "2015/03/01 00:00:00",
  107. "key": 1425168000000,
  108. "doc_count": 2,
  109. "the_sum": {
  110. "value": 375.0
  111. },
  112. "the_movavg": {
  113. "value": 305.0
  114. }
  115. }
  116. ]
  117. }
  118. }
  119. }
  120. --------------------------------------------------
  121. // TESTRESPONSE[s/"took": 11/"took": $body.took/]
  122. // TESTRESPONSE[s/"_shards": \.\.\./"_shards": $body._shards/]
  123. // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
  124. ==== Models
  125. The `moving_avg` aggregation includes four different moving average "models". The main difference is how the values in the
  126. window are weighted. As data-points become "older" in the window, they may be weighted differently. This will
  127. affect the final average for that window.
  128. Models are specified using the `model` parameter. Some models may have optional configurations which are specified inside
  129. the `settings` parameter.
  130. ===== Simple
  131. The `simple` model calculates the sum of all values in the window, then divides by the size of the window. It is effectively
  132. a simple arithmetic mean of the window. The simple model does not perform any time-dependent weighting, which means
  133. the values from a `simple` moving average tend to "lag" behind the real data.
  134. [source,js]
  135. --------------------------------------------------
  136. POST /_search
  137. {
  138. "size": 0,
  139. "aggs": {
  140. "my_date_histo":{
  141. "date_histogram":{
  142. "field":"date",
  143. "interval":"1M"
  144. },
  145. "aggs":{
  146. "the_sum":{
  147. "sum":{ "field": "price" }
  148. },
  149. "the_movavg":{
  150. "moving_avg":{
  151. "buckets_path": "the_sum",
  152. "window" : 30,
  153. "model" : "simple"
  154. }
  155. }
  156. }
  157. }
  158. }
  159. }
  160. --------------------------------------------------
  161. // CONSOLE
  162. // TEST[setup:sales]
  163. A `simple` model has no special settings to configure
  164. The window size can change the behavior of the moving average. For example, a small window (`"window": 10`) will closely
  165. track the data and only smooth out small scale fluctuations:
  166. [[movavg_10window]]
  167. .Moving average with window of size 10
  168. image::images/pipeline_movavg/movavg_10window.png[]
  169. In contrast, a `simple` moving average with larger window (`"window": 100`) will smooth out all higher-frequency fluctuations,
  170. leaving only low-frequency, long term trends. It also tends to "lag" behind the actual data by a substantial amount:
  171. [[movavg_100window]]
  172. .Moving average with window of size 100
  173. image::images/pipeline_movavg/movavg_100window.png[]
  174. ==== Linear
  175. The `linear` model assigns a linear weighting to points in the series, such that "older" datapoints (e.g. those at
  176. the beginning of the window) contribute a linearly less amount to the total average. The linear weighting helps reduce
  177. the "lag" behind the data's mean, since older points have less influence.
  178. [source,js]
  179. --------------------------------------------------
  180. POST /_search
  181. {
  182. "size": 0,
  183. "aggs": {
  184. "my_date_histo":{
  185. "date_histogram":{
  186. "field":"date",
  187. "interval":"1M"
  188. },
  189. "aggs":{
  190. "the_sum":{
  191. "sum":{ "field": "price" }
  192. },
  193. "the_movavg": {
  194. "moving_avg":{
  195. "buckets_path": "the_sum",
  196. "window" : 30,
  197. "model" : "linear"
  198. }
  199. }
  200. }
  201. }
  202. }
  203. }
  204. --------------------------------------------------
  205. // CONSOLE
  206. // TEST[setup:sales]
  207. A `linear` model has no special settings to configure
  208. Like the `simple` model, window size can change the behavior of the moving average. For example, a small window (`"window": 10`)
  209. will closely track the data and only smooth out small scale fluctuations:
  210. [[linear_10window]]
  211. .Linear moving average with window of size 10
  212. image::images/pipeline_movavg/linear_10window.png[]
  213. In contrast, a `linear` moving average with larger window (`"window": 100`) will smooth out all higher-frequency fluctuations,
  214. leaving only low-frequency, long term trends. It also tends to "lag" behind the actual data by a substantial amount,
  215. although typically less than the `simple` model:
  216. [[linear_100window]]
  217. .Linear moving average with window of size 100
  218. image::images/pipeline_movavg/linear_100window.png[]
  219. ==== EWMA (Exponentially Weighted)
  220. The `ewma` model (aka "single-exponential") is similar to the `linear` model, except older data-points become exponentially less important,
  221. rather than linearly less important. The speed at which the importance decays can be controlled with an `alpha`
  222. setting. Small values make the weight decay slowly, which provides greater smoothing and takes into account a larger
  223. portion of the window. Larger valuers make the weight decay quickly, which reduces the impact of older values on the
  224. moving average. This tends to make the moving average track the data more closely but with less smoothing.
  225. The default value of `alpha` is `0.3`, and the setting accepts any float from 0-1 inclusive.
  226. The EWMA model can be <<movavg-minimizer, Minimized>>
  227. [source,js]
  228. --------------------------------------------------
  229. POST /_search
  230. {
  231. "size": 0,
  232. "aggs": {
  233. "my_date_histo":{
  234. "date_histogram":{
  235. "field":"date",
  236. "interval":"1M"
  237. },
  238. "aggs":{
  239. "the_sum":{
  240. "sum":{ "field": "price" }
  241. },
  242. "the_movavg": {
  243. "moving_avg":{
  244. "buckets_path": "the_sum",
  245. "window" : 30,
  246. "model" : "ewma",
  247. "settings" : {
  248. "alpha" : 0.5
  249. }
  250. }
  251. }
  252. }
  253. }
  254. }
  255. }
  256. --------------------------------------------------
  257. // CONSOLE
  258. // TEST[setup:sales]
  259. [[single_0.2alpha]]
  260. .EWMA with window of size 10, alpha = 0.2
  261. image::images/pipeline_movavg/single_0.2alpha.png[]
  262. [[single_0.7alpha]]
  263. .EWMA with window of size 10, alpha = 0.7
  264. image::images/pipeline_movavg/single_0.7alpha.png[]
  265. ==== Holt-Linear
  266. The `holt` model (aka "double exponential") incorporates a second exponential term which
  267. tracks the data's trend. Single exponential does not perform well when the data has an underlying linear trend. The
  268. double exponential model calculates two values internally: a "level" and a "trend".
  269. The level calculation is similar to `ewma`, and is an exponentially weighted view of the data. The difference is
  270. that the previously smoothed value is used instead of the raw value, which allows it to stay close to the original series.
  271. The trend calculation looks at the difference between the current and last value (e.g. the slope, or trend, of the
  272. smoothed data). The trend value is also exponentially weighted.
  273. Values are produced by multiplying the level and trend components.
  274. The default value of `alpha` is `0.3` and `beta` is `0.1`. The settings accept any float from 0-1 inclusive.
  275. The Holt-Linear model can be <<movavg-minimizer, Minimized>>
  276. [source,js]
  277. --------------------------------------------------
  278. POST /_search
  279. {
  280. "size": 0,
  281. "aggs": {
  282. "my_date_histo":{
  283. "date_histogram":{
  284. "field":"date",
  285. "interval":"1M"
  286. },
  287. "aggs":{
  288. "the_sum":{
  289. "sum":{ "field": "price" }
  290. },
  291. "the_movavg": {
  292. "moving_avg":{
  293. "buckets_path": "the_sum",
  294. "window" : 30,
  295. "model" : "holt",
  296. "settings" : {
  297. "alpha" : 0.5,
  298. "beta" : 0.5
  299. }
  300. }
  301. }
  302. }
  303. }
  304. }
  305. }
  306. --------------------------------------------------
  307. // CONSOLE
  308. // TEST[setup:sales]
  309. In practice, the `alpha` value behaves very similarly in `holt` as `ewma`: small values produce more smoothing
  310. and more lag, while larger values produce closer tracking and less lag. The value of `beta` is often difficult
  311. to see. Small values emphasize long-term trends (such as a constant linear trend in the whole series), while larger
  312. values emphasize short-term trends. This will become more apparently when you are predicting values.
  313. [[double_0.2beta]]
  314. .Holt-Linear moving average with window of size 100, alpha = 0.5, beta = 0.2
  315. image::images/pipeline_movavg/double_0.2beta.png[]
  316. [[double_0.7beta]]
  317. .Holt-Linear moving average with window of size 100, alpha = 0.5, beta = 0.7
  318. image::images/pipeline_movavg/double_0.7beta.png[]
  319. ==== Holt-Winters
  320. The `holt_winters` model (aka "triple exponential") incorporates a third exponential term which
  321. tracks the seasonal aspect of your data. This aggregation therefore smooths based on three components: "level", "trend"
  322. and "seasonality".
  323. The level and trend calculation is identical to `holt` The seasonal calculation looks at the difference between
  324. the current point, and the point one period earlier.
  325. Holt-Winters requires a little more handholding than the other moving averages. You need to specify the "periodicity"
  326. of your data: e.g. if your data has cyclic trends every 7 days, you would set `period: 7`. Similarly if there was
  327. a monthly trend, you would set it to `30`. There is currently no periodicity detection, although that is planned
  328. for future enhancements.
  329. There are two varieties of Holt-Winters: additive and multiplicative.
  330. ===== "Cold Start"
  331. Unfortunately, due to the nature of Holt-Winters, it requires two periods of data to "bootstrap" the algorithm. This
  332. means that your `window` must always be *at least* twice the size of your period. An exception will be thrown if it
  333. isn't. It also means that Holt-Winters will not emit a value for the first `2 * period` buckets; the current algorithm
  334. does not backcast.
  335. [[holt_winters_cold_start]]
  336. .Holt-Winters showing a "cold" start where no values are emitted
  337. image::images/pipeline_movavg/triple_untruncated.png[]
  338. Because the "cold start" obscures what the moving average looks like, the rest of the Holt-Winters images are truncated
  339. to not show the "cold start". Just be aware this will always be present at the beginning of your moving averages!
  340. ===== Additive Holt-Winters
  341. Additive seasonality is the default; it can also be specified by setting `"type": "add"`. This variety is preferred
  342. when the seasonal affect is additive to your data. E.g. you could simply subtract the seasonal effect to "de-seasonalize"
  343. your data into a flat trend.
  344. The default values of `alpha` and `gamma` are `0.3` while `beta` is `0.1`. The settings accept any float from 0-1 inclusive.
  345. The default value of `period` is `1`.
  346. The additive Holt-Winters model can be <<movavg-minimizer, Minimized>>
  347. [source,js]
  348. --------------------------------------------------
  349. POST /_search
  350. {
  351. "size": 0,
  352. "aggs": {
  353. "my_date_histo":{
  354. "date_histogram":{
  355. "field":"date",
  356. "interval":"1M"
  357. },
  358. "aggs":{
  359. "the_sum":{
  360. "sum":{ "field": "price" }
  361. },
  362. "the_movavg": {
  363. "moving_avg":{
  364. "buckets_path": "the_sum",
  365. "window" : 30,
  366. "model" : "holt_winters",
  367. "settings" : {
  368. "type" : "add",
  369. "alpha" : 0.5,
  370. "beta" : 0.5,
  371. "gamma" : 0.5,
  372. "period" : 7
  373. }
  374. }
  375. }
  376. }
  377. }
  378. }
  379. }
  380. --------------------------------------------------
  381. // CONSOLE
  382. // TEST[setup:sales]
  383. [[holt_winters_add]]
  384. .Holt-Winters moving average with window of size 120, alpha = 0.5, beta = 0.7, gamma = 0.3, period = 30
  385. image::images/pipeline_movavg/triple.png[]
  386. ===== Multiplicative Holt-Winters
  387. Multiplicative is specified by setting `"type": "mult"`. This variety is preferred when the seasonal affect is
  388. multiplied against your data. E.g. if the seasonal affect is x5 the data, rather than simply adding to it.
  389. The default values of `alpha` and `gamma` are `0.3` while `beta` is `0.1`. The settings accept any float from 0-1 inclusive.
  390. The default value of `period` is `1`.
  391. The multiplicative Holt-Winters model can be <<movavg-minimizer, Minimized>>
  392. [WARNING]
  393. ======
  394. Multiplicative Holt-Winters works by dividing each data point by the seasonal value. This is problematic if any of
  395. your data is zero, or if there are gaps in the data (since this results in a divid-by-zero). To combat this, the
  396. `mult` Holt-Winters pads all values by a very small amount (1*10^-10^) so that all values are non-zero. This affects
  397. the result, but only minimally. If your data is non-zero, or you prefer to see `NaN` when zero's are encountered,
  398. you can disable this behavior with `pad: false`
  399. ======
  400. [source,js]
  401. --------------------------------------------------
  402. POST /_search
  403. {
  404. "size": 0,
  405. "aggs": {
  406. "my_date_histo":{
  407. "date_histogram":{
  408. "field":"date",
  409. "interval":"1M"
  410. },
  411. "aggs":{
  412. "the_sum":{
  413. "sum":{ "field": "price" }
  414. },
  415. "the_movavg": {
  416. "moving_avg":{
  417. "buckets_path": "the_sum",
  418. "window" : 30,
  419. "model" : "holt_winters",
  420. "settings" : {
  421. "type" : "mult",
  422. "alpha" : 0.5,
  423. "beta" : 0.5,
  424. "gamma" : 0.5,
  425. "period" : 7,
  426. "pad" : true
  427. }
  428. }
  429. }
  430. }
  431. }
  432. }
  433. }
  434. --------------------------------------------------
  435. // CONSOLE
  436. // TEST[setup:sales]
  437. ==== Prediction
  438. experimental[]
  439. All the moving average model support a "prediction" mode, which will attempt to extrapolate into the future given the
  440. current smoothed, moving average. Depending on the model and parameter, these predictions may or may not be accurate.
  441. Predictions are enabled by adding a `predict` parameter to any moving average aggregation, specifying the number of
  442. predictions you would like appended to the end of the series. These predictions will be spaced out at the same interval
  443. as your buckets:
  444. [source,js]
  445. --------------------------------------------------
  446. POST /_search
  447. {
  448. "size": 0,
  449. "aggs": {
  450. "my_date_histo":{
  451. "date_histogram":{
  452. "field":"date",
  453. "interval":"1M"
  454. },
  455. "aggs":{
  456. "the_sum":{
  457. "sum":{ "field": "price" }
  458. },
  459. "the_movavg": {
  460. "moving_avg":{
  461. "buckets_path": "the_sum",
  462. "window" : 30,
  463. "model" : "simple",
  464. "predict" : 10
  465. }
  466. }
  467. }
  468. }
  469. }
  470. }
  471. --------------------------------------------------
  472. // CONSOLE
  473. // TEST[setup:sales]
  474. The `simple`, `linear` and `ewma` models all produce "flat" predictions: they essentially converge on the mean
  475. of the last value in the series, producing a flat:
  476. [[simple_prediction]]
  477. .Simple moving average with window of size 10, predict = 50
  478. image::images/pipeline_movavg/simple_prediction.png[]
  479. In contrast, the `holt` model can extrapolate based on local or global constant trends. If we set a high `beta`
  480. value, we can extrapolate based on local constant trends (in this case the predictions head down, because the data at the end
  481. of the series was heading in a downward direction):
  482. [[double_prediction_local]]
  483. .Holt-Linear moving average with window of size 100, predict = 20, alpha = 0.5, beta = 0.8
  484. image::images/pipeline_movavg/double_prediction_local.png[]
  485. In contrast, if we choose a small `beta`, the predictions are based on the global constant trend. In this series, the
  486. global trend is slightly positive, so the prediction makes a sharp u-turn and begins a positive slope:
  487. [[double_prediction_global]]
  488. .Double Exponential moving average with window of size 100, predict = 20, alpha = 0.5, beta = 0.1
  489. image::images/pipeline_movavg/double_prediction_global.png[]
  490. The `holt_winters` model has the potential to deliver the best predictions, since it also incorporates seasonal
  491. fluctuations into the model:
  492. [[holt_winters_prediction_global]]
  493. .Holt-Winters moving average with window of size 120, predict = 25, alpha = 0.8, beta = 0.2, gamma = 0.7, period = 30
  494. image::images/pipeline_movavg/triple_prediction.png[]
  495. [[movavg-minimizer]]
  496. ==== Minimization
  497. Some of the models (EWMA, Holt-Linear, Holt-Winters) require one or more parameters to be configured. Parameter choice
  498. can be tricky and sometimes non-intuitive. Furthermore, small deviations in these parameters can sometimes have a drastic
  499. effect on the output moving average.
  500. For that reason, the three "tunable" models can be algorithmically *minimized*. Minimization is a process where parameters
  501. are tweaked until the predictions generated by the model closely match the output data. Minimization is not fullproof
  502. and can be susceptible to overfitting, but it often gives better results than hand-tuning.
  503. Minimization is disabled by default for `ewma` and `holt_linear`, while it is enabled by default for `holt_winters`.
  504. Minimization is most useful with Holt-Winters, since it helps improve the accuracy of the predictions. EWMA and
  505. Holt-Linear are not great predictors, and mostly used for smoothing data, so minimization is less useful on those
  506. models.
  507. Minimization is enabled/disabled via the `minimize` parameter:
  508. [source,js]
  509. --------------------------------------------------
  510. POST /_search
  511. {
  512. "size": 0,
  513. "aggs": {
  514. "my_date_histo":{
  515. "date_histogram":{
  516. "field":"date",
  517. "interval":"1M"
  518. },
  519. "aggs":{
  520. "the_sum":{
  521. "sum":{ "field": "price" }
  522. },
  523. "the_movavg": {
  524. "moving_avg":{
  525. "buckets_path": "the_sum",
  526. "model" : "holt_winters",
  527. "window" : 30,
  528. "minimize" : true, <1>
  529. "settings" : {
  530. "period" : 7
  531. }
  532. }
  533. }
  534. }
  535. }
  536. }
  537. }
  538. --------------------------------------------------
  539. // CONSOLE
  540. // TEST[setup:sales]
  541. <1> Minimization is enabled with the `minimize` parameter
  542. When enabled, minimization will find the optimal values for `alpha`, `beta` and `gamma`. The user should still provide
  543. appropriate values for `window`, `period` and `type`.
  544. [WARNING]
  545. ======
  546. Minimization works by running a stochastic process called *simulated annealing*. This process will usually generate
  547. a good solution, but is not guaranteed to find the global optimum. It also requires some amount of additional
  548. computational power, since the model needs to be re-run multiple times as the values are tweaked. The run-time of
  549. minimization is linear to the size of the window being processed: excessively large windows may cause latency.
  550. Finally, minimization fits the model to the last `n` values, where `n = window`. This generally produces
  551. better forecasts into the future, since the parameters are tuned around the end of the series. It can, however, generate
  552. poorer fitting moving averages at the beginning of the series.
  553. ======