UNPKG

bwsample

Version:

The JS implementation of the pypi package bwsample

220 lines (196 loc) 6.48 kB
'use strict'; /** * Sample BWS sets from a list of examples * * @param {Array[DATA]} examples A list of examples * @param {int} n_items Number items per BWS set * @param {String} method 'overlap' or 'twice' * @param {Boolean} shuffle Flag to permute/shuffle indices * * @returns {Array[Array[DATA]]} samples. A list of BWS sets. Each BWS set * is a list of n_items sampled examples * * Examples: * --------- * const { sample } = require('./sampling'); * var examples = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'] * var samples = sample(examples, n_items=4, method='overlap') */ const sample = (examples, n_items, method = 'overlap', shuffle = true) => { // Generate BWS sets const n_sets = Math.trunc(examples.length / (n_items - 1)) var bwsindices; if (method === 'overlap'){ bwsindices = indicesOverlap(n_sets, n_items, shuffle)[0]; }else if (method === 'twice'){ bwsindices = indicesTwice(n_sets, n_items, shuffle)[0]; }else{ throw new Error(`method='${method}' not available.`) } // enforce overflow if `index>=n` const n = examples.length; var oflowindices_ = []; bwsindices.forEach(indicies => { var tmp = []; indicies.forEach(idx => { tmp.push(idx % n); }); oflowindices_.push(tmp); }) // reshape data into BWS sets var reshaped = []; oflowindices_.forEach(indicies => { var tmp = []; for ( var [i, v] of examples.entries() ){ if ( indicies.includes(i) ){ tmp.push(v); } } reshaped.push(tmp); }); // done return reshaped; } /** * Shuffle the sublists' items * * @param {Array} arrs A list of (sub)lists * @param {Int} n_sets Requested number of BWS sets * @param {Int} n_items Number items per BWS set * * Example: * -------- * var arrs = [[1, 2, 3], [4, 5, 6]] * var out = shuffleSubarrs(arrs) */ const shuffleSubarrs = (arrs) => { arrs.forEach((a) => { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; }) return arrs; } /** * Generate BWS set indices so that each example occur at least once, * and exactly `1/(n_items - 1) * 100%` of examples occur twice across * all generate BWS sets. * * @param {Int} n_sets Requested number of BWS sets * @param {Int} n_items Number items per BWS set * @param {Boolean} shuffle Flag to permute/shuffle indices * * @returns {Array[Array[Int]]} bwsindices. A list of `n_sets` BWS sets. * Each BWS set is a list of `n_items` indices. * @returns {Int} n_examples. The number of indices spread across the * BWS sets. The indices can be generated as follows: * `indices=range(0, n_examples)` * Examples: * --------- * const {bwsindices, n_examples} = indicesOverlap(1000, 4, false) */ const indicesOverlap = (n_sets, n_items, shuffle = true) => { // abort if(n_items < 2){ console.log(`Warning: No overlap possible with n_items=${n_items}.`) return [ [], 0 ] } if(n_sets < 1){ console.log("Warning: Zero BWS sets requested.") return [ [], 0 ] } if(n_sets == 1){ console.log("Warning: Only one BWS set requested.") if (shuffle === true){ var arr = [...Array(n_items).keys()]; arr.sort(() => (Math.random() > .5) ? 1 : -1); return [ [arr], n_items ] }else{ return [ [[...Array(n_items).keys()]], n_items ] } } // compute required number of examples const n_examples = n_sets * (n_items - 1) // generate a `pool` of indices const pool = [...Array(n_examples).keys()]; // copy from indices `pool` var bwsindices = [] for(var k = 0; k < n_examples; k += (n_items - 1) ){ bwsindices.push( pool.slice(k, Number(k) + Number(n_items)) ); } bwsindices[bwsindices.length - 1][bwsindices[0].length - 1] = bwsindices[0][0]; // shuffle each BWS set if (shuffle){ bwsindices = shuffleSubarrs(bwsindices); } // done return [ bwsindices, n_examples ] } /** * Sample each example at least twice across all generated BWS sets * * @param {Int} n_sets Requested number of BWS sets * @param {Int} n_items Number items per BWS set * @param {Boolean} shuffle Flag to permute/shuffle indices * @returns {Array[Array[Int]]} bwsindices A list of `n_sets` BWS sets. * Each BWS set is a list of `n_items` indices. * @returns {Int} n_examples The number of indices spread across the BWS sets. * The indices can be generated as follows: * `indices=range(0, n_examples)` * Examples: * --------- * bwsindices, n_examples = indicesTwice(1000, 4, False) */ const indicesTwice = (n_sets, n_items, shuffle = true) => { // (A) Call `indicesOverlap` without randomness! var [ bwsindices, n_examples ] = indicesOverlap(n_sets, n_items, false); if (n_items <= 2 && n_sets <= 1){ return [ bwsindices, n_examples ] } // (B) Add BWS sets so that every index is used twice -- // number of BWS sets to connect examples const n_btw = Math.trunc( (n_examples * (n_items - 2)) / (n_items * (n_items - 1)) ); // which examples from the `pool` have not been used twice? var avail = []; for(var q = 0; q < n_examples; q++){ if( (q % (n_items - 1)) != 0 ){ avail.push(q); } } const n_avail = n_btw * Math.trunc(avail.length / n_btw); // generate BWS sets var grid = []; for(var i = 0; i < n_avail; i += n_btw){ if(grid.length < n_items){ grid.push(i); } } for(var r = 0; r < n_btw; r++){ var tmp = []; for(var k of grid){ tmp.push(avail[k + r]); } bwsindices.push(tmp); } // (C) Shuffle here if (shuffle){ bwsindices = shuffleSubarrs(bwsindices); } // done return [ bwsindices, n_examples ] } module.exports = { sample, shuffleSubarrs, indicesOverlap, indicesTwice };