Published
Edited
Sep 20, 2019
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// NMB method to determine frontier roughly adapted from Suen and Goldhaber-Fiebert's article
// Admitly this may not be an optimal implementation
// I've preserved the variable names to match the original R source code for easy comparison
function frontierByNMB(data, maxWTP) {
const CEmat = data;
let WTPtestPoints = [0];
let ICERmat = CEmat.forEach((d, i) =>
CEmat.forEach((e, j) => {
if (j >= i) return; // avoid redundant calcs
let wtp = (d.cost - e.cost) / (d.benefit - e.benefit);
if (
wtp > 0 &&
isFinite(wtp) &&
WTPtestPoints.indexOf(wtp) < 0 && // unique
wtp <= maxWTP
) {
WTPtestPoints.push(wtp);
}
})
);
WTPtestPoints = WTPtestPoints.sort((a, b) => a - b);

let NMBmat = [];
CEmat.forEach(d =>
NMBmat.push(WTPtestPoints.map(e => e * d.benefit - d.cost))
);

let NMBmatTransposed = d3.transpose(NMBmat);
let maxVals = NMBmatTransposed.map(arr => d3.max(arr));
let indiciesOfMax = [];
NMBmatTransposed.forEach((arr, i) => {
arr.forEach((d, j) => {
if (d == maxVals[i] && indiciesOfMax.indexOf(j) < 0)
indiciesOfMax.push(j);
});
});
return indiciesOfMax;
}
Insert cell
// convex hull method to determine frontier
function frontierByHull(data, maxWTP) {
const points = data.map((d, i) => [d.cost, d.benefit, d.index]);
const initial_hull = d3.polygonHull(points);
// rehydrate point info into an object
let hull = initial_hull.map((d, i) => {
return _.assign({
cost: d[0],
benefit: d[1],
original_index: d[2],
hull_index: i,
on_frontier: false
});
});

// Slice the list of points so that we start with the point with the smallest cost
// e.g. if array = [1,2,3,4,5,6] and min_cost_index is 3 --> [3,4,5,6,1,2]
const min_cost_index = _.minBy(hull, 'cost').hull_index;
hull = hull.slice(min_cost_index).concat(hull.slice(0, min_cost_index));

// Iterate through the points on the hull in order
// Stop if wtp is > maxWTP or if benefit is less than benefit of previous point
let previous_benefit = -Infinity;
let previous_cost = 0;
hull.forEach(d => {
let wtp = (d.cost - previous_cost) / (d.benefit - previous_benefit);
if (d.benefit >= previous_benefit && wtp <= maxWTP) {
d.on_frontier = true;
previous_cost = d.cost;
previous_benefit = d.benefit;
}
});
// return original point index for each point on the frontier
return hull.filter(d => d.on_frontier).map(d => d.original_index);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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