mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Add support for date math in Timelion's .movingaverage() (#11555)
* Add support for date math to movingaverage * Fix excess phase shift and dropped point in .movingaverage() * Change help language, use if() instead of self executing function
This commit is contained in:
parent
11581ae136
commit
01e54022ad
2 changed files with 59 additions and 16 deletions
|
@ -1,33 +1,59 @@
|
|||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import buckets from './fixtures/bucketList';
|
||||
import getSeries from './helpers/get_series';
|
||||
import getSeriesList from './helpers/get_series_list';
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
function getFivePointSeries() {
|
||||
return getSeriesList([
|
||||
getSeries('Five', [].concat(buckets).push(moment('1984-01-01T00:00:00.000Z')), [10, 20, 30, 40, 50]),
|
||||
]);
|
||||
}
|
||||
|
||||
describe(filename, () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
seriesList = require('./fixtures/seriesList.js')();
|
||||
seriesList = getFivePointSeries();
|
||||
});
|
||||
|
||||
it('centers the averaged series by default', () => {
|
||||
return invoke(fn, [seriesList, 2]).then((r) => {
|
||||
expect(_.map(r.output.list[1].data, 1)).to.eql([null, 75, 50, null]);
|
||||
return invoke(fn, [seriesList, 3]).then((r) => {
|
||||
expect(_.map(r.output.list[0].data, 1)).to.eql([null, 20, 30, 40, null]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('aligns the moving average to the left', () => {
|
||||
return invoke(fn, [seriesList, 2, 'left']).then((r) => {
|
||||
expect(_.map(r.output.list[1].data, 1)).to.eql([null, null, 75, 50]);
|
||||
return invoke(fn, [seriesList, 3, 'left']).then((r) => {
|
||||
expect(_.map(r.output.list[0].data, 1)).to.eql([null, null, 20, 30, 40]);
|
||||
});
|
||||
});
|
||||
|
||||
it('aligns the moving average to the right', () => {
|
||||
return invoke(fn, [seriesList, 2, 'right']).then((r) => {
|
||||
expect(_.map(r.output.list[1].data, 1)).to.eql([75, 50, null, null]);
|
||||
return invoke(fn, [seriesList, 3, 'right']).then((r) => {
|
||||
expect(_.map(r.output.list[0].data, 1)).to.eql([20, 30, 40, null, null]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('date math', () => {
|
||||
it('accepts 2 years', () => {
|
||||
return invoke(fn, [seriesList, '2y', 'left']).then((r) => {
|
||||
expect(_.map(r.output.list[0].data, 1)).to.eql([null, 15, 25, 35, 45]);
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts 3 years', () => {
|
||||
return invoke(fn, [seriesList, '3y', 'left']).then((r) => {
|
||||
expect(_.map(r.output.list[0].data, 1)).to.eql([null, null, 20, 30, 40]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
import toMS from '../lib/to_milliseconds.js';
|
||||
|
||||
module.exports = new Chainable('movingaverage', {
|
||||
args: [
|
||||
{
|
||||
|
@ -9,8 +11,10 @@ module.exports = new Chainable('movingaverage', {
|
|||
},
|
||||
{
|
||||
name: 'window',
|
||||
types: ['number'],
|
||||
help: 'Number of points to average over'
|
||||
types: ['number', 'string'],
|
||||
help: 'Number of points, or a date math expression (eg 1d, 1M) to average over. ' +
|
||||
'If a date math expression is specified, the function will get as close as possible given the currently select interval' +
|
||||
'If the date math expression is not evenly divisible by the interval the results may appear abnormal.'
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
|
@ -20,9 +24,21 @@ module.exports = new Chainable('movingaverage', {
|
|||
],
|
||||
aliases: ['mvavg'],
|
||||
help: 'Calculate the moving average over a given window. Nice for smoothing noisey series',
|
||||
fn: function movingaverageFn(args) {
|
||||
fn: function movingaverageFn(args, tlConfig) {
|
||||
return alter(args, function (eachSeries, _window, _position) {
|
||||
|
||||
// _window always needs to be a number, if isn't we have to make it into one.
|
||||
if (typeof _window !== 'number') {
|
||||
// Ok, I guess its a datemath expression
|
||||
const windowMilliseconds = toMS(_window);
|
||||
|
||||
// calculate how many buckets that _window represents
|
||||
const intervalMilliseconds = toMS(tlConfig.time.interval);
|
||||
|
||||
// Round, floor, ceil? We're going with round because it splits the difference.
|
||||
_window = Math.round(windowMilliseconds / intervalMilliseconds) || 1;
|
||||
}
|
||||
|
||||
_position = _position || 'center';
|
||||
const validPositions = ['left', 'right', 'center'];
|
||||
if (!_.contains(validPositions, _position)) throw new Error('Valid positions are: ' + validPositions.join(', '));
|
||||
|
@ -44,18 +60,19 @@ module.exports = new Chainable('movingaverage', {
|
|||
const windowLeft = Math.floor(_window / 2);
|
||||
const windowRight = _window - windowLeft;
|
||||
eachSeries.data = _.map(pairs, function (point, i) {
|
||||
if (i < windowLeft || i >= pairsLen - windowRight) return [point[0], null];
|
||||
if (i < windowLeft || i > pairsLen - windowRight) return [point[0], null];
|
||||
return toPoint(point, pairs.slice(i - windowLeft, i + windowRight));
|
||||
});
|
||||
} else if (_position === 'left') {
|
||||
eachSeries.data = _.map(pairs, function (point, i) {
|
||||
if (i < _window) return [point[0], null];
|
||||
return toPoint(point, pairs.slice(i - _window, i));
|
||||
const cursor = i + 1;
|
||||
if (cursor < _window) return [point[0], null];
|
||||
return toPoint(point, pairs.slice(cursor - _window , cursor));
|
||||
});
|
||||
|
||||
} else if (_position === 'right') {
|
||||
eachSeries.data = _.map(pairs, function (point, i) {
|
||||
if (i >= pairsLen - _window) return [point[0], null];
|
||||
if (i > pairsLen - _window) return [point[0], null];
|
||||
return toPoint(point, pairs.slice(i , i + _window));
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue