Skip to content

Instantly share code, notes, and snippets.

@Kasahs
Last active January 13, 2018 13:24
Show Gist options
  • Save Kasahs/c1bb60ee4bd2a1cdc21da85c638a50d5 to your computer and use it in GitHub Desktop.
Save Kasahs/c1bb60ee4bd2a1cdc21da85c638a50d5 to your computer and use it in GitHub Desktop.
sampling by relative frequency distribution
/**
* given a sorted list and a number to search returns the right insertion index to maintain sorted order
* @param list {number[]}
* @param x {number}
*/
const bisectRight = (list, x) => {
if(list.length === 0) {
return 0
}
let first = 0
let last = list.length - 1
let mid = 0
while (first<=last){
mid = Math.floor((last + first) / 2)
if(list[mid] === x) {
return mid + 1
}
if(x < list[mid] ){
last = mid - 1
} else if(x > list[mid]){
first = mid + 1
}
}
if(x < list[mid]){
return mid
}
return mid + 1
}
/**
* return a cumulative sum array for given array
* @param {*} arr {number[]}
* @param {*} precision {number}
*/
const cumsum = (arr, precision) => {
let list = arr.slice()
for(let i = 0; i < list.length; i++){
if(i > 0) {
list[i] = parseFloat((list[i] + list[i - 1]).toPrecision(precision || 10))
}
}
return list
}
/**
* sample from labels based on given relative frequency distribution array
* @param {*} labels {any[]}
* @param {*} pd {number[]}
*/
const samplepd = (labels, pd) => {
if(labels.length !== pd.length) {
throw(new Error("labels.length doesn't match pd.length"))
}
let isInValidPd = pd.reduce((a,b) => {return a+b}) !== 1
if(isInValidPd) {
throw(new Error("invalid distribution: distribution doesn't reduce to sum 1.0"))
}
let cumpb = cumsum(pd, 10)
let draw = Math.random()
let idx = bisectRight(cumpb, draw)
return labels[idx]
}
module.exports = {
samplepd,
bisectRight,
cumsum
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment