function calculateLowessFit(x, y, alpha, inc, residuals) {
const k = Math.floor(x.length * alpha);
const sortedX = x.slice();
sortedX.sort((a, b) => b - a);
const xMax = d3Array.quantile(sortedX, 0.98);
const xMin = d3Array.quantile(sortedX, 0.02);
const xy = d3Array.zip(x, y, residuals).sort();
const size = Math.abs(xMax - xMin) / inc;
const smallest = xMin;
const largest = xMax;
const xProto = d3Array.range(smallest, largest, size);
const yProto = [];
for (let i = 0; i < xProto.length; i++) {
const ix = xProto[i];
let xiNeighbors = xy
.map(xyi => [Math.abs(xyi[0] - ix), xyi[0], xyi[1], xyi[2]])
.sort()
.slice(0, k);
const iDelta = d3Array.max(xiNeighbors)[0];
xiNeighbors = xiNeighbors.map(wxy => ({
w: this.tricubeWeight(wxy[0] / iDelta) * wxy[3],
x: wxy[1],
y: wxy[2]
}));
const { x0, beta } = this.weightedLeastSquares(xiNeighbors);
yProto.push(x0 + beta * ix);
}
return { x: xProto, y: yProto };
}