d2-charts-api
Advanced tools
Comparing version 27.0.9 to 27.0.10
@@ -6,25 +6,20 @@ 'use strict'; | ||
}); | ||
exports.REGRESSION_TYPE_LINEAR = undefined; | ||
exports.default = function (series, isStacked) { | ||
exports.default = function (regressionType, series, isStacked) { | ||
var newSeries = []; | ||
if (isStacked) { | ||
return [].concat(_toConsumableArray(series), [Object.assign({}, DEFAULT_TRENDLINE, { | ||
data: getRegressionData((0, _getStackedData2.default)(series, true)) | ||
})]); | ||
newSeries.push.apply(newSeries, _toConsumableArray(series).concat([Object.assign({}, getRegressionObj((0, _getStackedData2.default)(series), regressionType))])); | ||
} else { | ||
var newSeries = []; | ||
series.forEach(function (seriesObj) { | ||
newSeries.push(seriesObj, Object.assign({}, DEFAULT_TRENDLINE, { | ||
color: getDarkerColor(seriesObj.color), | ||
data: getRegressionData(seriesObj.data) | ||
newSeries.push(seriesObj, Object.assign({}, getRegressionObj(seriesObj.data, regressionType), { | ||
name: seriesObj.name + ' (trend)', | ||
color: getDarkerColor(seriesObj.color) | ||
})); | ||
}); | ||
} | ||
return newSeries; | ||
} | ||
return newSeries; | ||
}; | ||
var _jqplot_regression = require('../../../util/regression/jqplot_regression'); | ||
var _d3Color = require('d3-color'); | ||
@@ -40,6 +35,4 @@ | ||
var REGRESSION_TYPE_LINEAR = exports.REGRESSION_TYPE_LINEAR = 'LINEAR'; | ||
var DEFAULT_TRENDLINE = { | ||
type: 'line', | ||
type: 'spline', | ||
name: 'Trend', | ||
@@ -50,2 +43,3 @@ dashStyle: 'solid', | ||
marker: { | ||
enabled: false, | ||
symbol: 'circle', | ||
@@ -56,24 +50,476 @@ radius: 2 | ||
function getAdaptedRegressionData(data) { | ||
return data.map(function (array) { | ||
return array[1]; | ||
function getColor(colors, index) { | ||
return colors[index] || getColor(colors, index - colors.length); | ||
} | ||
function getDarkerColor(color) { | ||
return (0, _d3Color.rgb)(color).darker(0.5).toString(); | ||
} | ||
function getRegressionData(data) { | ||
return data.map(function (value, index) { | ||
return [index, value]; | ||
}); | ||
} | ||
function getRegressionData(data, isClean) { | ||
var adaptedRegressionData = getAdaptedRegressionData((0, _jqplot_regression.fitData)(data).data); | ||
var i = 0; | ||
function getRegressionObj(data, regressionType) { | ||
// LINEAR: | ||
// - decimalPlaces (default = 2) | ||
// LOESS: | ||
// - loessSmooth (default = 25) | ||
// POLYNOMIAL: | ||
// - order (default = 2) | ||
// - extrapolate (default = 0) | ||
return isClean ? adaptedRegressionData : data.map(function (value, index) { | ||
return value === null ? value : adaptedRegressionData[i++]; | ||
var regression = void 0; | ||
var regressionTypeOptions = {}; | ||
switch (regressionType) { | ||
case 'LINEAR': | ||
// linear(data, decimalPlaces) | ||
regression = linear(getRegressionData(data)); | ||
regressionTypeOptions.type = 'line'; | ||
break; | ||
case 'POLYNOMIAL': | ||
// polynomial(data, order, extrapolate) | ||
regression = polynomial(getRegressionData(data), 2, 0); | ||
break; | ||
case 'LOESS': | ||
// loess(data, smoothing) | ||
regression = loess(getRegressionData(data), 0.25); | ||
break; | ||
}; | ||
return Object.assign({}, DEFAULT_TRENDLINE, regressionTypeOptions, { | ||
data: regression.points | ||
}); | ||
} | ||
function getColor(colors, index) { | ||
return colors[index] || getColor(colors, index - colors.length); | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function gaussianElimination(a, o) { | ||
var maxrow = 0, | ||
tmp = 0, | ||
n = a.length - 1, | ||
x = new Array(o); | ||
for (var i = 0; i < n; i++) { | ||
maxrow = i; | ||
for (var j = i + 1; j < n; j++) { | ||
if (Math.abs(a[i][j]) > Math.abs(a[i][maxrow])) { | ||
maxrow = j; | ||
} | ||
} | ||
for (var k = i; k < n + 1; k++) { | ||
tmp = a[k][i]; | ||
a[k][i] = a[k][maxrow]; | ||
a[k][maxrow] = tmp; | ||
} | ||
for (var _j = i + 1; _j < n; _j++) { | ||
for (var _k = n; _k >= i; _k--) { | ||
a[_k][_j] -= a[_k][i] * a[i][_j] / a[i][i]; | ||
} | ||
} | ||
} | ||
for (var _j2 = n - 1; _j2 >= 0; _j2--) { | ||
tmp = 0; | ||
for (var _k2 = _j2 + 1; _k2 < n; _k2++) { | ||
tmp += a[_k2][_j2] * x[_k2]; | ||
} | ||
x[_j2] = (a[n][_j2] - tmp) / a[_j2][_j2]; | ||
} | ||
return x; | ||
} | ||
function getDarkerColor(color) { | ||
return (0, _d3Color.rgb)(color).darker(0.5).toString(); | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
// Human readable formulas: | ||
// | ||
// N * Σ(XY) - Σ(X) | ||
// intercept = --------------------- | ||
// N * Σ(X^2) - Σ(X)^2 | ||
// | ||
// correlation = N * Σ(XY) - Σ(X) * Σ (Y) / √ ( N * Σ(X^2) - Σ(X) ) * ( N * Σ(Y^2) - Σ(Y)^2 ) ) ) | ||
function linear(data, decimalPlaces) { | ||
var sum = [0, 0, 0, 0, 0], | ||
results = [], | ||
N = data.length; | ||
for (var _n = 0, len = data.length; _n < len; _n++) { | ||
if (data[_n]['x'] != null) { | ||
data[_n][0] = data[_n].x; | ||
data[_n][1] = data[_n].y; | ||
} | ||
if (data[_n][1] != null) { | ||
sum[0] += data[_n][0]; // Σ(X) | ||
sum[1] += data[_n][1]; // Σ(Y) | ||
sum[2] += data[_n][0] * data[_n][0]; // Σ(X^2) | ||
sum[3] += data[_n][0] * data[_n][1]; // Σ(XY) | ||
sum[4] += data[_n][1] * data[_n][1]; // Σ(Y^2) | ||
} else { | ||
N -= 1; | ||
} | ||
} | ||
var gradient = (N * sum[3] - sum[0] * sum[1]) / (N * sum[2] - sum[0] * sum[0]); | ||
var intercept = sum[1] / N - gradient * sum[0] / N; | ||
// let correlation = (N * sum[3] - sum[0] * sum[1]) / Math.sqrt((N * sum[2] - sum[0] * sum[0]) * (N * sum[4] - sum[1] * sum[1])); | ||
for (var i = 0, _len = data.length; i < _len; i++) { | ||
var coorY = data[i][0] * gradient + intercept; | ||
if (decimalPlaces) { | ||
coorY = parseFloat(coorY.toFixed(decimalPlaces)); | ||
} | ||
var coordinate = [data[i][0], coorY]; | ||
results.push(coordinate); | ||
} | ||
results.sort(function (a, b) { | ||
if (a[0] > b[0]) { | ||
return 1; | ||
} | ||
if (a[0] < b[0]) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
var string = 'y = ' + Math.round(gradient * 100) / 100 + 'x + ' + Math.round(intercept * 100) / 100; | ||
return { | ||
equation: [gradient, intercept], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function logarithmic(data) { | ||
var sum = [0, 0, 0, 0], | ||
results = [], | ||
mean = 0; | ||
for (var _n2 = 0, len = data.length; _n2 < len; _n2++) { | ||
if (data[_n2].x != null) { | ||
data[_n2][0] = data[_n2].x; | ||
data[_n2][1] = data[_n2].y; | ||
} | ||
if (data[_n2][1] != null) { | ||
sum[0] += Math.log(data[_n2][0]); | ||
sum[1] += data[_n2][1] * Math.log(data[_n2][0]); | ||
sum[2] += data[_n2][1]; | ||
sum[3] += Math.pow(Math.log(data[_n2][0]), 2); | ||
} | ||
} | ||
var B = (n * sum[1] - sum[2] * sum[0]) / (n * sum[3] - sum[0] * sum[0]); | ||
var A = (sum[2] - B * sum[0]) / n; | ||
for (var i = 0, _len2 = data.length; i < _len2; i++) { | ||
var coordinate = [data[i][0], A + B * Math.log(data[i][0])]; | ||
results.push(coordinate); | ||
} | ||
results.sort(function (a, b) { | ||
if (a[0] > b[0]) { | ||
return 1; | ||
} | ||
if (a[0] < b[0]) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
var string = 'y = ' + Math.round(A * 100) / 100 + ' + ' + Math.round(B * 100) / 100 + ' ln(x)'; | ||
return { | ||
equation: [A, B], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function power(data) { | ||
var sum = [0, 0, 0, 0], | ||
results = []; | ||
for (var _n3 = 0, len = data.length; _n3 < len; _n3++) { | ||
if (data[_n3].x != null) { | ||
data[_n3][0] = data[_n3].x; | ||
data[_n3][1] = data[_n3].y; | ||
} | ||
if (data[_n3][1] != null) { | ||
sum[0] += Math.log(data[_n3][0]); | ||
sum[1] += Math.log(data[_n3][1]) * Math.log(data[_n3][0]); | ||
sum[2] += Math.log(data[_n3][1]); | ||
sum[3] += Math.pow(Math.log(data[_n3][0]), 2); | ||
} | ||
} | ||
var B = (n * sum[1] - sum[2] * sum[0]) / (n * sum[3] - sum[0] * sum[0]); | ||
var A = Math.pow(Math.E, (sum[2] - B * sum[0]) / n); | ||
for (var i = 0, _len3 = data.length; i < _len3; i++) { | ||
var coordinate = [data[i][0], A * Math.pow(data[i][0], B)]; | ||
results.push(coordinate); | ||
} | ||
results.sort(function (a, b) { | ||
if (a[0] > b[0]) { | ||
return 1; | ||
} | ||
if (a[0] < b[0]) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
var string = 'y = ' + Math.round(A * 100) / 100 + 'x^' + Math.round(B * 100) / 100; | ||
return { | ||
equation: [A, B], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function polynomial(data, order, extrapolate) { | ||
if (typeof order == 'undefined') { | ||
order = 2; | ||
} | ||
var lhs = [], | ||
rhs = [], | ||
results = [], | ||
a = 0, | ||
b = 0, | ||
k = order + 1; | ||
for (var i = 0; i < k; i++) { | ||
for (var l = 0, len = data.length; l < len; l++) { | ||
if (data[l].x != null) { | ||
data[l][0] = data[l].x; | ||
data[l][1] = data[l].y; | ||
} | ||
if (data[l][1] != null) { | ||
a += Math.pow(data[l][0], i) * data[l][1]; | ||
} | ||
} | ||
lhs.push(a); | ||
a = 0; | ||
var c = []; | ||
for (var j = 0; j < k; j++) { | ||
for (var _l = 0, _len4 = data.length; _l < _len4; _l++) { | ||
if (data[_l][1]) { | ||
b += Math.pow(data[_l][0], i + j); | ||
} | ||
} | ||
c.push(b); | ||
b = 0; | ||
} | ||
rhs.push(c); | ||
} | ||
rhs.push(lhs); | ||
var equation = gaussianElimination(rhs, k); | ||
var resultLength = data.length + extrapolate; | ||
var step = data[data.length - 1][0] - data[data.length - 2][0]; | ||
for (var _i = 0, _len5 = resultLength; _i < _len5; _i++) { | ||
var answer = 0, | ||
x = 0; | ||
if (typeof data[_i] !== 'undefined') { | ||
x = data[_i][0]; | ||
} else { | ||
x = data[data.length - 1][0] + (_i - data.length) * step; | ||
} | ||
for (var w = 0; w < equation.length; w++) { | ||
answer += equation[w] * Math.pow(x, w); | ||
} | ||
results.push([x, answer]); | ||
} | ||
results.sort(function (a, b) { | ||
if (a[0] > b[0]) { | ||
return 1; | ||
} | ||
if (a[0] < b[0]) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
var string = 'y = '; | ||
for (var _i2 = equation.length - 1; _i2 >= 0; _i2--) { | ||
if (_i2 > 1) { | ||
string += Math.round(equation[_i2] * 100) / 100 + 'x^' + _i2 + ' + '; | ||
} else if (_i2 == 1) { | ||
string += Math.round(equation[_i2] * 100) / 100 + 'x' + ' + '; | ||
} else { | ||
string += Math.round(equation[_i2] * 100) / 100; | ||
} | ||
} | ||
return { | ||
equation: equation, | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// @author: Ignacio Vazquez | ||
// Based on | ||
// - http://commons.apache.org/proper/commons-math/download_math.cgi LoesInterpolator.java | ||
// - https://gist.github.com/avibryant/1151823 | ||
function loess(data, bandwidth) { | ||
bandwidth = bandwidth || 0.25; | ||
var xval = data.map(function (pair) { | ||
return pair[0]; | ||
}); | ||
var distinctX = array_unique(xval); | ||
if (2 / distinctX.length > bandwidth) { | ||
bandwidth = Math.min(2 / distinctX.length, 1); | ||
console.warn("updated bandwith to " + bandwidth); | ||
} | ||
var yval = data.map(function (pair) { | ||
return pair[1]; | ||
}); | ||
function array_unique(values) { | ||
var o = {}, | ||
i = void 0, | ||
l = values.length, | ||
r = []; | ||
for (i = 0; i < l; i += 1) { | ||
o[values[i]] = values[i]; | ||
} | ||
for (i in o) { | ||
r.push(o[i]); | ||
} | ||
return r; | ||
} | ||
function tricube(x) { | ||
var tmp = 1 - x * x * x; | ||
return tmp * tmp * tmp; | ||
} | ||
var res = []; | ||
var left = 0; | ||
var right = Math.floor(bandwidth * xval.length) - 1; | ||
for (var i in xval) { | ||
var x = xval[i]; | ||
if (i > 0) { | ||
if (right < xval.length - 1 && xval[right + 1] - xval[i] < xval[i] - xval[left]) { | ||
left++; | ||
right++; | ||
} | ||
} | ||
//console.debug("left: "+left + " right: " + right ); | ||
var edge = void 0; | ||
if (xval[i] - xval[left] > xval[right] - xval[i]) { | ||
edge = left; | ||
} else { | ||
edge = right; | ||
} | ||
var denom = Math.abs(1.0 / (xval[edge] - x)); | ||
var sumWeights = 0; | ||
var sumX = 0, | ||
sumXSquared = 0, | ||
sumY = 0, | ||
sumXY = 0; | ||
var k = left; | ||
while (k <= right) { | ||
var xk = xval[k]; | ||
var yk = yval[k]; | ||
var dist = void 0; | ||
if (k < i) { | ||
dist = x - xk; | ||
} else { | ||
dist = xk - x; | ||
} | ||
var w = tricube(dist * denom); | ||
var xkw = xk * w; | ||
sumWeights += w; | ||
sumX += xkw; | ||
sumXSquared += xk * xkw; | ||
sumY += yk * w; | ||
sumXY += yk * xkw; | ||
k++; | ||
} | ||
var meanX = sumX / sumWeights; | ||
//console.debug(meanX); | ||
var meanY = sumY / sumWeights; | ||
var meanXY = sumXY / sumWeights; | ||
var meanXSquared = sumXSquared / sumWeights; | ||
var beta = void 0; | ||
if (meanXSquared == meanX * meanX) { | ||
beta = 0; | ||
} else { | ||
beta = (meanXY - meanX * meanY) / (meanXSquared - meanX * meanX); | ||
} | ||
var alpha = meanY - beta * meanX; | ||
res[i] = beta * x + alpha; | ||
} | ||
//console.debug(res); | ||
return { | ||
equation: "", | ||
points: xval.map(function (x, i) { | ||
return [x, res[i]]; | ||
}), | ||
string: "" | ||
}; | ||
} | ||
//# sourceMappingURL=addTrendLines.js.map |
@@ -74,4 +74,5 @@ 'use strict'; | ||
// DHIS2-1243 add trend lines after sorting | ||
if (layout.regressionType === _addTrendLines.REGRESSION_TYPE_LINEAR) { | ||
config.series = (0, _addTrendLines2.default)(config.series, isStacked); | ||
// trend line on pie and gauge does not make sense | ||
if (layout.type !== CHART_TYPE_GAUGE && layout.type !== CHART_TYPE_PIE && layout.regressionType !== 'NONE') { | ||
config.series = (0, _addTrendLines2.default)(layout.regressionType, config.series, isStacked); | ||
} | ||
@@ -78,0 +79,0 @@ |
{ | ||
"name": "d2-charts-api", | ||
"version": "27.0.9", | ||
"version": "27.0.10", | ||
"description": "DHIS2 charts api", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -1,9 +0,6 @@ | ||
import { fitData } from '../../../util/regression/jqplot_regression'; | ||
import { rgb } from 'd3-color'; | ||
import getStackedData from './getStackedData'; | ||
export const REGRESSION_TYPE_LINEAR = 'LINEAR'; | ||
const DEFAULT_TRENDLINE = { | ||
type: 'line', | ||
type: 'spline', | ||
name: 'Trend', | ||
@@ -14,2 +11,3 @@ dashStyle: 'solid', | ||
marker: { | ||
enabled: false, | ||
symbol: 'circle', | ||
@@ -20,11 +18,31 @@ radius: 2 | ||
function getAdaptedRegressionData(data) { | ||
return data.map(array => array[1]); | ||
} | ||
export default function (regressionType, series, isStacked) { | ||
const newSeries = []; | ||
function getRegressionData(data, isClean) { | ||
const adaptedRegressionData = getAdaptedRegressionData(fitData(data).data); | ||
let i = 0; | ||
if (isStacked) { | ||
newSeries.push( | ||
...series, | ||
Object.assign( | ||
{}, | ||
getRegressionObj(getStackedData(series), regressionType), | ||
) | ||
); | ||
} | ||
else { | ||
series.forEach(seriesObj => { | ||
newSeries.push( | ||
seriesObj, | ||
Object.assign( | ||
{}, | ||
getRegressionObj(seriesObj.data, regressionType), | ||
{ | ||
name: seriesObj.name + ' (trend)', | ||
color: getDarkerColor(seriesObj.color), | ||
} | ||
) | ||
); | ||
}); | ||
} | ||
return isClean ? adaptedRegressionData : data.map((value, index) => value === null ? value : adaptedRegressionData[i++]); | ||
return newSeries; | ||
} | ||
@@ -40,23 +58,435 @@ | ||
export default function (series, isStacked) { | ||
if (isStacked) { | ||
return [ | ||
...series, | ||
Object.assign({}, DEFAULT_TRENDLINE, { | ||
data: getRegressionData(getStackedData(series, true)) | ||
}) | ||
]; | ||
function getRegressionData(data) { | ||
return data.map((value, index) => { | ||
return [index, value]; | ||
}); | ||
} | ||
function getRegressionObj(data, regressionType) { | ||
// LINEAR: | ||
// - decimalPlaces (default = 2) | ||
// LOESS: | ||
// - loessSmooth (default = 25) | ||
// POLYNOMIAL: | ||
// - order (default = 2) | ||
// - extrapolate (default = 0) | ||
let regression; | ||
let regressionTypeOptions = {}; | ||
switch (regressionType) { | ||
case 'LINEAR': | ||
// linear(data, decimalPlaces) | ||
regression = linear(getRegressionData(data)); | ||
regressionTypeOptions.type = 'line'; | ||
break; | ||
case 'POLYNOMIAL': | ||
// polynomial(data, order, extrapolate) | ||
regression = polynomial(getRegressionData(data), 2, 0); | ||
break; | ||
case 'LOESS': | ||
// loess(data, smoothing) | ||
regression = loess(getRegressionData(data), 0.25); | ||
break; | ||
}; | ||
return Object.assign( | ||
{}, | ||
DEFAULT_TRENDLINE, | ||
regressionTypeOptions, | ||
{ | ||
data: regression.points | ||
} | ||
); | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function gaussianElimination(a, o) { | ||
let maxrow = 0, tmp = 0, n = a.length - 1, x = new Array(o); | ||
for (let i = 0; i < n; i++) { | ||
maxrow = i; | ||
for (let j = i + 1; j < n; j++) { | ||
if (Math.abs(a[i][j]) > Math.abs(a[i][maxrow])) { | ||
maxrow = j; | ||
} | ||
} | ||
for (let k = i; k < n + 1; k++) { | ||
tmp = a[k][i]; | ||
a[k][i] = a[k][maxrow]; | ||
a[k][maxrow] = tmp; | ||
} | ||
for (let j = i + 1; j < n; j++) { | ||
for (let k = n; k >= i; k--) { | ||
a[k][j] -= a[k][i] * a[i][j] / a[i][i]; | ||
} | ||
} | ||
} | ||
else { | ||
const newSeries = []; | ||
series.forEach(seriesObj => { | ||
newSeries.push(seriesObj, Object.assign({}, DEFAULT_TRENDLINE, { | ||
color: getDarkerColor(seriesObj.color), | ||
data: getRegressionData(seriesObj.data) | ||
})); | ||
}); | ||
for (let j = n - 1; j >= 0; j--) { | ||
tmp = 0; | ||
return newSeries; | ||
for (let k = j + 1; k < n; k++) { | ||
tmp += a[k][j] * x[k]; | ||
} | ||
x[j] = (a[n][j] - tmp) / a[j][j]; | ||
} | ||
return (x); | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
// Human readable formulas: | ||
// | ||
// N * Σ(XY) - Σ(X) | ||
// intercept = --------------------- | ||
// N * Σ(X^2) - Σ(X)^2 | ||
// | ||
// correlation = N * Σ(XY) - Σ(X) * Σ (Y) / √ ( N * Σ(X^2) - Σ(X) ) * ( N * Σ(Y^2) - Σ(Y)^2 ) ) ) | ||
function linear(data, decimalPlaces) { | ||
let sum = [0, 0, 0, 0, 0], results = [], N = data.length; | ||
for (let n = 0, len = data.length; n < len; n++) { | ||
if (data[n]['x'] != null) { | ||
data[n][0] = data[n].x; | ||
data[n][1] = data[n].y; | ||
} | ||
if (data[n][1] != null) { | ||
sum[0] += data[n][0]; // Σ(X) | ||
sum[1] += data[n][1]; // Σ(Y) | ||
sum[2] += data[n][0] * data[n][0]; // Σ(X^2) | ||
sum[3] += data[n][0] * data[n][1]; // Σ(XY) | ||
sum[4] += data[n][1] * data[n][1]; // Σ(Y^2) | ||
} else { | ||
N -= 1; | ||
} | ||
} | ||
let gradient = (N * sum[3] - sum[0] * sum[1]) / (N * sum[2] - sum[0] * sum[0]); | ||
let intercept = (sum[1] / N) - (gradient * sum[0]) / N; | ||
// let correlation = (N * sum[3] - sum[0] * sum[1]) / Math.sqrt((N * sum[2] - sum[0] * sum[0]) * (N * sum[4] - sum[1] * sum[1])); | ||
for (let i = 0, len = data.length; i < len; i++) { | ||
let coorY = data[i][0] * gradient + intercept; | ||
if (decimalPlaces) { | ||
coorY = parseFloat(coorY.toFixed(decimalPlaces)); | ||
} | ||
let coordinate = [data[i][0], coorY]; | ||
results.push(coordinate); | ||
} | ||
results.sort((a, b) => { | ||
if (a[0] > b[0]) { return 1; } | ||
if (a[0] < b[0]) { return -1; } | ||
return 0; | ||
}); | ||
let string = 'y = ' + Math.round(gradient * 100) / 100 + 'x + ' + Math.round(intercept * 100) / 100; | ||
return { | ||
equation: [gradient, intercept], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function logarithmic(data) { | ||
let sum = [0, 0, 0, 0], results = [], mean = 0; | ||
for (let n = 0, len = data.length; n < len; n++) { | ||
if (data[n].x != null) { | ||
data[n][0] = data[n].x; | ||
data[n][1] = data[n].y; | ||
} | ||
if (data[n][1] != null) { | ||
sum[0] += Math.log(data[n][0]); | ||
sum[1] += data[n][1] * Math.log(data[n][0]); | ||
sum[2] += data[n][1]; | ||
sum[3] += Math.pow(Math.log(data[n][0]), 2); | ||
} | ||
} | ||
let B = (n * sum[1] - sum[2] * sum[0]) / (n * sum[3] - sum[0] * sum[0]); | ||
let A = (sum[2] - B * sum[0]) / n; | ||
for (let i = 0, len = data.length; i < len; i++) { | ||
let coordinate = [data[i][0], A + B * Math.log(data[i][0])]; | ||
results.push(coordinate); | ||
} | ||
results.sort((a, b) => { | ||
if (a[0] > b[0]) { return 1; } | ||
if (a[0] < b[0]) { return -1; } | ||
return 0; | ||
}); | ||
let string = 'y = ' + Math.round(A * 100) / 100 + ' + ' + Math.round(B * 100) / 100 + ' ln(x)'; | ||
return { | ||
equation: [A, B], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function power(data) { | ||
let sum = [0, 0, 0, 0], results = []; | ||
for (let n = 0, len = data.length; n < len; n++) { | ||
if (data[n].x != null) { | ||
data[n][0] = data[n].x; | ||
data[n][1] = data[n].y; | ||
} | ||
if (data[n][1] != null) { | ||
sum[0] += Math.log(data[n][0]); | ||
sum[1] += Math.log(data[n][1]) * Math.log(data[n][0]); | ||
sum[2] += Math.log(data[n][1]); | ||
sum[3] += Math.pow(Math.log(data[n][0]), 2); | ||
} | ||
} | ||
let B = (n * sum[1] - sum[2] * sum[0]) / (n * sum[3] - sum[0] * sum[0]); | ||
let A = Math.pow(Math.E, (sum[2] - B * sum[0]) / n); | ||
for (let i = 0, len = data.length; i < len; i++) { | ||
let coordinate = [data[i][0], A * Math.pow(data[i][0] , B)]; | ||
results.push(coordinate); | ||
} | ||
results.sort((a,b) => { | ||
if (a[0] > b[0]) { return 1; } | ||
if (a[0] < b[0]) { return -1; } | ||
return 0; | ||
}); | ||
let string = 'y = ' + Math.round(A * 100) / 100 + 'x^' + Math.round(B * 100) / 100; | ||
return { | ||
equation: [A, B], | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// Code extracted from https://github.com/Tom-Alexander/regression-js/ | ||
function polynomial(data, order, extrapolate) { | ||
if (typeof order == 'undefined') { | ||
order = 2; | ||
} | ||
let lhs = [], rhs = [], results = [], a = 0, b = 0, k = order + 1; | ||
for (let i = 0; i < k; i++) { | ||
for (let l = 0, len = data.length; l < len; l++) { | ||
if (data[l].x != null) { | ||
data[l][0] = data[l].x; | ||
data[l][1] = data[l].y; | ||
} | ||
if (data[l][1] != null) { | ||
a += Math.pow(data[l][0], i) * data[l][1]; | ||
} | ||
} | ||
lhs.push(a); | ||
a = 0; | ||
let c = []; | ||
for (let j = 0; j < k; j++) { | ||
for (let l = 0, len = data.length; l < len; l++) { | ||
if (data[l][1]) { | ||
b += Math.pow(data[l][0], i + j); | ||
} | ||
} | ||
c.push(b); | ||
b = 0; | ||
} | ||
rhs.push(c); | ||
} | ||
rhs.push(lhs); | ||
let equation = gaussianElimination(rhs, k); | ||
let resultLength = data.length + extrapolate; | ||
let step = data[data.length - 1][0] - data[data.length - 2][0]; | ||
for (let i = 0, len = resultLength; i < len; i++) { | ||
let answer = 0, x = 0; | ||
if (typeof data[i] !== 'undefined') { | ||
x = data[i][0]; | ||
} else { | ||
x = data[data.length - 1][0] + (i - data.length) * step; | ||
} | ||
for (let w = 0; w < equation.length; w++) { | ||
answer += equation[w] * Math.pow(x, w); | ||
} | ||
results.push([x, answer]); | ||
} | ||
results.sort((a,b) => { | ||
if (a[0] > b[0]) { return 1; } | ||
if (a[0] < b[0]) { return -1; } | ||
return 0; | ||
}); | ||
let string = 'y = '; | ||
for (let i = equation.length-1; i >= 0; i--) { | ||
if (i > 1) { | ||
string += Math.round(equation[i] * 100) / 100 + 'x^' + i + ' + '; | ||
} | ||
else if (i == 1) { | ||
string += Math.round(equation[i] * 100) / 100 + 'x' + ' + '; | ||
} | ||
else { | ||
string += Math.round(equation[i] * 100) / 100; | ||
} | ||
} | ||
return { | ||
equation: equation, | ||
points: results, | ||
string: string | ||
}; | ||
} | ||
// @author: Ignacio Vazquez | ||
// Based on | ||
// - http://commons.apache.org/proper/commons-math/download_math.cgi LoesInterpolator.java | ||
// - https://gist.github.com/avibryant/1151823 | ||
function loess(data, bandwidth) { | ||
bandwidth = bandwidth || 0.25 ; | ||
let xval = data.map(pair => { return pair[0]; }); | ||
let distinctX = array_unique(xval); | ||
if (2 / distinctX.length > bandwidth) { | ||
bandwidth = Math.min(2 / distinctX.length, 1); | ||
console.warn("updated bandwith to " + bandwidth); | ||
} | ||
let yval = data.map(pair => { return pair[1]; }); | ||
function array_unique(values) { | ||
let o = {}, i, l = values.length, r = []; | ||
for (i = 0; i < l; i += 1) { | ||
o[values[i]] = values[i]; | ||
} | ||
for (i in o) { | ||
r.push(o[i]); | ||
} | ||
return r; | ||
} | ||
function tricube(x) { | ||
let tmp = 1 - x * x * x; | ||
return tmp * tmp * tmp; | ||
} | ||
let res = []; | ||
let left = 0; | ||
let right = Math.floor(bandwidth * xval.length) - 1; | ||
for (let i in xval) { | ||
let x = xval[i]; | ||
if (i > 0) { | ||
if (right < xval.length - 1 && | ||
xval[right+1] - xval[i] < xval[i] - xval[left]) { | ||
left++; | ||
right++; | ||
} | ||
} | ||
//console.debug("left: "+left + " right: " + right ); | ||
let edge; | ||
if (xval[i] - xval[left] > xval[right] - xval[i]) { | ||
edge = left; | ||
} | ||
else { | ||
edge = right; | ||
} | ||
let denom = Math.abs(1.0 / (xval[edge] - x)); | ||
let sumWeights = 0; | ||
let sumX = 0, sumXSquared = 0, sumY = 0, sumXY = 0; | ||
let k = left; | ||
while (k <= right) { | ||
let xk = xval[k]; | ||
let yk = yval[k]; | ||
let dist; | ||
if (k < i) { | ||
dist = (x - xk); | ||
} else { | ||
dist = (xk - x); | ||
} | ||
let w = tricube(dist * denom); | ||
let xkw = xk * w; | ||
sumWeights += w; | ||
sumX += xkw; | ||
sumXSquared += xk * xkw; | ||
sumY += yk * w; | ||
sumXY += yk * xkw; | ||
k++; | ||
} | ||
let meanX = sumX / sumWeights; | ||
//console.debug(meanX); | ||
let meanY = sumY / sumWeights; | ||
let meanXY = sumXY / sumWeights; | ||
let meanXSquared = sumXSquared / sumWeights; | ||
let beta; | ||
if (meanXSquared == meanX * meanX) { | ||
beta = 0; | ||
} | ||
else { | ||
beta = (meanXY - meanX * meanY) / (meanXSquared - meanX * meanX); | ||
} | ||
let alpha = meanY - beta * meanX; | ||
res[i] = beta * x + alpha; | ||
} | ||
//console.debug(res); | ||
return { | ||
equation: "" , | ||
points: xval.map((x,i) => { return [x, res[i]]; }), | ||
string: "" | ||
}; | ||
} |
@@ -14,3 +14,3 @@ import objectClean from 'd2-utilizr/lib/objectClean'; | ||
import getTrimmedConfig from './getTrimmedConfig'; | ||
import addTrendLines, { REGRESSION_TYPE_LINEAR } from './addTrendLines'; | ||
import addTrendLines from './addTrendLines'; | ||
@@ -80,4 +80,5 @@ export const CHART_TYPE_PIE = 'pie'; | ||
// DHIS2-1243 add trend lines after sorting | ||
if (layout.regressionType === REGRESSION_TYPE_LINEAR) { | ||
config.series = addTrendLines(config.series, isStacked); | ||
// trend line on pie and gauge does not make sense | ||
if (layout.type !== CHART_TYPE_GAUGE && layout.type !== CHART_TYPE_PIE && layout.regressionType !== 'NONE') { | ||
config.series = addTrendLines(layout.regressionType, config.series, isStacked); | ||
} | ||
@@ -84,0 +85,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
208065
3045
119