Comparing version 3.2.2 to 3.3.0
1416
lib/index.js
@@ -1,1273 +0,195 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var msSpectrum = require('ms-spectrum'); | ||
var mfFinder = require('mf-finder'); | ||
var mfGenerator = require('mf-generator'); | ||
var massFragmentation = require('mass-fragmentation'); | ||
var mfMatcher = require('mf-matcher'); | ||
var mfParser = require('mf-parser'); | ||
var mfUtilities = require('mf-utilities'); | ||
var chemicalGroups = require('chemical-groups'); | ||
var nucleotide = require('nucleotide'); | ||
var peptide = require('peptide'); | ||
var JSZip = require('jszip'); | ||
var crossFetch = require('cross-fetch'); | ||
var mfFromGoogleSheet = require('mf-from-google-sheet'); | ||
var isotopicDistribution = require('isotopic-distribution'); | ||
var peaksSimilarity = require('peaks-similarity'); | ||
var mlRegressionTheilSen = require('ml-regression-theil-sen'); | ||
var mlSpectraProcessing = require('ml-spectra-processing'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var JSZip__default = /*#__PURE__*/_interopDefaultLegacy(JSZip); | ||
var crossFetch__default = /*#__PURE__*/_interopDefaultLegacy(crossFetch); | ||
/** | ||
* | ||
* @param {object} experimentalSpectrum | ||
* @param {object} database | ||
* @param {object} [options={}] | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {string} [options.ionizations=''] - string containing a comma separated list of modifications | ||
* @param {number} [options.precision=100] - Allowed mass range based on precision | ||
*/ | ||
async function appendFragmentsInfo( | ||
experimentalSpectrum, | ||
database, | ||
options = {}, | ||
) { | ||
const { ionizations, onStep, precision } = options; | ||
if (!experimentalSpectrum) { | ||
throw new Error('Experimental spectrum is not defined'); | ||
} | ||
if (!database) { | ||
throw new Error('Database is not defined'); | ||
} | ||
const peaks = experimentalSpectrum.getPeaks({ sumValue: 1 }); | ||
for (let entry of database) { | ||
const ranges = Object.keys(entry.atoms) | ||
.map((atom) => `${atom}0-${entry.atoms[atom]}`) | ||
.join(' '); | ||
entry.fragments = { | ||
nbFound: 0, | ||
intensityFound: 0, | ||
assignments: [], | ||
}; | ||
for (let i = 0; i < peaks.length; i++) { | ||
if (onStep) await onStep(i); | ||
const peak = peaks[i]; | ||
const possibleMFs = await mfFinder.findMFs(peak.x, { | ||
ionizations, | ||
precision, | ||
ranges, | ||
}); | ||
if (possibleMFs.mfs.length > 0) { | ||
entry.fragments.nbFound++; | ||
entry.fragments.intensityFound += peak.y; | ||
entry.fragments.assignments.push({ | ||
peak, | ||
bestMF: possibleMFs.mfs[0], | ||
}); | ||
} | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
} | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.EMDB = void 0; | ||
const ms_spectrum_1 = require("ms-spectrum"); | ||
const appendFragmentsInfo_js_1 = require("./append/appendFragmentsInfo.js"); | ||
const fromArray_js_1 = require("./from/fromArray.js"); | ||
const fromMolecules_js_1 = require("./from/fromMolecules.js"); | ||
const fromMonoisotopicMass_js_1 = require("./from/fromMonoisotopicMass.js"); | ||
const fromNucleicSequence_js_1 = require("./from/fromNucleicSequence.js"); | ||
const fromPeptidicSequence_js_1 = require("./from/fromPeptidicSequence.js"); | ||
const fromRange_js_1 = require("./from/fromRange.js"); | ||
const loadCommercials_js_1 = require("./loadCommercials.js"); | ||
const loadGoogleSheet_js_1 = require("./loadGoogleSheet.js"); | ||
const loadKnapSack_js_1 = require("./loadKnapSack.js"); | ||
const search_js_1 = require("./search.js"); | ||
const searchMSEM_js_1 = require("./searchMSEM.js"); | ||
const searchSimilarity_js_1 = require("./searchSimilarity.js"); | ||
__exportStar(require("./massShifts.js"), exports); | ||
__exportStar(require("./util/fetchJSON.js"), exports); | ||
/** | ||
* Generates a database 'generated' from an array of molecular formula | ||
* @param {array} mfsArray - Array of string or Array of array containing the parts to combine | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.estimate=false] - estimate the number of MF without filters | ||
* @param {string} [options.databaseName='generated'] | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {number} [options.limit=10000000] - Maximum number of results | ||
* @param {boolean} [options.canonizeMF=true] - Canonize molecular formula | ||
* @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique | ||
* @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule) | ||
* @param {object} [options.filter={}] | ||
* @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass | ||
* @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass | ||
* @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass | ||
* @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass | ||
* @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass | ||
* @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {object} [options.filter.atoms] - object of atom:{min, max} | ||
* @param {function} [options.filter.callback] - a function to filter the MF | ||
* @param {string} [options.filterFct] - A string representing a function | ||
* | ||
* @example | ||
* | ||
* const {EMDB} = require('emdb'); | ||
* let emdb = new EMDB(); | ||
* let array = ['C1-10', 'H1-10']; | ||
* emdb.fromArray(array); // create a database 'generated' combining all possibilies | ||
* console.log(emdb.get('generated').length); // 100 | ||
* | ||
* @example | ||
* const {EMDB} = require('emdb'); | ||
* let emdb = new EMDB(); | ||
* let array = ['C1-10 H1-10']; | ||
* emdb.fromArray(array); // create a database 'generated' combining all possibilies | ||
* console.log(emdb.get('generated').length); // 100 | ||
* | ||
* @example | ||
* const {EMDB} = require('emdb'); | ||
* let emdb = new EMDB(); | ||
* // in case of an array of array, one of the group is allwed | ||
* let array = [['C1-10','H1-10'],'Cl0-1 Br0-1']; | ||
* emdb.fromArray(array); // create a database 'generated' combining all possibilies | ||
* console.log(emdb.get('generated').length); // 80 | ||
* | ||
* @example | ||
* <script src="https://www.lactame.com/lib/mass-tools/HEAD/mass-tools.js" /> | ||
* <script> | ||
* let emdb = new MassTools.EMDB(); | ||
* let array = ['C1-10', 'H1-10']; | ||
* emdb.fromArray(array); // create a database 'generated' combining all possibilities | ||
* console.log(emdb.get('generated').length); // 100 | ||
* </script> | ||
* | ||
* // from the browser | ||
* A class that deals with database of monoisotopic mass and molecular formula | ||
*/ | ||
async function fromArray(mfsArray, options = {}) { | ||
return mfGenerator.generateMFs(mfsArray, options); | ||
} | ||
/** * Generates a database 'monoisotopic' from a monoisotopic mass and various options | ||
* @param {{smiles?:string,molecule?:string,idCode?:string}[]} entries - Array of object containing a property to recreate the molecule | ||
* @param {import('openchemlib')} ocl - The OCL library | ||
* @param {object} [options={}] | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {boolean} [options.allowNeutral=true] | ||
* @param {string} [options.ionizations=''] - string containing a comma separated list of modifications | ||
* @param {number} [options.precision=100] - Allowed mass range based on precision | ||
* @param {boolean} [options.groupResults=false] - Should we group the results if they have the same monoisotopic mass and experimental mass | ||
* @param {object} [options.fragmentation={}] | ||
* @param {object} [options.fragmentation.acyclic=false] | ||
* @param {object} [options.fragmentation.cyclic=false] | ||
* @param {object} [options.fragmentation.full=true] | ||
* @param {import('mf-matcher').MSEMFilterOptions} [options.filter={}] | ||
* @returns {Promise} | ||
*/ | ||
async function fromMolecules(entries, ocl, options = {}) { | ||
let { | ||
onStep, | ||
ionizations, | ||
filter, | ||
fragmentation = { acyclic: false, cyclic: false, full: true }, | ||
groupResults = false, | ||
} = options; | ||
ionizations = mfUtilities.preprocessIonizations(ionizations); | ||
let results = []; | ||
for (let i = 0; i < entries.length; i++) { | ||
const entry = entries[i]; | ||
const molecule = getMolecule(entry, ocl); | ||
if (!molecule) continue; | ||
const { acyclic = false, cyclic = false, full = true } = fragmentation; | ||
const allFragments = massFragmentation.fragment(molecule, { | ||
acyclic, | ||
cyclic, | ||
full, | ||
}); | ||
for (const oneFragment of allFragments) { | ||
appendResults(results, oneFragment, { | ||
entry, | ||
ionizations, | ||
filter, | ||
}); | ||
if (onStep) onStep(i); | ||
class EMDB { | ||
constructor() { | ||
this.databases = {}; | ||
this.experimentalSpectrum = undefined; | ||
} | ||
} | ||
if (groupResults) { | ||
results = groupFragmentationResults(results); | ||
} | ||
return results.sort((a, b) => a.em - b.em); | ||
} | ||
/** | ||
* We could group the results and replace the property 'fragment' by 'fragments' | ||
* @param {*} results | ||
*/ | ||
function groupFragmentationResults(results) { | ||
const sortedResults = results.slice().sort((a, b) => { | ||
if (a.em !== b.em) { | ||
return a.em - b.em; | ||
/** | ||
* | ||
* @param {*} data | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.normed=true] Should we normed (sum Y to 1) the experimental spectrum ? | ||
* @param {number} [options.threshold=0.00025] Threshold used for peak picking | ||
*/ | ||
setExperimentalSpectrum(data, options = {}) { | ||
const { normed = true, threshold = 0.00025 } = options; | ||
this.experimentalSpectrum = new ms_spectrum_1.Spectrum(data, { threshold }); | ||
if (normed) { | ||
this.experimentalSpectrum.normedY(); | ||
} | ||
return this.experimentalSpectrum; | ||
} | ||
if (a.ms.em !== b.ms.em) { | ||
return a.ms.em - b.ms.em; | ||
/** | ||
* Add a new database using the KnapSack content | ||
* @param {*} options | ||
*/ | ||
async loadKnapSack(options = {}) { | ||
const { databaseName = 'knapSack', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) | ||
return; | ||
this.databases[databaseName] = await (0, loadKnapSack_js_1.loadKnapSack)(); | ||
} | ||
if (a.fragment.idCode < b.fragment.idCode) return -1; | ||
return 1; | ||
}); | ||
const groupedResults = []; | ||
let currentResult = {}; | ||
for (let result of sortedResults) { | ||
if ( | ||
result.em !== currentResult.em || | ||
result.ms.em !== currentResult.ms.em | ||
) { | ||
currentResult = { ...result }; | ||
currentResult.fragments = [ | ||
{ | ||
idCode: result.fragment.idCode, | ||
type: result.fragment.type, | ||
count: 1, | ||
parents: [{ ...result.fragment.parent, count: 1 }], | ||
}, | ||
]; | ||
delete currentResult.fragment; | ||
groupedResults.push(currentResult); | ||
} else { | ||
const lastFragment = | ||
currentResult.fragments[currentResult.fragments.length - 1]; | ||
if (lastFragment.idCode === result.fragment.idCode) { | ||
lastFragment.count++; | ||
if ( | ||
lastFragment.parents[lastFragment.parents.length - 1].idCode === | ||
result.fragment.parent.idCode | ||
) { | ||
lastFragment.parents[lastFragment.parents.length - 1].count++; | ||
} else { | ||
lastFragment.parents.push({ ...result.fragment.parent, count: 1 }); | ||
} | ||
} else { | ||
currentResult.fragments.push({ | ||
idCode: result.fragment.idCode, | ||
type: result.fragment.type, | ||
count: 1, | ||
parents: [{ ...result.fragment.parent, count: 1 }], | ||
}); | ||
} | ||
/** | ||
* Add a new database of 12000 commercial products | ||
* @param {*} options | ||
*/ | ||
async loadCommercials(options = {}) { | ||
const { databaseName = 'commercials', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) | ||
return; | ||
this.databases[databaseName] = await (0, loadCommercials_js_1.loadCommercials)(); | ||
} | ||
} | ||
for (let group of groupedResults) { | ||
group.fragments = group.fragments.sort((a, b) => b.count - a.count); | ||
} | ||
return groupedResults; | ||
} | ||
/** | ||
* | ||
* @param {object} entry | ||
* @param {import('openchemlib')} ocl - The OCL library | ||
*/ | ||
function getMolecule(entry, ocl) { | ||
if (entry.idCode) { | ||
return ocl.Molecule.fromIDCode(entry.idCode); | ||
} | ||
if (entry.ocl && entry.ocl.idCode) { | ||
return ocl.Molecule.fromIDCode(entry.ocl.idCode); | ||
} | ||
if (entry.smiles) { | ||
return ocl.Molecule.fromSmiles(entry.smiles); | ||
} | ||
if (entry.molfile) { | ||
return ocl.Molecule.fromMolfile(entry.molfile); | ||
} | ||
return undefined; | ||
} | ||
function appendResults(results, oneFragment, options) { | ||
const mf = oneFragment.mfInfo.mf; | ||
const { ionizations, filter, entry } = options; | ||
const mfInfo = new mfParser.MF(mf).getInfo(); | ||
for (let ionization of ionizations) { | ||
const result = { | ||
charge: mfInfo.charge, | ||
em: mfInfo.monoisotopicMass, | ||
mw: mfInfo.mass, | ||
mf: mfInfo.mf, | ||
ionization, | ||
unsaturation: mfInfo.unsaturation, | ||
atoms: mfInfo.atoms, | ||
fragment: { | ||
parent: { | ||
...entry, | ||
idCode: oneFragment.parentIDCode, | ||
}, | ||
idCode: oneFragment.idCode, | ||
type: oneFragment.fragmentType, | ||
}, | ||
}; | ||
let match = mfMatcher.msemMatcher(result, filter); | ||
if (!match) continue; | ||
result.ms = match.ms; | ||
result.ionization = match.ionization; | ||
results.push(result); | ||
} | ||
} | ||
/** | ||
* Generates a database 'monoisotopic' from a monoisotopic mass and various options | ||
* @param {number|string|array} masses - Monoisotopic mass | ||
* @param {object} [options={}] | ||
* @param {number} [options.maxIterations=10000000] - Maximum number of iterations | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {boolean} [options.allowNeutral=true] | ||
* @param {boolean} [options.uniqueMFs=true] | ||
* @param {number} [options.limit=1000] - Maximum number of results | ||
* @param {string} [options.ionizations=''] - string containing a comma separated list of modifications | ||
* @param {string} [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search | ||
* @param {number} [options.precision=100] - Allowed mass range based on precision | ||
* @param {object} [options.filter={}] | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {object} [options.filter.atoms] - object of atom:{min, max} | ||
* @param {function} [options.filter.callback] - a function to filter the MF | ||
* @returns {Promise} | ||
*/ | ||
async function fromMonoisotopicMass(masses, options = {}) { | ||
if (typeof masses === 'string') { | ||
masses = masses.split(/[ ,;\r\n\t]/).map(Number); | ||
} | ||
if (typeof masses === 'number') { | ||
masses = [masses]; | ||
} | ||
let results = []; | ||
for (let mass of masses) { | ||
results.push(await mfFinder.findMFs(mass, options)); | ||
} | ||
return { | ||
mfs: results.map((entry) => entry.mfs).flat(), | ||
info: { | ||
numberMFEvaluated: results.reduce( | ||
(sum, current) => (sum += current.info.numberMFEvaluated), | ||
0, | ||
), | ||
numberResults: results.reduce( | ||
(sum, current) => (sum += current.info.numberResults), | ||
0, | ||
), | ||
}, | ||
}; | ||
} | ||
/** | ||
* Add a database starting from a peptidic sequence | ||
* | ||
* @param {string} [sequencesString] Sequence as a string of 1 letter or 3 letters code. Could also be a correct molecular formula respecting uppercase, lowercase | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.estimate=false] - estimate the number of MF without filters | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {number} [options.limit=100000] | ||
* @param {string} [options.ionizations=''] | ||
* @param {object} [options.info={}] | ||
* @param {string} [options.info.kind] - rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna | ||
* @param {string} [options.info.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate | ||
* @param {string} [options.info.circular=false] | ||
* @param {array} [options.mfsArray=[]] | ||
* @param {object} [options.fragmentation={}] Object defining options for fragmentation | ||
* @param {boolean} [options.fragmentation.a=false] If true allow fragments of type 'a' | ||
* @param {boolean} [options.fragmentation.ab=false] If true allow fragments of type 'a' minus base | ||
* @param {boolean} [options.fragmentation.b=false] If true allow fragments of type 'b' | ||
* @param {boolean} [options.fragmentation.c=false] If true allow fragments of type 'c' | ||
* @param {boolean} [options.fragmentation.d=false] If true allow fragments of type 'd' | ||
* @param {boolean} [options.fragmentation.dh2o=false] If true allow fragments of type 'd' with water loss | ||
* @param {boolean} [options.fragmentation.w=false] If true allow fragments of type 'w' | ||
* @param {boolean} [options.fragmentation.x=false] If true allow fragments of type 'x' | ||
* @param {boolean} [options.fragmentation.y=false] If true allow fragments of type 'y' | ||
* @param {boolean} [options.fragmentation.z=false] If true allow fragments of type 'z' | ||
* @param {boolean} [options.baseLoss=false] If true allow base loss at all the positions | ||
* | ||
* @param {object} [options.filter={}] Object defining options for molecular formula filter | ||
* @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass | ||
* @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass | ||
* @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass | ||
* @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass | ||
* @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass | ||
* @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @returns {Promise} | ||
*/ | ||
async function fromNucleicSequence(sequencesString, options = {}) { | ||
const { | ||
mfsArray = [], | ||
fragmentation = {}, | ||
filter = {}, | ||
ionizations = '', | ||
info = {}, | ||
estimate = false, | ||
limit = 100000, | ||
onStep, | ||
} = options; | ||
let sequences = nucleotide.sequenceToMF(sequencesString, info).split('.'); | ||
let fragmentsArray = sequences.slice(); | ||
// calculate fragmentation | ||
for (let i = 0; i < sequences.length; i++) { | ||
let sequence = sequences[i]; | ||
let fragments = nucleotide.generateFragments(sequence, fragmentation); | ||
if (i === 1) { | ||
// complementary sequence | ||
fragments = fragments.map((fragment) => fragment.replace(/\$/g, '$cmp-')); | ||
get(databaseName) { | ||
return this.databases[databaseName]; | ||
} | ||
fragmentsArray = fragmentsArray.concat(fragments); | ||
if (fragmentation.baseLoss) { | ||
fragmentsArray = fragmentsArray.concat(nucleotide.baseLoss(sequence)); | ||
/** | ||
* Load the contaminants database from a google sheet document | ||
* @param {object} [options={}] | ||
* @param {string} [options.databaseName='contaminants'] | ||
* @param {string} [options.forceReload=false] | ||
*/ | ||
async loadContaminants(options = {}) { | ||
const { databaseName = 'contaminants', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) | ||
return; | ||
this.databases[databaseName] = await (0, loadGoogleSheet_js_1.loadGoogleSheet)(); | ||
} | ||
} | ||
mfsArray.push(fragmentsArray); | ||
let combined = await mfGenerator.generateMFs(mfsArray, { | ||
ionizations, | ||
filter, | ||
uniqueMFs: false, | ||
estimate, | ||
onStep, | ||
limit, | ||
}); | ||
if (Array.isArray(combined)) { | ||
// not an estimation | ||
combined.forEach((result) => { | ||
result.sequence = chemicalGroups.groupsToSequence( | ||
result.parts.filter((part) => part).join(' '), | ||
); | ||
}); | ||
} | ||
return combined; | ||
} | ||
function fragmentPeptide(sequence, options = {}) { | ||
const { digestion = {}, protonation, fragmentation, protonationPH } = options; | ||
sequence = peptide.sequenceToMF(sequence); | ||
let fragmentsArray = [sequence]; | ||
// do we also have some digest fragments ? | ||
if (digestion.enzyme) { | ||
let digests = peptide.digestPeptide(sequence, digestion); | ||
if (options.protonation) { | ||
digests = peptide.chargePeptide(digests, { | ||
pH: options.protonationPH, | ||
}); | ||
/** | ||
* Load a google sheet containing MF information | ||
* @param {object} [options={}] | ||
* @param {string} [options.databaseName='sheet'] | ||
* @param {string} [options.forceReload=false] | ||
*/ | ||
async loadGoogleSheet(options = {}) { | ||
const { databaseName = 'sheet', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) | ||
return; | ||
this.databases[databaseName] = await (0, loadGoogleSheet_js_1.loadGoogleSheet)(); | ||
} | ||
fragmentsArray = fragmentsArray.concat(digests); | ||
} | ||
// allow neutral loss | ||
if (options.allowNeutralLoss) { | ||
sequence = peptide.allowNeutralLoss(sequence); | ||
} | ||
// apply protonation | ||
if (protonation) { | ||
sequence = peptide.chargePeptide(sequence, { pH: protonationPH }); | ||
} | ||
// calculate fragmentation | ||
let fragments = peptide.generatePeptideFragments(sequence, fragmentation); | ||
fragmentsArray = fragmentsArray.concat(fragments); | ||
return fragmentsArray; | ||
} | ||
/** | ||
* Add a database starting from a peptidic sequence | ||
* | ||
* @param {string} [sequences] Sequence as a string of 1 letter or 3 letters code. Could also be a correct molecular formula respecting uppercase, lowercase. It can be comma separated if you have many peptide sequences | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.estimate=false] - estimate the number of MF without filters | ||
* @param {string} [options.ionizations=''] | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {array} [options.mfsArray=[]] | ||
* @param {boolean} [options.protonation=false] | ||
* @param {number} [options.protonationPH=7] | ||
* @param {boolean} [options.allowNeutralLoss=false] | ||
* @param {number} [options.limit=100000] | ||
* | ||
* @param {object} [options.digestion={}] Object defining options for digestion | ||
* @param {number} [options.digestion.minMissed=0] Minimal number of allowed missed cleavage | ||
* @param {number} [options.digestion.maxMissed=0] Maximal number of allowed missed cleavage | ||
* @param {number} [options.digestion.minResidue=0] Minimal number of residues | ||
* @param {number} [options.digestion.maxResidue=+Infinity] Maximal number of residues | ||
* @param {string} [options.digestion.enzyme] Mandatory field containing the name of the enzyme among: chymotrypsin, trypsin, glucph4, glucph8, thermolysin, cyanogenbromide | ||
* | ||
* @param {object} [options.fragmentation={}] Object defining options for fragmentation | ||
* @param {boolean} [options.fragmentation.a=false] If true allow fragments of type 'a' | ||
* @param {boolean} [options.fragmentation.b=false] If true allow fragments of type 'b' | ||
* @param {boolean} [options.fragmentation.c=false] If true allow fragments of type 'c' | ||
* @param {boolean} [options.fragmentation.x=false] If true allow fragments of type 'x' | ||
* @param {boolean} [options.fragmentation.y=false] If true allow fragments of type 'y' | ||
* @param {boolean} [options.fragmentation.z=false] If true allow fragments of type 'z' | ||
* @param {boolean} [options.fragmentation.ya=false] If true allow fragments of type 'ya' | ||
* @param {boolean} [options.fragmentation.yb=false] If true allow fragments of type 'yb' | ||
* @param {boolean} [options.fragmentation.yc=false] If true allow fragments of type 'yc' | ||
* @param {boolean} [options.fragmentation.zc=false] If true allow fragments of type 'zc' | ||
* @param {number} [options.fragmentation.minInternal=0] Minimal internal fragment length | ||
* @param {number} [options.fragmentation.maxInternal=+Infinity] Maximal internal fragment length | ||
* | ||
* @param {object} [options.filter={}] Object defining options for molecular formula filter | ||
* @param {number} [options.filter.precision=1000] - The precision on the experimental mass | ||
* @param {number} [options.filter.targetMass] - Target mass, allows to calculate error and filter results | ||
* @param {number[]} [options.filter.targetMasses] - Target masses: SORTED array of numbers | ||
* @param {number[]} [options.filter.targetIntensities] - Target intensities: SORTED array of numbers | ||
* @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass | ||
* @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass | ||
* @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass | ||
* @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {function} [options.filter.callback] - a function to filter the MF | ||
* @returns {Promise} | ||
*/ | ||
async function fromPeptidicSequence(sequences, options = {}) { | ||
const { | ||
digestion = {}, | ||
mfsArray: originalMFsArray = [], | ||
allowNeutralLoss = false, | ||
protonation = false, | ||
protonationPH = 7, | ||
fragmentation = {}, | ||
filter = {}, | ||
ionizations = '', | ||
limit = 100000, | ||
estimate = false, | ||
onStep, | ||
links = {}, | ||
} = options; | ||
const hasLinked = sequences.includes('#'); | ||
const mfsArrayLinked = JSON.parse(JSON.stringify(originalMFsArray)); | ||
const mfsArrayUnlinked = JSON.parse(JSON.stringify(originalMFsArray)); | ||
const unlinked = []; | ||
mfsArrayUnlinked.push(unlinked); | ||
for (const sequence of sequences.split(/[,:]/)) { | ||
let fragmentsArray = fragmentPeptide(sequence, { | ||
digestion, | ||
protonation, | ||
fragmentation, | ||
protonationPH, | ||
allowNeutralLoss, | ||
}); | ||
mfsArrayLinked.push( | ||
fragmentsArray.filter((fragment) => fragment.includes('#')), | ||
); | ||
unlinked.push( | ||
...fragmentsArray.filter((fragment) => !fragment.includes('#')), | ||
); | ||
} | ||
let combined = await mfGenerator.generateMFs(mfsArrayUnlinked, { | ||
ionizations, | ||
filter, | ||
estimate, | ||
limit, | ||
onStep, | ||
links, | ||
}); | ||
if (hasLinked) { | ||
combined.push( | ||
...(await mfGenerator.generateMFs(mfsArrayLinked, { | ||
ionizations, | ||
filter, | ||
estimate, | ||
limit, | ||
onStep, | ||
links, | ||
})), | ||
); | ||
} | ||
if (!estimate) { | ||
combined.forEach((result) => { | ||
result.sequence = chemicalGroups.groupsToSequence( | ||
result.parts.filter((part) => part).join(' '), | ||
); | ||
}); | ||
} | ||
return combined; | ||
} | ||
/** | ||
* Generates a database 'generated' from an array of molecular formula | ||
* @param {string} rangesString - a string representing the range to search | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.estimate=false] - estimate the number of MF without filters | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {string} [options.databaseName='generated'] | ||
* @param {number} [options.limit=100000] - Maximum number of results | ||
* @param {boolean} [options.canonizeMF=true] - Canonize molecular formula | ||
* @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique | ||
* @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule) | ||
* @param {object} [options.filter={}] | ||
* @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass | ||
* @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass | ||
* @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass | ||
* @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass | ||
* @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass | ||
* @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {function} [options.filter.callback] - a function to filter the MF | ||
* @param {object} [options.filter.atoms] - object of atom:{min, max} | ||
* | ||
* @returns {Promise} - list of possible molecular formula | ||
* | ||
* @example | ||
* const {EMDB} = require('emdb'); | ||
* let emdb = new EMDB(); | ||
* // semi-columns separated for combination, comma for 'or' | ||
* emdb.fromRange('C1-10, H1-10; Cl0-1 Br0-1'); // create a database 'generated' combining all possibilies | ||
* console.log(emdb.get('generated').length); // 80 | ||
*/ | ||
async function fromRange(rangesString, options = {}) { | ||
let ranges = rangesString.split(/ *[;\r\n] */); | ||
for (let i = 0; i < ranges.length; i++) { | ||
let range = ranges[i]; | ||
if (range.includes(',')) { | ||
ranges[i] = range.split(/ *, */); | ||
async loadTest() { | ||
await this.fromArray(['C1-100'], { | ||
databaseName: 'test', | ||
ionizations: '+', | ||
}); | ||
} | ||
} | ||
return mfGenerator.generateMFs(ranges, options); | ||
} | ||
async function fetchArrayBuffer(url) { | ||
const result = await crossFetch__default["default"](url); | ||
return result.arrayBuffer(); | ||
} | ||
const loadingPromises$1 = {}; | ||
async function loadCommercials(options = {}) { | ||
const { | ||
url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/commercials.zip', | ||
} = options; | ||
if (!loadingPromises$1[url]) { | ||
loadingPromises$1[url] = fetchArrayBuffer(url); | ||
} | ||
const buffer = await loadingPromises$1[url]; | ||
const jsZip = new JSZip__default["default"](); | ||
let zip = await jsZip.loadAsync(buffer); | ||
let fileData = await zip.files['commercials.json'].async('string'); | ||
let data = JSON.parse(fileData); | ||
data.sort((a, b) => a.em - b.em); | ||
return data; | ||
} | ||
async function loadGoogleSheet(options = {}) { | ||
let { | ||
refUUID = '1C_H9aiJyu9M9in7sHMOaz-d3Sv758rE72oLxEKH9ioA', | ||
uuid = '1LrJCl9-xSZKhGA9Y8nKVkYwB-mEOHBkTXg5qYXeFpZY', | ||
} = options; | ||
if (options.uuid && !options.refUUID) refUUID = ''; | ||
let url = `https://googledocs.cheminfo.org/spreadsheets/d/${uuid}/export?format=tsv`; | ||
let refURL = refUUID | ||
? `https://googledocs.cheminfo.org/spreadsheets/d/${refUUID}/export?format=tsv` | ||
: ''; | ||
let data = await mfFromGoogleSheet.mfFromGoogleSheet(url, refURL); | ||
data.sort((a, b) => a.em - b.em); | ||
return data; | ||
} | ||
const loadingPromises = {}; | ||
async function loadKnapSack(options = {}) { | ||
const { | ||
url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/ms.zip', | ||
} = options; | ||
if (!loadingPromises[url]) { | ||
loadingPromises[url] = fetchArrayBuffer(url); | ||
} | ||
const buffer = await loadingPromises[url]; | ||
const jsZip = new JSZip__default["default"](); | ||
let zip = await jsZip.loadAsync(buffer); | ||
let fileData = await zip.files['ms.json'].async('string'); | ||
let data = JSON.parse(fileData); | ||
data.forEach((d) => { | ||
d.url = `http://kanaya.naist.jp/knapsack_jsp/information.jsp?word=${d.id}`; | ||
}); | ||
data.sort((a, b) => a.em - b.em); | ||
return data; | ||
} | ||
/** | ||
Searching by various criteria. This mass will not take into account the mass | ||
of the mass of the electron | ||
* @param {object} [filter={}] | ||
* @param {number} [filter.minMW=0] - Minimal molecular weight | ||
* @param {number} [filter.maxMW=+Infinity] - Maximal molecular weight | ||
* @param {number} [filter.minEM=0] - Minimal monoisotopic mass | ||
* @param {number} [filter.maxEM=+Infinity] - Maximal monoisotopic mass | ||
* @param {number} [filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [filter.unsaturation={}] | ||
* @param {number} [filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {object} [filter.atoms] - object of atom:{min, max} | ||
* @param {object} [options={}] | ||
* @param {array} [options.databases] - an array containing the name of the databases so search, by default all | ||
* @param {boolean} [options.flatten=false] - should we return the array as a flat result | ||
*/ | ||
function search(emdb, filter, options = {}) { | ||
let { databases = Object.keys(emdb.databases), flatten = false } = options; | ||
let results = {}; | ||
for (let database of databases) { | ||
results[database] = emdb.databases[database].filter((entry) => | ||
mfMatcher.generalMatcher(entry, filter), | ||
); | ||
} | ||
if (flatten) { | ||
let flattenResults = []; | ||
for (let database of databases) { | ||
for (let entry of results[database]) { | ||
entry.database = database; | ||
flattenResults.push(entry); | ||
} | ||
async loadNeutralTest(options = {}) { | ||
const { maxC = 100 } = options; | ||
await this.fromArray([`C1-${maxC}`], { databaseName: 'test' }); | ||
} | ||
return flattenResults; | ||
} else { | ||
return results; | ||
} | ||
} | ||
/** | ||
Search for an experimental monoisotopic mass | ||
* @param {number} msem - The observed monoisotopic mass | ||
* @param {object} [options={}] | ||
* @param {array} [options.databases] - an array containing the name of the databases so search, by default all | ||
* @param {boolean} [options.flatten=false] - should we return the array as a flat result | ||
* @param {string} [options.ionizations] - list the allowed ionizations possibilities | ||
* @param {import('mf-matcher').MSEMFilterOptions} [options.filter={}] | ||
*/ | ||
function searchMSEM(emdb, msem, options = {}) { | ||
let filter = { ...(options.filter || {}), targetMass: msem }; | ||
let { databases = Object.keys(emdb.databases), flatten = false } = options; | ||
let ionizations = mfUtilities.preprocessIonizations(options.ionizations); | ||
let results = {}; | ||
for (let database of databases) { | ||
results[database] = []; | ||
} | ||
for (let ionization of ionizations) { | ||
filter.ionization = ionization; | ||
for (let database of databases) { | ||
for (let entry of emdb.databases[database]) { | ||
let match = mfMatcher.msemMatcher(entry, filter); | ||
if (match) { | ||
results[database].push({ | ||
...entry, | ||
ms: match.ms, | ||
ionization: match.ionization, | ||
}); | ||
} | ||
} | ||
async fromMonoisotopicMass(mass, options = {}) { | ||
const { databaseName = 'monoisotopic', append = false } = options; | ||
let result = await (0, fromMonoisotopicMass_js_1.fromMonoisotopicMass)(mass, options); | ||
replaceOrAppend(this, databaseName, result.mfs, append); | ||
return result; | ||
} | ||
} | ||
if (flatten) { | ||
let flattenResults = []; | ||
for (let database of databases) { | ||
for (let entry of results[database]) { | ||
entry.database = database; | ||
flattenResults.push(entry); | ||
} | ||
async fromArray(sequence, options = {}) { | ||
const { databaseName = 'generated', append = false, estimate } = options; | ||
const results = await (0, fromArray_js_1.fromArray)(sequence, options); | ||
if (estimate) | ||
return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
flattenResults.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm)); | ||
return flattenResults; | ||
} else { | ||
Object.keys(results).forEach((k) => | ||
results[k].sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm)), | ||
); | ||
return results; | ||
} | ||
} | ||
/** | ||
Search for an experimental monoisotopic mass and calculate the similarity | ||
* @param {object} [options={}] | ||
* @param {array} [options.databases] - an array containing the name of the databases so search, by default all | ||
* @param {boolean} [options.flatten] - should we return the array as a flat result | ||
* @param {function} [options.onStep] - Callback to do after each step | ||
* @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule) | ||
* @param {object} [options.minSimilarity=0.5] - min similarity value | ||
* @param {object} [options.filter={}] | ||
* @param {boolean} [options.filter.forceIonization=false] - If true ignore existing ionizations | ||
* @param {number} [options.filter.msem] - Observed monoisotopic mass in mass spectrometer | ||
* @param {number} [options.filter.precision=1000] - The precision on the experimental mass | ||
* @param {number} [options.filter.minCharge=-Infinity] - Minimal charge | ||
* @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge | ||
* @param {boolean} [options.filter.absoluteCharge=false] - If true, the charge is absolute (so between 0 and +Infinity by default) | ||
* @param {object} [options.filter.unsaturation={}] | ||
* @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation | ||
* @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation | ||
* @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation | ||
* @param {object} [options.filter.atoms] - object of atom:{min, max} | ||
* @param {object} [options.filter.callback] - a function to filter the MF | ||
* @param {object} [options.similarity={}] | ||
* @param {number} [options.similarity.widthBottom] | ||
* @param {number} [options.similarity.widthTop] | ||
* @param {object} [options.similarity.widthFunction] - function called with mass that should return an object width containing top and bottom | ||
* @param {object} [options.similarity.zone={}] | ||
* @param {number} [options.similarity.zone.low=-0.5] - window shift based on observed monoisotopic mass | ||
* @param {number} [options.similarity.zone.high=2.5] - to value for the comparison window | ||
* @param {boolean} [options.similarity.zone.auto=false] - if true, low / high is determined based on the isotopic distribution and the threshold | ||
* @param {string} [options.similarity.common] | ||
* @param {number} [options.similarity.threshold=0.001] - when calculating similarity we only use the isotopic distribution with peaks over this relative threshold | ||
* @param {number} [options.similarity.limit] - We may define the maximum number of peaks to keep | ||
* @returns {Promise} | ||
*/ | ||
async function searchSimilarity(emdb, options = {}) { | ||
const { similarity = {}, minSimilarity = 0.5, filter = {}, onStep } = options; | ||
let width = { | ||
bottom: similarity.widthBottom, | ||
top: similarity.widthTop, | ||
}; | ||
if ( | ||
!emdb.experimentalSpectrum || | ||
!emdb.experimentalSpectrum.data.x.length > 0 | ||
) { | ||
throw Error( | ||
'You need to add an experimental spectrum first using setMassSpectrum', | ||
); | ||
} | ||
let experimentalData = emdb.experimentalSpectrum.data; | ||
let sumY = emdb.experimentalSpectrum.sumY(); | ||
// the result of emdb query will be stored in a property 'ms' | ||
let results = emdb.searchMSEM(filter.msem, options); | ||
let flatEntries = []; | ||
if (!options.flatten) { | ||
for (let database of Object.keys(results)) { | ||
for (let entry of results[database]) { | ||
flatEntries.push(entry); | ||
} | ||
async fromMolecules(entries, ocl, options = {}) { | ||
const { databaseName = 'molecules', append = false } = options; | ||
const results = await (0, fromMolecules_js_1.fromMolecules)(entries, ocl, options); | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
} else { | ||
flatEntries = results; | ||
} | ||
let { widthFunction, zone = {}, threshold = 0.001, limit } = similarity; | ||
if (widthFunction && typeof widthFunction === 'string') { | ||
// eslint-disable-next-line no-new-func | ||
widthFunction = new Function('mass', widthFunction); | ||
let checkTopBottom = widthFunction(123); | ||
if (!checkTopBottom.bottom || !checkTopBottom.top) { | ||
throw Error( | ||
'widthFunction should return an object with bottom and top properties', | ||
); | ||
async fromRange(sequence, options = {}) { | ||
const { databaseName = 'generated', append = false, estimate } = options; | ||
const results = await (0, fromRange_js_1.fromRange)(sequence, options); | ||
if (estimate) | ||
return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
} | ||
const { low = -0.5, high = 2.5, auto } = zone; | ||
// we need to calculate the similarity of the isotopic distribution | ||
let similarityProcessor = new peaksSimilarity.Comparator(similarity); | ||
similarityProcessor.setPeaks1([experimentalData.x, experimentalData.y]); | ||
for (let i = 0; i < flatEntries.length; i++) { | ||
const entry = flatEntries[i]; | ||
if (onStep) await onStep(i); | ||
if (widthFunction) { | ||
width = widthFunction(entry.ms.em); | ||
async fromPeptidicSequence(sequence, options = {}) { | ||
const { databaseName = 'peptidic', append = false, estimate } = options; | ||
const results = await (0, fromPeptidicSequence_js_1.fromPeptidicSequence)(sequence, options); | ||
if (estimate) | ||
return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
let isotopicDistribution$1 = new isotopicDistribution.IsotopicDistribution(entry.mf, { | ||
allowNeutral: false, | ||
ionizations: [entry.ionization], | ||
fwhm: width.top / 2, | ||
threshold, | ||
limit, | ||
}); | ||
let distribution = isotopicDistribution$1.getDistribution(); | ||
// we need to define the comparison zone that depends of the charge | ||
let from, to; | ||
if (auto) { | ||
from = distribution.minX - 0.5; | ||
to = distribution.maxX + 0.5; | ||
similarityProcessor.setFromTo(from, to); | ||
} else { | ||
from = entry.ms.em + low / Math.abs(entry.ms.charge); | ||
to = entry.ms.em + high / Math.abs(entry.ms.charge); | ||
similarityProcessor.setFromTo(from, to); | ||
/** | ||
* | ||
* @param {string} databaseName | ||
* @param {object} [options={}] | ||
* @param {number} [options.precision=100] | ||
* @param {string} [options.ionizations=''] | ||
* @returns | ||
*/ | ||
async appendFragmentsInfo(databaseName, options = {}) { | ||
const database = this.databases[databaseName]; | ||
await (0, appendFragmentsInfo_js_1.appendFragmentsInfo)(this.experimentalSpectrum, database, options); | ||
return database; | ||
} | ||
if (widthFunction) { | ||
similarityProcessor.setTrapezoid(width.bottom, width.top); | ||
async fromNucleicSequence(sequence, options = {}) { | ||
const { databaseName = 'nucleic', append = false, estimate } = options; | ||
const results = await (0, fromNucleicSequence_js_1.fromNucleicSequence)(sequence, options); | ||
if (estimate) | ||
return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
similarityProcessor.setPeaks2([distribution.xs, distribution.ys]); | ||
let result = similarityProcessor.getSimilarity(); | ||
result.extractInfo1.from = from; | ||
result.extractInfo1.to = to; | ||
if (result.similarity > minSimilarity) { | ||
entry.ms.similarity = { | ||
value: result.similarity, | ||
experimental: result.extract1, | ||
theoretical: result.extract2, | ||
difference: result.diff, | ||
experimentalInfo: result.extractInfo1, | ||
thereoticalInfo: result.extractInfo2, | ||
quantity: result.extractInfo1.sum / sumY, | ||
factor: result.extractInfo1.max / result.extractInfo2.max, // by how much we should mulitply the extrat2 to reach the spectrum | ||
width, | ||
}; | ||
listDatabases() { | ||
return Object.keys(this.databases).sort(); | ||
} | ||
} | ||
if (!options.flatten) { | ||
for (let database of Object.keys(results)) { | ||
results[database] = results[database] | ||
.filter((entry) => entry.ms.similarity) | ||
.sort((a, b) => b.ms.similarity.value - a.ms.similarity.value); | ||
for (let entry of results[database]) { | ||
flatEntries.push(entry); | ||
} | ||
getInfo() { | ||
return { | ||
databases: Object.keys(this.databases) | ||
.sort() | ||
.map((key) => { | ||
return { name: key, nbEntries: this.databases[key].length }; | ||
}), | ||
}; | ||
} | ||
} else { | ||
results = results | ||
.filter((entry) => entry.ms.similarity) | ||
.sort((a, b) => b.ms.similarity.value - a.ms.similarity.value); | ||
} | ||
return results; | ||
} | ||
/** | ||
* Calculates a function that allows post-calibration on mass spectra based on the error in assignment | ||
* @param {*} similarities | ||
* @param {object} [options={}] | ||
* @returns | ||
*/ | ||
function massShifts(similarities, options = {}) { | ||
const { minSimilarity = 0.95, minLength = 10 } = options; | ||
let results = []; | ||
if (!Array.isArray(similarities)) { | ||
for (let key of results) { | ||
for (let entry of results[key]) { | ||
results.push(entry); | ||
} | ||
search(filter, options = {}) { | ||
return (0, search_js_1.search)(this, filter, options); | ||
} | ||
} else { | ||
results = similarities; | ||
} | ||
results = results.filter( | ||
(result) => | ||
result.ms && | ||
result.ms.similarity && | ||
result.ms.similarity.value > minSimilarity, | ||
); | ||
if (results.length < minLength) { | ||
throw new Error( | ||
`X rescale can not be applied. We need at least ${minLength} peaks with over ${Math.round( | ||
minSimilarity * 100, | ||
)}% similarity`, | ||
); | ||
} | ||
const data = results | ||
.map((result) => { | ||
return { | ||
em: result.ms.em, | ||
delta: result.ms.delta, | ||
}; | ||
}) | ||
.sort((a, b) => a.em - b.em); | ||
let shifts = { x: [], y: [] }; | ||
data.forEach((datum) => { | ||
shifts.x.push(Number(datum.em)); | ||
shifts.y.push(Number(datum.delta)); | ||
}); | ||
const regression = new mlRegressionTheilSen.TheilSenRegression(shifts.x, shifts.y); | ||
let minX = mlSpectraProcessing.xMinValue(shifts.x); | ||
let maxX = mlSpectraProcessing.xMaxValue(shifts.x); | ||
let shiftsPPM = { x: shifts.x, y: [] }; | ||
data.forEach((datum) => { | ||
shiftsPPM.y.push(Number((datum.delta / datum.em) * 1e6)); | ||
}); | ||
let regressionChart = { x: [], y: [] }; | ||
for (let i = minX; i < maxX; i += (maxX - minX) / 1000) { | ||
regressionChart.x.push(i); | ||
regressionChart.y.push(regression.predict(i)); | ||
} | ||
return { | ||
shifts, | ||
shiftsPPM, | ||
fit: regressionChart, | ||
score: regression.score(shifts.x, shifts.y), | ||
predictFct: regression.predict.bind(regression), | ||
tex: regression.toLaTeX(3), | ||
slope: regression.slope, | ||
intercept: regression.intercept, | ||
predictFctString: `${regression.slope} * mass + ${regression.intercept}`, | ||
}; | ||
} | ||
async function fetchJSON(url) { | ||
const result = await crossFetch__default["default"](url); | ||
return result.json(); | ||
} | ||
/** | ||
* A class that deals with database of monoisotopic mass and molecular formula | ||
*/ | ||
class EMDB { | ||
constructor() { | ||
this.databases = {}; | ||
this.experimentalSpectrum = undefined; | ||
} | ||
/** | ||
* | ||
* @param {*} data | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.normed=true] Should we normed (sum Y to 1) the experimental spectrum ? | ||
* @param {number} [options.threshold=0.00025] Threshold used for peak picking | ||
*/ | ||
setExperimentalSpectrum(data, options = {}) { | ||
const { normed = true, threshold = 0.00025 } = options; | ||
this.experimentalSpectrum = new msSpectrum.Spectrum(data, { threshold }); | ||
if (normed) { | ||
this.experimentalSpectrum.normedY(); | ||
searchMSEM(filter, options = {}) { | ||
return (0, searchMSEM_js_1.searchMSEM)(this, filter, options); | ||
} | ||
return this.experimentalSpectrum; | ||
} | ||
/** | ||
* Add a new database using the KnapSack content | ||
* @param {*} options | ||
*/ | ||
async loadKnapSack(options = {}) { | ||
const { databaseName = 'knapSack', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) return; | ||
this.databases[databaseName] = await loadKnapSack(); | ||
} | ||
/** | ||
* Add a new database of 12000 commercial products | ||
* @param {*} options | ||
*/ | ||
async loadCommercials(options = {}) { | ||
const { databaseName = 'commercials', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) return; | ||
this.databases[databaseName] = await loadCommercials(); | ||
} | ||
get(databaseName) { | ||
return this.databases[databaseName]; | ||
} | ||
/** | ||
* Load the contaminants database from a google sheet document | ||
* @param {object} [options={}] | ||
* @param {string} [options.databaseName='contaminants'] | ||
* @param {string} [options.forceReload=false] | ||
*/ | ||
async loadContaminants(options = {}) { | ||
const { databaseName = 'contaminants', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) return; | ||
this.databases[databaseName] = await loadGoogleSheet(); | ||
} | ||
/** | ||
* Load a google sheet containing MF information | ||
* @param {object} [options={}] | ||
* @param {string} [options.databaseName='sheet'] | ||
* @param {string} [options.forceReload=false] | ||
*/ | ||
async loadGoogleSheet(options = {}) { | ||
const { databaseName = 'sheet', forceReload = false } = options; | ||
if (this.databases[databaseName] && !forceReload) return; | ||
this.databases[databaseName] = await loadGoogleSheet(); | ||
} | ||
async loadTest() { | ||
await this.fromArray(['C1-100'], { | ||
databaseName: 'test', | ||
ionizations: '+', | ||
}); | ||
} | ||
async loadNeutralTest(options = {}) { | ||
const { maxC = 100 } = options; | ||
await this.fromArray([`C1-${maxC}`], { databaseName: 'test' }); | ||
} | ||
async fromMonoisotopicMass(mass, options = {}) { | ||
const { databaseName = 'monoisotopic', append = false } = options; | ||
let result = await fromMonoisotopicMass(mass, options); | ||
replaceOrAppend(this, databaseName, result.mfs, append); | ||
return result; | ||
} | ||
async fromArray(sequence, options = {}) { | ||
const { databaseName = 'generated', append = false, estimate } = options; | ||
const results = await fromArray(sequence, options); | ||
if (estimate) return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
async fromMolecules(entries, ocl, options = {}) { | ||
const { databaseName = 'molecules', append = false } = options; | ||
const results = await fromMolecules(entries, ocl, options); | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
async fromRange(sequence, options = {}) { | ||
const { databaseName = 'generated', append = false, estimate } = options; | ||
const results = await fromRange(sequence, options); | ||
if (estimate) return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
async fromPeptidicSequence(sequence, options = {}) { | ||
const { databaseName = 'peptidic', append = false, estimate } = options; | ||
const results = await fromPeptidicSequence(sequence, options); | ||
if (estimate) return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
/** | ||
* | ||
* @param {string} databaseName | ||
* @param {object} [options={}] | ||
* @param {number} [options.precision=100] | ||
* @param {string} [options.ionizations=''] | ||
* @returns | ||
*/ | ||
async appendFragmentsInfo(databaseName, options = {}) { | ||
const database = this.databases[databaseName]; | ||
await appendFragmentsInfo(this.experimentalSpectrum, database, options); | ||
return database; | ||
} | ||
async fromNucleicSequence(sequence, options = {}) { | ||
const { databaseName = 'nucleic', append = false, estimate } = options; | ||
const results = await fromNucleicSequence(sequence, options); | ||
if (estimate) return results; | ||
replaceOrAppend(this, databaseName, results, append); | ||
} | ||
listDatabases() { | ||
return Object.keys(this.databases).sort(); | ||
} | ||
getInfo() { | ||
return { | ||
databases: Object.keys(this.databases) | ||
.sort() | ||
.map((key) => { | ||
return { name: key, nbEntries: this.databases[key].length }; | ||
}), | ||
}; | ||
} | ||
search(filter, options = {}) { | ||
return search(this, filter, options); | ||
} | ||
searchMSEM(filter, options = {}) { | ||
return searchMSEM(this, filter, options); | ||
} | ||
searchSimilarity(options = {}) { | ||
return searchSimilarity(this, options); | ||
} | ||
searchSimilarity(options = {}) { | ||
return (0, searchSimilarity_js_1.searchSimilarity)(this, options); | ||
} | ||
} | ||
exports.EMDB = EMDB; | ||
function replaceOrAppend(emdb, databaseName, results, append = false) { | ||
if (!emdb.databases[databaseName] || !append) { | ||
emdb.databases[databaseName] = results; | ||
return; | ||
} | ||
emdb.databases[databaseName] = emdb.databases[databaseName].concat(results); | ||
if (!emdb.databases[databaseName] || !append) { | ||
emdb.databases[databaseName] = results; | ||
return; | ||
} | ||
emdb.databases[databaseName] = emdb.databases[databaseName].concat(results); | ||
} | ||
exports.EMDB = EMDB; | ||
exports.fetchJSON = fetchJSON; | ||
exports.massShifts = massShifts; |
{ | ||
"name": "emdb", | ||
"version": "3.2.2", | ||
"version": "3.3.0", | ||
"description": "Database manager for exact mass query", | ||
@@ -23,28 +23,28 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"chemical-elements": "^2.0.4", | ||
"chemical-groups": "^2.1.1", | ||
"chemical-elements": "^2.1.0", | ||
"chemical-groups": "^2.2.0", | ||
"cross-fetch": "^4.0.0", | ||
"isotopic-distribution": "^3.1.3", | ||
"isotopic-distribution": "^3.2.0", | ||
"jszip": "^3.10.1", | ||
"mass-fragmentation": "^1.9.4", | ||
"mf-finder": "^3.3.1", | ||
"mf-from-google-sheet": "^3.0.7", | ||
"mf-generator": "^3.2.1", | ||
"mf-matcher": "^3.1.1", | ||
"mf-parser": "^3.1.1", | ||
"mf-utilities": "^3.1.1", | ||
"mass-fragmentation": "^1.10.0", | ||
"mf-finder": "^3.4.0", | ||
"mf-from-google-sheet": "^3.1.0", | ||
"mf-generator": "^3.3.0", | ||
"mf-matcher": "^3.2.0", | ||
"mf-parser": "^3.2.0", | ||
"mf-utilities": "^3.2.0", | ||
"ml-regression-theil-sen": "^3.0.0", | ||
"ml-spectra-processing": "^14.5.0", | ||
"ms-spectrum": "^3.5.2", | ||
"nucleotide": "^3.0.3", | ||
"openchemlib-utils": "^5.19.1", | ||
"ml-spectra-processing": "^14.5.1", | ||
"ms-spectrum": "^3.6.0", | ||
"nucleotide": "^3.1.0", | ||
"openchemlib-utils": "^6.0.1", | ||
"peaks-similarity": "^3.1.1", | ||
"peptide": "^2.1.2" | ||
"peptide": "^2.2.0" | ||
}, | ||
"devDependencies": { | ||
"jest-matcher-deep-close-to": "^3.0.2", | ||
"openchemlib": "^8.10.0", | ||
"openchemlib": "^8.14.0", | ||
"xy-parser": "^5.0.5" | ||
}, | ||
"gitHead": "11531f8e222bd2a964a518a2fa14bf4d42c2507e" | ||
"gitHead": "28dae91d3b42556a23097ee08acfe4061f276ed0" | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
141374
57
3047
3
- Removedopenchemlib-utils@5.21.1(transitive)
Updatedchemical-elements@^2.1.0
Updatedchemical-groups@^2.2.0
Updatedisotopic-distribution@^3.2.0
Updatedmass-fragmentation@^1.10.0
Updatedmf-finder@^3.4.0
Updatedmf-from-google-sheet@^3.1.0
Updatedmf-generator@^3.3.0
Updatedmf-matcher@^3.2.0
Updatedmf-parser@^3.2.0
Updatedmf-utilities@^3.2.0
Updatedms-spectrum@^3.6.0
Updatednucleotide@^3.1.0
Updatedopenchemlib-utils@^6.0.1
Updatedpeptide@^2.2.0