Spectrum

Class dealing with mass spectra and peak picking

new Spectrum(data: any, options: object)
Parameters
data (any = defaultData)
options (object = {})
Instance Members
peaks
clearCache()
maxY(options)
getPeaksWithCharge(options)
getSelectedPeaksWithCharge(selectedPeaks, options)
getChargeClusters(options)
setIonizations(ionizations)
getChargeLadders()
getFragmentPeaksFct(mf, options)
getMassRemainderFct(mass, options)

getChargeClusters

Group the peaks into isotopologue clusters and give a charge to each cluster.

The charge of a single peak can not be evaluated: an isotopologue in the middle of an envelope looks the same whatever the charge, and the last one of an envelope has nothing after it. What carries the charge is the series: a run of peaks separated by NEUTRON_MASS / charge. So the series are searched first, and the charge of a series is given to all the peaks it holds.

A series of charge 1 is always a subseries of a series of charge 2, which is one of a series of charge 4: the clusters are therefore assigned from the one explaining the most intensity to the one explaining the least, and a peak only takes the charge of the first cluster that claims it.

getChargeClusters(peaks: Array<{x: number, y: number}>, options: object): Array<{charge: number, peaks: Array}>
Parameters
peaks (Array<{x: number, y: number}>) sorted by mass
options (object = {})
Name Description
options.minCharge number (default 1)
options.maxCharge number (default 10)
options.precision number (default 20) tolerance on the position of an isotopologue, in ppm. It is never allowed to reach half of the distance between two isotopologues, otherwise a series could jump from one to another.
options.minLength number (default 3) two peaks that happen to be at the right distance are common, three in a row much less
Returns
Array<{charge: number, peaks: Array}>: the clusters, from the one holding the most intensity to the one holding the least

buildClusters

The clusters as the two functions above need them: the position of the peaks rather than the peaks, and the intensity they hold to be able to sort them.

buildClusters(peaks: Array<{x: number, y: number}>, options: object): Array<{charge: number, indexes: Array<number>, intensity: number}>
Parameters
peaks (Array<{x: number, y: number}>)
options (object = {})
Returns
Array<{charge: number, indexes: Array<number>, intensity: number}>:

getPeaksWithClusterCharge

Give to each peak the charge of the cluster that explains it.

getPeaksWithClusterCharge(peaks: Array<{x: number, y: number}>, options: object): Array
Parameters
peaks (Array<{x: number, y: number}>) sorted by mass
options (object = {}) same as getChargeClusters
Returns
Array: copy of the peaks, with a charge when one was found

isOnPosition

Is the peak at index where the next isotopologue is expected?

The isotopologues of the charges z and z + 1 are only spacing / (z + 1) apart, and that distance shrinks fast: 0.5 Da between the charges 1 and 2, but 0.024 Da between 6 and 7. A tolerance that reaches it would let the same peaks be read with either charge, so it is capped well under.

isOnPosition(masses: Float64Array, index: number, expected: number, spacing: number, charge: number, precision: number): boolean
Parameters
masses (Float64Array)
index (number)
expected (number)
spacing (number)
charge (number)
precision (number) in ppm
Returns
boolean:

getChargeLadders

Group the peaks of a multiply charged species into charge-state ladders.

An electrospray of a protein does not show its isotopologues but a ladder of charge states: the same molecule of neutral mass M ionized a growing number of times by a charge carrier (a proton, a sodium, ...). A peak of charge z sits at m/z = M / z + delta, where delta is the m/z shift of one charge carrier, so a whole series shares one M while each of its peaks has its own charge. The charge of a single peak can therefore not be read on its own: what shows it is the ladder it belongs to.

A ladder is grown from its most intense peak by predicting, one charge state at a time, where the next one must lie, and it is kept only when it spans at least minLength consecutive charge states. Ladders are assigned from the one holding the most intensity to the one holding the least, and a peak only takes the charge of the first ladder that claims it, so a harmonic (every other state of a real ladder) can not steal the peaks of the series it mimics.

getChargeLadders(peaks: Array<{x: number, y: number}>, options: object): Array<{mass: number, ionization: string, peaks: Array}>
Parameters
peaks (Array<{x: number, y: number}>) the peaks of the spectrum
options (object = {})
Name Description
options.ionizations (string | Array) (default 'H+') the charge carriers to try, as a comma separated list ( 'H+,Na+,K+' ) or an array of preprocessed ionizations. Each envelope is assigned the carrier that explains it best.
options.maxCharge number (default 100) highest charge state to consider; the ladder is grown over the states 1 to maxCharge (its magnitude is used, so a negative-mode range such as -100 behaves like 100 )
options.tolerance number (default 500) tolerance on the position of the next charge state, in ppm. Larger than for isotopologues because the charge states of a protein are broad and rarely mass resolved.
options.minLength number (default 5) shortest ladder that shows a charge
options.minRelativeIntensity number (default 0.05) peaks under this fraction of the most intense one are satellites, adducts or noise and take no part in the ladders
Returns
Array<{mass: number, ionization: string, peaks: Array}>: the ladders, from the one holding the most intensity to the one holding the least. mass is the neutral mass they reconstruct, ionization the carrier that explains them and each peak carries its charge .

assignLadderCharge

Give to each of selectedPeaks the charge of the ladder peak at its mass.

A peak that matches no ladder peak gets no charge. Used by getPeaksWithCharge to complete the charge the isotopologue clusters can not give to a multiply charged species.

assignLadderCharge(selectedPeaks: Array, ladders: Array<{peaks: Array}>, tolerance: number): Array
Parameters
selectedPeaks (Array) peaks to evaluate
ladders (Array<{peaks: Array}>) as returned by getChargeLadders
tolerance (number = 20) tolerance, in ppm, to match a selected peak to a ladder peak
Returns
Array: copy of selectedPeaks , carrying a charge when matched

buildLadders

The ladders the exported functions need, each with its reconstructed neutral mass, its carrier and its peaks (carrying their charge), sorted from the one holding the most intensity to the one holding the least.

buildLadders(peaks: Array<{x: number, y: number}>, options: object): Array<{mass: number, ionization: string, intensity: number, peaks: Array}>
Parameters
peaks (Array<{x: number, y: number}>)
options (object = {})
Returns
Array<{mass: number, ionization: string, intensity: number, peaks: Array}>:

growLadder

Grow a ladder around an anchor peak assumed to carry charge.

The next charge state is predicted from the last matched peak rather than from the anchor, so the error of a broad, unresolved peak never accumulates over the whole ladder. Growth stops at the first state that is missing or already claimed by a more intense ladder.

growLadder(significant: Array<{x: number, claim: boolean}>, masses: Float64Array, anchor: {x: number}, charge: number, bounds: object): Array<{peak: object, charge: number}>
Parameters
significant (Array<{x: number, claim: boolean}>) the significant peaks, sorted by mass
masses (Float64Array) their masses, for the closest-peak search
anchor ({x: number}) the peak to grow from
charge (number) hypothesised charge of the anchor
bounds (object)
Name Description
bounds.from number lowest charge state
bounds.to number highest charge state
bounds.relativeTolerance number tolerance as a plain factor (ppm * 1e-6)
bounds.delta number m/z shift of one charge carrier
Returns
Array<{peak: object, charge: number}>:

getFragmentPeaks

Filter the array of peaks

getFragmentPeaks(peaks: array, mf: string, options: object): array
Parameters
peaks (array) array of all the peaks
mf (string) Molecular formula of the parent molecule
options (object = {})
Name Description
options.from number? min X value of the window to consider
options.to number? max X value of the window to consider
options.threshold number (default 0.01) minimal intensity compare to base peak
options.limit number (default undefined) maximal number of peaks (based on intensity)
options.ionizations string?
options.precision number?
Returns
array: copy of peaks with 'close' annotation

getMassRemainder

Remove an integer number of time the specifiedd monoisotopic mass. Mass remainder analysis (MARA): https://doi.org/10.1021/acs.analchem.7b04730

getMassRemainder(spectrum: object, mass: number, options: object)
Parameters
spectrum (object)
mass (number)
options (object = {})
Name Description
options.delta number (default 0.001)

getPeakChargeBySimilarity

Evaluate the charge of the peak observed at a mass.

The charge is the one of the isotopologue cluster the peak belongs to, see getChargeClusters. A peak on its own shows no charge: what shows it is the series of peaks around it, separated by one dalton over the charge.

getPeakChargeBySimilarity(spectrum: any, targetMass: number, options: object): (number | undefined)
Parameters
spectrum (any)
targetMass (number)
options (object = {})
Name Description
options.minCharge number (default 1)
options.maxCharge number (default 10)
options.precision number (default 20) tolerance on the position of an isotopologue, in ppm
options.minLength number (default 3) shortest series that shows a charge
options.minSignalToNoise number (default 10) a peak under median plus minSignalToNoise times the standard deviation of the noise takes no part in the series: the position of a maximum of the noise is random and would give a charge to what is only noise. Only for a continuous spectrum, a list of centroids does not describe its noise anymore.
Returns
(number | undefined): the charge, or undefined when no series of peaks shows one at that mass

getMinIntensity

Intensity under which a peak is too close to the noise to say anything about a charge. The peak picking already removes what is under median + 3 sd, but a gaussian noise still leaves some of its maxima over it.

getMinIntensity(spectrum: any, options: object): number
Parameters
spectrum (any)
options (object = {})
Name Description
options.minSignalToNoise number (default 10)
Returns
number:

getPeaksWithCharge

Evaluate the charge of every peak.

The charge comes from the isotopologue clusters: a peak takes the charge of the series it belongs to. Evaluating a peak on its own can not work, because an isotopologue in the middle of an envelope looks the same whatever the charge and the last one of an envelope has nothing after it.

A multiply charged species (a protein electrospray, ...) shows no resolved isotopologues but a ladder of charge states. When such ladders are present (an unresolved spectrum, see maxClusteredFraction) they take precedence: an unresolved envelope peak often forms a spurious charge-1 cluster from its neighbours, and the ladder gives the real, much higher charge. A peak that belongs to neither gets no charge at all.

getPeaksWithCharge(peaks: Array, options: object): Array
Parameters
peaks (Array) all the peaks of the spectrum, sorted by mass
options (object = {})
Name Description
options.min number (default 1) lowest charge the isotopologue clusters consider
options.max number (default 100) highest charge to consider; shared by the isotopologue clusters and the charge-state ladder (which spans 1 to max )
options.precision number (default 20) tolerance on the position of an isotopologue, in ppm (isotopologue clusters)
options.minLength number (default 3) shortest isotopologue series that shows a charge. This is the cluster length, not the ladder's: a ladder has its own minLength (default 5) inside options.ladder
options.minIntensity number (default 0) peaks under it are noise and take no part in the series
options.ionizations (string | Array) (default 'H+') the charge carriers a charge-state ladder may show, see getChargeLadders
options.maxClusteredFraction number (default 0.2) the charge-state ladders are ignored when the isotopologue clusters already explain more than this fraction of the significant peaks: a resolved spectrum (where the ladders would only be coincidences) is read from its isotopologues alone
options.ladder object (default {}) options forwarded to getChargeLadders , kept apart because a ladder is broader than an isotopologue and needs its own tolerances. The min / precision / minLength above are the isotopologue cluster parameters and do not reach the ladder; max is shared and caps both, so the ladder has no maxCharge of its own
options.ladder.tolerance number (default 500) tolerance on the position of the next charge state, in ppm. Larger than for isotopologues because the charge states of a protein are broad and rarely mass resolved
options.ladder.minLength number (default 5) shortest ladder that shows a charge
options.ladder.minRelativeIntensity number (default 0.05) peaks under this fraction of the most intense one are satellites, adducts or noise and take no part in the ladders
Returns
Array: copy of peaks , with a charge when one was found

getChargeAtMass

Charge of the clustered peak lying at a mass, if there is one there.

getChargeAtMass(clustered: Array, masses: any, targetMass: number, precision: number): (number | undefined)
Parameters
clustered (Array) peaks carrying their charge, sorted by mass
masses (any)
targetMass (number)
precision (number) in ppm
Returns
(number | undefined):

isContinuous

When a spectrum is continous ?

  • has more than 100 points
  • deltaX change can not be more than a factor 2
  • deltaX may not be larger than what maxDeltaX allows at that mass
  • if y is zero it does not count
isContinuous(spectrum: object, options: object)
Parameters
spectrum (object)
options (object = {})
Name Description
options.minLength number (default 100)
options.relativeHeightThreshold number (default 0.001) // Under this value the
options.maxDeltaRatio number (default 3)
options.maxDeltaX function? function called with the mass that returns the largest step a profile spectrum may have there. Defaults to defaultMaxDeltaX .

defaultMaxDeltaX

Largest step a profile spectrum may have around a mass.

A time of flight is sampled evenly in time and an orbitrap evenly in frequency, so in both the m/z step grows with the m/z: a MALDI-TOF is sampled every 0.19 Da around m/z 1000 and every 0.47 Da around m/z 6000, and is still a profile spectrum. The step is therefore allowed to grow with the mass, between two bounds:

  • never under 0.1 Da, so that a spectrum of small molecules keeps the step it always had
  • never over 0.6 Da, because the isotopologues of a singly charged ion are one dalton apart: over that a profile could not describe them, and a list of centroids would pass for one whatever its mass
defaultMaxDeltaX(mass: number): number
Parameters
mass (number)
Returns
number:

peakPicking

Filter the array of peaks

peakPicking(spectrum: any): array
Parameters
spectrum (any)
Returns
array:

getNoiseLevel

Intensity under which a maximum is considered to be noise.

We have to compute it ourselves: left alone, gsd only estimates a noise level when the x values are equally spaced within 5%, and falls back to 0 otherwise. A mass spectrum is sampled in time or in frequency, so over a wide m/z range the steps grow by far more than that and the noise would not be filtered.

getNoiseLevel(y: any, noiseFactor: number): (number | undefined)
Parameters
y (any)
noiseFactor (number = 3) 0 leaves the decision to gsd
Returns
(number | undefined):

getPeaks

Filter the array of peaks

getPeaks(peaks: array, options: object): array
Parameters
peaks (array) array of all the peaks
options (object = {})
Name Description
options.from number? min X value of the window to consider
options.to number? max X value of the window to consider
options.threshold number (default 0.01) minimal intensity compare to base peak
options.limit number (default undefined) maximal number of peaks (based on intensity)
options.sumValue number? // if sumValue is defined, maxValue is ignored
Returns
array: copy of peaks with 'close' annotation

getBestPeaks

Filter the array by taking the higher peaks and only keep one per slot. There are 2 different slots, the smallest one will have the extra annotation close to true

getBestPeaks(peaks: array, options: object): array
Parameters
peaks (array) array of all the peaks
options (object = {})
Name Description
options.from number? min X value of the window to consider
options.to number? max X value of the window to consider
options.minValue number (default Number.NEGATIVE_INFINITY) min Y value of the window to consider
options.maxValue number (default Number.POSITIVE_INFINITY) max Y value of the window to consider
options.searchMonoisotopicRatio number (default 0) search previous peaks with at least ratio height
options.limit number (default 20) max number of peaks
options.threshold number (default 0.01) minimal intensity compare to base peak
options.numberSlots number (default 10) define the number of slots and indirectly the slot width
options.numberCloseSlots number (default 50)
Returns
array: copy of peaks with 'close' annotation

constructor

Create a class that will be able to get the similarity between 2 spectra The similarity is based on 'cosine' similarity. The goal is 2 prepare 2 vectors on which the similarity is calculated. The vectors are created by taking the mass and the intensity of the peaks.

constructor(options: object)
Parameters
options (object = {})
Name Description
options.nbPeaks number? Before comparing spectra how many peaks should be kept
options.minNbCommonPeaks number? Minimum number of peaks in common to consider any similarity
options.minIntensity number? What is the minimal relative intensity to keep a peak
options.massPower number (default 3) High power will give more weight to the mass. If you would prefer to observe fragments you should use a number less than 1
options.intensityPower number (default 0.6) How important is the intensity. By default we don't give to much importance to it
options.selectedMasses Array<number>? List of allowed masses.
options.delta (number | Function) (default 0.1) Tolerance in Da (u) to consider 2 peaks as aligned. If a function is provided it will be called with the mass of the peak

getSimilarityToMasses

Get the similarity between a spectrum and a list of masses. The main issue is that we don't have the intensity of the peaks. So we will use the intensity of the closest peak.

getSimilarityToMasses(dataXY: any, masses: Array<number>)
Parameters
dataXY (any)
masses (Array<number>)

getSimilarity

getSimilarity(dataXY1: any, dataXY2: any)
Parameters
dataXY1 (any)
dataXY2 (any)

normalizeAndCacheData

normalizeAndCacheData(cache: WeakMap, dataXY: any, options: object)
Parameters
cache (WeakMap)
dataXY (any)
options (object = {})
Name Description
options.nbPeaks number?
options.minIntensity number?

returnSimilarity

returnSimilarity(aligned: any, options: object)
Parameters
aligned (any)
options (object = {})
Name Description
options.massPower number?
options.intensityPower number?
options.minNbCommonPeaks number?

cosineSimilarity

Returns the average of cosine distances between vectors a and b Copied from https://github.com/mljs/distance/blob/0b15acd6476413f4111cb4852ca1bec9edaa2805/src/similarities/cosine.ts

cosineSimilarity(a: any, b: any): number
Parameters
a (any) {import('cheminfo-types').NumberArray} - first vector
b (any) {import('cheminfo-types').NumberArray} - second vector
Returns
number: cosine similarity

getPeaksAnnotation

getPeaksAnnotation(bestPeaks: array, options: object): Promise
Parameters
bestPeaks (array)
options (object = {})
Name Description
options.numberDigits number (default 5)
options.numberMFs number (default 0)
options.showMF boolean (default false)
options.mfColors array (default {})
options.charge number (default 1)
options.shift number (default 0)
options.mfPrefs object?
options.displayCharge number (default true)
options.displayProperties number (default []) Array of properties name to display
Returns
Promise:

fromMonoisotopicMass

Generates a database 'monoisotopic' from a monoisotopic mass and various options

fromMonoisotopicMass(masses: (number | string | array), options: object): Promise
Parameters
masses ((number | string | array)) Monoisotopic mass
options (object = {})
Name Description
options.maxIterations number (default 10000000) Maximum number of iterations
options.onStep function? Callback to do after each step
options.allowNeutral boolean (default true)
options.uniqueMFs boolean (default true)
options.limit number (default 1000) Maximum number of results
options.ionizations string (default '') string containing a comma separated list of modifications
options.ranges string (default 'C0-100 H0-100 O0-100 N0-100') range of mfs to search
options.precision number (default 100) Allowed mass range based on precision
options.filter object (default {})
options.filter.minCharge number (default -Infinity) Minimal charge
options.filter.maxCharge number (default +Infinity) Maximal charge
options.filter.absoluteCharge boolean (default false) If true, the charge is absolute (so between 0 and +Infinity by default)
options.filter.unsaturation object (default {})
options.filter.unsaturation.min number (default -Infinity) Minimal unsaturation
options.filter.unsaturation.max number (default +Infinity) Maximal unsaturation
options.filter.unsaturation.onlyInteger boolean (default false) Integer unsaturation
options.filter.unsaturation.onlyNonInteger boolean (default false) Non integer unsaturation
options.filter.atoms object? object of atom:{min, max}
options.filter.callback function? a function to filter the MF
Returns
Promise: