fullfact
Version:
Create full-factorial DOE designs
70 lines (60 loc) • 1.8 kB
JavaScript
const R = require("ramda");
function zeros2d(m, n) {
return R.map(() => R.repeat(0, n), R.range(0, m));
}
const fullfact = (levels) => {
// check input argument
if (
!Array.isArray(levels) ||
!levels.reduce((a, c) => a && Number.isInteger(c), true)
) {
throw new Error("Argument must be an array of integers.");
}
const nFactors = levels.length;
const nbLines = levels.reduce((accum, curr) => accum * curr, 1);
const H = zeros2d(nbLines, nFactors);
let levelRepeat = 1;
let rangeRepeat = levels.reduce((a, c) => a * c, 1);
for (let i of R.range(0, nFactors)) {
rangeRepeat = Math.floor(rangeRepeat / levels[i]);
const lvl = R.flatten(
R.map((x) => R.repeat(x, levelRepeat), R.range(0, levels[i]))
);
const rng = R.flatten(R.repeat(lvl, rangeRepeat));
for (let k of R.range(0, nbLines)) {
H[k][i] = rng[k];
}
levelRepeat *= levels[i];
}
return H;
};
module.exports.fullfact = fullfact;
const hydratedFullfact = (factorMatrix) => {
// check input argument
let inputCheckPass = false;
if (
typeof factorMatrix === "object" &&
!Array.isArray(factorMatrix) &&
factorMatrix !== null
) {
inputCheckPass = Object.keys(factorMatrix).reduce(
(a, key) => a && Array.isArray(factorMatrix[key]),
true
);
}
if (!inputCheckPass) {
throw new Error(
"Argument must be an object of the form { [key: string]: any[] }."
);
}
const keyList = Object.keys(factorMatrix);
const levels = keyList.map((key) => factorMatrix[key].length);
const doe = fullfact(levels);
return doe.map((x) => {
return keyList.reduce((a, key, idx) => {
a[key] = factorMatrix[key][x[idx]];
return a;
}, {});
});
};
module.exports.hydratedFullfact = hydratedFullfact;