ecclesia
Version:
Framework for political and electoral simulations
55 lines • 2.36 kB
JavaScript
import { enumerate, max } from "@gouvernathor/python";
import { DefaultMap, NumberCounter } from "@gouvernathor/python/collections";
import { fmean, median } from "@gouvernathor/python/statistics";
import { Scores } from "../tally";
/**
* Creates an attribution method in which all the seats go to the candidate with the highest average score.
*
* The ballots are not required to grade all the candidates.
*/
export function averageScore({ nSeats }) {
const attrib = (votes, _rest = {}) => {
const counts = new DefaultMap(() => []);
for (const [party, grades] of votes) {
for (const [grade, qty] of enumerate(grades)) {
counts.get(party).push(...Array(qty).fill(grade));
}
}
return NumberCounter.fromEntries([[max(counts.keys(), party => fmean(counts.get(party))), nSeats]]);
};
attrib.nSeats = nSeats;
return attrib;
}
/**
* Creates an attribution method in which all the seats go to the candidate with the highest median score.
*
* If there is a tie, the contingency method is called on the candidates that are tied.
* The default contingency is to take the maximum average score.
*
* The ballots are not required to grade all the candidates.
*/
export function medianScore({ nSeats, contingency }) {
if (contingency === undefined) {
contingency = averageScore({ nSeats });
}
const attrib = (votes, rest = {}) => {
const counts = new DefaultMap(() => []);
for (const [party, grades] of votes) {
for (const [grade, qty] of enumerate(grades)) {
counts.get(party).push(...Array(qty).fill(grade));
}
}
const medians = new Map(Array.from(counts.entries(), ([party, partigrades]) => [party, median(partigrades)]));
const winScore = Math.max(...medians.values());
const [winner, ...winners] = [...medians.keys()].filter(p => medians.get(p) === winScore);
if (winners.length === 0) { // no tie
return NumberCounter.fromEntries([[winner, nSeats]]);
}
winners.unshift(winner);
const trimmedResults = Scores.fromEntries(winners.map(party => [party, counts.get(party)]));
return contingency(trimmedResults, rest);
};
attrib.nSeats = nSeats;
return attrib;
}
//# sourceMappingURL=scoreFactory.js.map