in src/converter/data/dataSmooth.ts [55:126]
public convert(points: IDataRepresentationPoint[]): IDataRepresentationPoint[] {
const length = points.length;
const bandwidthInPoints = Math.floor(this.bandwidth * length);
const resultPoints: IDataRepresentationPoint[] = [];
const residuals: number[] = [];
const robustnessWeights: number[] = [];
for (let i: number = 0; i < length; i++) {
residuals[i] = 0;
robustnessWeights[i] = 1;
}
let alphaBeta: IAlphaBeta;
for (let iter: number = 0; iter < this.robustnessIters; iter++) {
const bandwidthInterval: number[] = [0, bandwidthInPoints - 1];
for (let i: number = 0; i < length; i++) {
const x: number = points[i].x.getTime();
if (i > 0) {
this.science_stats_loessUpdateBandwidthInterval(points, i, bandwidthInterval);
}
const ileft: number = bandwidthInterval[0];
const iright: number = bandwidthInterval[1];
if (!isNaN(ileft)
&& !isNaN(iright)
&& ileft !== iright
&& ileft >= 0
&& iright >= 0
) {
alphaBeta = this.calcAlphaBeta(robustnessWeights, i, x, ileft, iright, points);
if (!isNaN(alphaBeta.beta) && !isNaN(alphaBeta.alpha)) {
resultPoints[i] = {
...points[i],
y: alphaBeta.beta * x + alphaBeta.alpha,
};
} else {
resultPoints[i] = { ...points[i] };
}
residuals[i] = Math.abs(points[i].y - resultPoints[i].y);
} else {
resultPoints[i] = { ...points[i] };
}
}
if (iter === this.robustnessIters) {
break;
}
const sortedResiduals: number[] = residuals
.slice()
.sort();
const medianResidual: number = sortedResiduals[Math.floor(length / 2)];
if (Math.abs(medianResidual) < this.accuracy) {
break;
}
for (let i: number = 0; i < length; i++) {
const arg: number = residuals[i] / (6 * medianResidual);
robustnessWeights[i] = (arg >= 1) ? 0 : ((alphaBeta.w = 1 - arg * arg) * alphaBeta.w);
}
}
return resultPoints;
}