UNPKG

@infect/infect-rda-sample-storage

Version:

INFECT Sample Storage for RDA

199 lines (151 loc) 7.14 kB
import InfectFilterFactory from './InfectFilterFactory.js'; export default class InfectMapper { constructor() { this.filterFactory = new InfectFilterFactory(); // slots used for mic values. Each value will put into one of thos slots // 0.001 ... 0.512 + 1 ... 512 this.micSlots = Array.apply(null, {length:20}) .map((v, i) => i) .reverse() .map(v => Math.ceil((512 / Math.pow(2, v)) * 1000) / 1000); // disc diffusion slots. each value will be assigned to one of the slots // 5 ... 50 this.ddSlots = Array.apply(null, {length:45}).map((v, i) => i + 6); } async load() { await this.filterFactory.load(); } async compute({ models, filterConfiguration, subRoutines = [] }) { const prapareStart = process.hrtime.bigint(); const filter = this.filterFactory.createFilter(filterConfiguration); const preparationDuration = process.hrtime.bigint()-prapareStart; // were' mapping the data to nested maps // so that we can count the resistance on them const mappingMap = new Map(); const filterStart = process.hrtime.bigint(); let filteredModelCount = 0; let invalidModelCount = 0; let resistanceMICCount = 0; let resistanceDiscDiffusionCount = 0; let resistanceQualitativeCount = 0; // flags const discDiffusionPercentileSubRoutine = subRoutines.includes('DiscDiffusionPercentile'); const micPercentileSubRoutine = subRoutines.includes('MICPercentileSubRoutine'); for (const model of models) { if (!model.isValid()) { invalidModelCount++; } else if (model.satisfiesFilter(filter)) { const id = `${model.microorganismId},${model.compoundSubstanceId}`; if (!mappingMap.has(id)) { const data = { resistant: 0, intermediate: 0, susceptible: 0, microorganismId: model.microorganismId, compoundSubstanceId: model.compoundSubstanceId, modelCount: 0, resistanceMICCount: 0, resistanceDiscDiffusionCount: 0, resistanceQualitativeCount: 0, }; if (discDiffusionPercentileSubRoutine) { data.discDiffusionValues = new Map(this.ddSlots.map(v => ([v, 0]))); } if (micPercentileSubRoutine) { data.MICValues = new Map(this.micSlots.map(v => ([v, 0]))); } mappingMap.set(id, data); } const mapping = mappingMap.get(id); if (model.hasValue('resistanceQualitative')) { mapping.resistanceQualitativeCount++; resistanceQualitativeCount++; if (model.resistanceQualitative === 'r') mapping.resistant++; else if (model.resistanceQualitative === 'i') mapping.intermediate++; else if (model.resistanceQualitative === 's') mapping.susceptible++; } if (model.hasValue('resistanceQuantitativeMic')) { resistanceMICCount++; mapping.resistanceMICCount++; if (micPercentileSubRoutine) { this.addValueToSlot(this.micSlots, mapping.MICValues, model.getValue('resistanceQuantitativeMic'), true); } } if (model.hasValue('resistanceQuantitativeDiscDiffusion')) { resistanceDiscDiffusionCount++; mapping.resistanceDiscDiffusionCount++; if (discDiffusionPercentileSubRoutine) { this.addValueToSlot(this.ddSlots, mapping.discDiffusionValues, model.getValue('resistanceQuantitativeDiscDiffusion'), false); } } mapping.modelCount++; } else { filteredModelCount++; } }; // canont return maps -> return arrays const values = Array.from(mappingMap.values()); for (const value of values) { if (value.MICValues) { value.MICValues = Array.from(value.MICValues.entries()); } if (value.discDiffusionValues) { value.discDiffusionValues = Array.from(value.discDiffusionValues.entries()); } } const filterDuration = process.hrtime.bigint()-filterStart; const divider = BigInt(1000000); return { values, counters: { filteredModelCount, invalidModelCount, totalModelCount: models.length, filteredPercentage: ((filteredModelCount + invalidModelCount)/models.length*100), resistanceMICCount, resistanceDiscDiffusionCount, resistanceQualitativeCount, }, timings: { preparation: Number(preparationDuration)/1000000, filtering: Number(filterDuration)/1000000, }, } } /** * assigns a value to a given slot by mathicng it and rounding it up or down * if it doesn't match to a slot * * @param {array} slots predefined array of slots that * exist * @param {map} sampleSlots the slots that store the counters * on the matrix points * @param {number} sampleValue the current value * @param {boolean} [roundUp=true] round up or down? */ addValueToSlot(slots, sampleSlots, sampleValue, roundUp = true) { // set to first of the slots let previousSlotKey = slots[0]; let lastSlotKey = slots[slots.length -1]; // find the correct slot for the value for (const slotKey of slots) { if (sampleValue === slotKey) { // value matches slot, put it there sampleSlots.set(slotKey, sampleSlots.get(slotKey) + 1); } else if (sampleValue < slotKey) { // value is smaller than that before, put it in the current // slot if rounding up or the previous if rounding down const key = roundUp ? slotKey : previousSlotKey; sampleSlots.set(key, sampleSlots.get(key) + 1); } else if (slotKey === lastSlotKey) { // put the last value in the last slot sampleSlots.set(slotKey, sampleSlots.get(slotKey) + 1); } else { previousSlotKey = slotKey; continue; } // isn't encountered when the els statement is executed break; } } }