Published
Edited
Mar 27, 2020
Importers
Insert cell
md`# smoothers`
Insert cell
function addLs({ target, data, accessor, xScale, yScale, lsLine }) {
const svg = getSvgChildOf(target);
data = data[0];
const minX = d3Array.min(data, accessor);
const maxX = d3Array.max(data, accessor);

select(target)
.selectAll('.mg-least-squares-line')
.remove();

svg
.append('svg:line')
.attr('x1', xScale(minX))
.attr('x2', xScale(maxX))
.attr('y1', yScale(lsLine.fit(minX)))
.attr('y2', yScale(lsLine.fit(maxX)))
.attr('class', 'mg-least-squares-line');
}
Insert cell
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);

// for each prototype, find its fit.
const yProto = [];

for (let i = 0; i < xProto.length; i++) {
const ix = xProto[i];

// get k closest neighbors.
let xiNeighbors = xy
.map(xyi => [Math.abs(xyi[0] - ix), xyi[0], xyi[1], xyi[2]])
.sort()
.slice(0, k);

// Get the largest distance in the neighbor set.
const iDelta = d3Array.max(xiNeighbors)[0];

// Prepare the weights for mean calculation and WLS.

xiNeighbors = xiNeighbors.map(wxy => ({
w: this.tricubeWeight(wxy[0] / iDelta) * wxy[3],
x: wxy[1],
y: wxy[2]
}));

// Find the weighted least squares, obviously.
const { x0, beta } = this.weightedLeastSquares(xiNeighbors);

yProto.push(x0 + beta * ix);
}

return { x: xProto, y: yProto };
}
Insert cell
Insert cell
function weightedLeastSquares(wxy) {
const { xBar, yBar } = this.weightedMeans(wxy);
const beta = this.weightedBeta(wxy, xBar, yBar);
const x0 = yBar - beta * xBar;

return { beta, xBar, yBar, x0 };
}
Insert cell
function weightedBeta(wxy, xBar, yBar) {
const num = d3Array.sum(
wxy.map(el => Math.pow(el.w, 2) * (el.x - xBar) * (el.y - yBar))
);
const denom = d3Array.sum(
wxy.map(el => Math.pow(el.w, 2) * Math.pow(el.x - xBar, 2))
);

return num / denom;
}
Insert cell
function weightedMeans(wxy) {
const wSum = d3Array.sum(wxy.map(el => el.w));

return {
xBar: d3Array.sum(wxy.map(el => el.w * el.x)) / wSum,
yBar: d3Array.sum(wxy.map(el => el.w * el.y)) / wSum
};
}
Insert cell
function manhattan(x1, x2) {
return Math.abs(x1 - x2);
}
Insert cell
function neighborHoodWidth(x0, xis) {
return d3Array.max(xis, xi => Math.abs(x0, xi));
}
Insert cell
function tricubeWeight(u) {
return powWeight(u, 3);
}
Insert cell
function bisquareWeight(u) {
return powWeight(u, 2);
}
Insert cell
function powWeight(u, w) {
return u >= 0 && u <= 1 ? Math.pow(1 - Math.pow(u, w), w) : 0;
}
Insert cell
function leastSquares(x_, y_) {
const x = x_[0] instanceof Date ? x_.map(d => d.getTime()) : x_;
const y = y_[0] instanceof Date ? y_.map(d => d.getTime()) : y_;

const xHat = d3Array.mean(x);
const yHat = d3Array.mean(y);
let numerator = 0;
let denominator = 0;

for (let i = 0; i < x.length; i++) {
const xi = x[i];
const yi = y[i];
numerator += (xi - xHat) * (yi - yHat);
denominator += (xi - xHat) * (xi - xHat);
}

const beta = numerator / denominator;
const x0 = yHat - beta * xHat;

return {
x0,
beta,
fit: d => x0 + x * beta
};
}
Insert cell
function addLowess({ target, lowessLine, xScale, yScale, interpolate }) {
const svg = getSvgChildOf(target);
const lowess = lowessLine;

const lineGenerator = line()
.x(xScale)
.y(yScale)
.interpolate(interpolate);

svg
.append('path')
.attr('d', lineGenerator(lowess))
.classed('mg-lowess-line', true);
}
Insert cell
d3Array = require('d3-array')
Insert cell
select = require('d3-selection')
Insert cell
line = require('d3-shape')
Insert cell
import { getSvgChildOf } from '@neatlogic/utility'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more