comparative-judgement
Version:
Comparative Judgement Algorithms
185 lines (167 loc) • 4.13 kB
JavaScript
/* jshint -W024, expr:true */
/*jslint node: true */
/*global expect, fx, sinon*/
;
var _ = require('underscore');
var factorial = function (num) {
// If the number is less than 0, reject it.
if (num < 0) {
return -1;
}
// If the number is 0, its factorial is 1.
else if (num === 0) {
return 1;
}
// Otherwise, call this recursive procedure again.
else {
return (num * factorial(num - 1));
}
};
var rasch = function (ability, difficulty) {
var ex = Math.exp(ability - difficulty);
return ex / (1 + ex);
};
var average = function (a) {
var r = {
mean: 0,
variance: 0,
deviation: 0
},
t = a.length;
for (var m, s = 0, l = t; l--; s += a[l]);
for (m = r.mean = s / t, l = t, s = 0; l--; s += Math.pow(a[l] - m, 2));
return r.deviation = Math.sqrt(r.variance = s / t), r;
};
var info = function (p) {
return p * (1 - p);
};
var zsq = function (p) {
var res = 1 - p;
return Math.pow(res, 2) / info(p);
};
var wms = function (probs) {
if (probs.length > 0) {
var numer = 0;
var denom = 0;
for (var i = 0; i < probs.length; i++) {
var inf = info(probs[i]);
numer += inf * zsq(probs[i]);
denom += inf;
}
return (numer / denom);
} else {
return 0;
}
};
function k_combinations(set, k) {
var i, j, combs, head, tailcombs;
if (k > set.length || k <= 0) {
return [];
}
if (k == set.length) {
return [set];
}
if (k == 1) {
combs = [];
for (i = 0; i < set.length; i++) {
combs.push([set[i]]);
}
return combs;
}
// Assert {1 < k < set.length}
combs = [];
for (i = 0; i < set.length - k + 1; i++) {
head = set.slice(i, i + 1);
tailcombs = k_combinations(set.slice(i + 1), k - 1);
for (j = 0; j < tailcombs.length; j++) {
combs.push(head.concat(tailcombs[j]));
}
}
return combs;
}
var infoPR = function (info, wq) {
// Weighted probabilities of selection
var iPR = _.map(info, function (ii) {
return Math.pow(ii, wq);
});
var totalInfoPR = _.reduce(iPR, function (memo, num) {
return memo + num;
}, 0);
//if totalInfoPR == 0
//then no information
//w is probably wrong
//choose pair with most info
//will throw a divide by zero error
//fix by not letting w be wrong
var probSelect = _.map(iPR, function (i) {
return i / totalInfoPR;
});
//console.log(probSelect);
return probSelect;
};
var iI = function (theta, trueScores) {
//Get probabiltities for every item
var Ps = _.map(trueScores, function (trueScore) {
return rasch(theta, trueScore);
});
//Convert probabilities into information
var ii = _.map(Ps, function (p) {
return (info(p));
});
return ii;
};
var wq = function (itemsAdministered, itemsExpected, AP) {
var rng = _.range(1, itemsAdministered + 1);
var numerator = _.reduce(rng, function (memo, num) {
return memo + (Math.pow(num, AP));
}, 0);
rng = _.range(1, itemsExpected);
var denominator = _.reduce(rng, function (memo, num) {
return memo + (Math.pow(num, AP));
}, 0);
var wwq = itemsExpected * numerator / denominator;
return wwq;
};
var calciPR = function (theta, items, done, thr, AP) {
// Progressive selection probabilities
if(done>=thr) done=thr-1;
var info = iI(theta, items);
//console.log('info: ',info);
//console.log('done: ',done, 'thr: ',thr, 'AP: ',AP);
var w = wq(done, thr, AP);
// console.log('w: ',w);
var iPr = infoPR(info, w);
//console.log(iPr);
return iPr;
};
var sampleVector = function (array, runif) {
//Take a cumulative sum of the vector
var rnd = runif;
var cumulative = 0;
var sums = _.map(array, function (num) {
cumulative += num;
return cumulative;
});
sums[sums.length-1] = 1.1;
// Break
for (var i = 0; i < sums.length; i++) {
if (sums[i] > rnd) {
break;
}
}
return i;
};
module.exports = {
sampleVector: sampleVector,
calciPR: calciPR,
infoPR: infoPR,
iI: iI,
wq: wq,
factorial: factorial,
rasch: rasch,
average: average,
info: info,
zsq: zsq,
wms: wms,
k_combinations: k_combinations,
};