Skip to content

Instantly share code, notes, and snippets.

@dlmanning
Last active April 18, 2020 22:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dlmanning/04b117b8a81dfcf64f14a37a6f398118 to your computer and use it in GitHub Desktop.
Save dlmanning/04b117b8a81dfcf64f14a37a6f398118 to your computer and use it in GitHub Desktop.
Runs a simulation of doing the Standford Santa Clara County serological survey 30000 times against a given population
Hypothetical distribution of study results in a population with 0% prevalence using a test with 98.5% selectivity
[0.0078, 0.0084]: |
[0.0084, 0.0090]: |
[0.0090, 0.0096]: ||
[0.0096, 0.0102]: |||||
[0.0102, 0.0108]: |||||||||
[0.0108, 0.0114]: ||||||||||||||||||
[0.0114, 0.0120]: ||||||||||||||||||||||||||||||||
[0.0120, 0.0126]: ||||||||||||||||||||||||||||||||||||||||||||||||
[0.0126, 0.0132]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0132, 0.0138]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0138, 0.0144]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0144, 0.0150]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0150, 0.0156]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0156, 0.0162]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0162, 0.0168]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0168, 0.0174]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0174, 0.0180]: |||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0180, 0.0186]: |||||||||||||||||||||||||||||||||||||
[0.0186, 0.0192]: |||||||||||||||||||||||
[0.0192, 0.0198]: |||||||||||||||
[0.0198, 0.0204]: ||||||||
[0.0204, 0.0210]: |||||
[0.0210, 0.0216]: |||
[0.0216, 0.0222]: |
[0.0222, 0.0228]: |
[0.0228, 0.0234]: |
[0.0234, 0.0240]:
[0.0240, 0.0246]:
[0.0252, 0.0258]:
Hypothetical distribution of study results in a population with 1% prevalence using a test with 99.5% selectivity
[0.0069, 0.0075]: |
[0.0075, 0.0081]: |
[0.0081, 0.0087]: ||
[0.0087, 0.0093]: ||||
[0.0093, 0.0099]: ||||||||
[0.0099, 0.0105]: |||||||||||||||||
[0.0105, 0.0111]: |||||||||||||||||||||||||||||
[0.0111, 0.0117]: |||||||||||||||||||||||||||||||||||||||||||||||
[0.0117, 0.0123]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0123, 0.0129]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0129, 0.0135]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0135, 0.0141]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0141, 0.0147]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0147, 0.0153]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0153, 0.0159]: |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0159, 0.0165]: ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0165, 0.0171]: |||||||||||||||||||||||||||||||||||||||||||||||||||||
[0.0171, 0.0177]: ||||||||||||||||||||||||||||||||||||
[0.0177, 0.0183]: |||||||||||||||||||||
[0.0183, 0.0189]: ||||||||||||||
[0.0189, 0.0195]: |||||||||
[0.0195, 0.0201]: ||||
[0.0201, 0.0207]: ||
[0.0207, 0.0213]: |
[0.0213, 0.0219]: |
[0.0219, 0.0225]: |
[0.0225, 0.0231]:
const crypto = require('crypto');
/**
* Extremely simple simulation of possible outcomes of the Santa Clara Country Serological Survey
* see: https://www.medrxiv.org/content/10.1101/2020.04.14.20062463v1
*/
const N = 3330; // Size of Santa Clara County Serological Study
const PREVALANCE = 0.01; // Hypothetical population prevalance of SARS-CoV-2
const SPECIFICITY = .995; // Estimated test kit specificity
const SENSITIVITY = .918; // Estimated test kit sensitivity
const results = [];
for (let i = 0; i < 30000; i++) {
const study_prevalance = doStudy(N, PREVALANCE, SPECIFICITY, SENSITIVITY);
results.push(study_prevalance);
}
printResults(results, 25);
/**
* @param {Number} n - The number of individuals in the study sample
* @param {Number} prevalance - The actual prevalance in the population
* @param {Number} specificity - Probability the test correctly detects a negative result
* @param {Number} sensitivity - Probability the test correctly detects a positive result;
*/
function doStudy(n, prevalance, specificity, sensitivity) {
const participants = [];
for (let i = 0; i < n; i++) {
// Generate a random sample from the population with the given prevalance
participants.push(random() < prevalance);
}
// Test everyone in the sample
const testedPositive = participants.reduce((total, isReallyPositive) => {
if (isReallyPositive) {
if (random() < sensitivity) {
total += 1; // Correctly concluded a positive individual is positive
} else {
// False negative
}
} else {
if (random() > specificity) {
total += 1; // False positive
} else {
// Correctly concluded a negative individual is negative
}
}
return total;
}, 0);
// Return detected sample prevalance
return testedPositive / n;
}
function printResults(studyResults, bins) {
const numberOfStudies = studyResults.length;
studyResults.sort();
const maxResult = studyResults[numberOfStudies - 1];
const minResult = studyResults[0];
const binWidth = Math.floor((maxResult * 10000 - minResult * 10000) / bins) / 10000;
const results = studyResults.reduce((report, result) => {
const idx = Math.floor((result - minResult) / binWidth);
report[idx] += 1;
return report;
}, Array(bins + 1).fill(0));
let output = '';
results.forEach((result, idx) => {
output += `[${Number((idx) * binWidth + minResult).toFixed(4)}, ${Number((idx + 1) * binWidth + minResult).toFixed(4)}]: `;
for (let i = 0; i < result / 30; i++) {
output += '|';
}
output += '\n';
});
console.log(output);
}
function random() {
return Math.random();
// use this one for better random numbers. Takes a lot longer.
// return crypto.randomBytes(4).readUInt32BE(0) / 2 ** 32
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment