Comparing version 6.1.2 to 6.2.0
# Changelog | ||
## [6.2.0](https://www.github.com/mljs/global-spectral-deconvolution/compare/v6.1.2...v6.2.0) (2020-12-05) | ||
### Features | ||
* add factorLimits parameter for optimizePeaks ([1af47e1](https://www.github.com/mljs/global-spectral-deconvolution/commit/1af47e1a32cfef957cc43078ae389ecc4f2d691b)) | ||
* add failling test case ([a061013](https://www.github.com/mljs/global-spectral-deconvolution/commit/a06101315d97e70bc19119dad3692eff64848568)) | ||
* improve groupPeaks ([6ef7024](https://www.github.com/mljs/global-spectral-deconvolution/commit/6ef7024a1e34ce63c71fa0679946df74271d3c88)) | ||
* simplify optimizePeaks ([3abfe93](https://www.github.com/mljs/global-spectral-deconvolution/commit/3abfe93a4cc938d799acc495fea959d15db272a3)) | ||
* update ml-spectra-fitting ([97899d5](https://www.github.com/mljs/global-spectral-deconvolution/commit/97899d5f3453d6f0b0a4afa0c8102b07c5d69a32)) | ||
### Bug Fixes | ||
* always ascending order in x dimension ([6ffc64f](https://www.github.com/mljs/global-spectral-deconvolution/commit/6ffc64f288220f4d004b7627c2fb44caa9b21bbf)) | ||
* update dependencies ([5ea4c7f](https://www.github.com/mljs/global-spectral-deconvolution/commit/5ea4c7fbb042c2145e760b97aab6c92788eeb117)) | ||
### [6.1.2](https://www.github.com/mljs/global-spectral-deconvolution/compare/v6.1.1...v6.1.2) (2020-11-18) | ||
@@ -4,0 +21,0 @@ |
203
lib/index.js
@@ -8,2 +8,3 @@ 'use strict'; | ||
var mlSpectraFitting = require('ml-spectra-fitting'); | ||
var mlSpectraProcessing = require('ml-spectra-processing'); | ||
@@ -354,2 +355,33 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
/** | ||
* Group peaks based on factor and add group property in peaks | ||
* @param {array} peakList | ||
* @param {number} factor | ||
*/ | ||
function groupPeaks(peakList, factor = 1) { | ||
if (peakList.length === 0) return []; | ||
let peaks = peakList.sort((a, b) => a.x - b.x); | ||
let previousPeak = { x: Number.NEGATIVE_INFINITY, width: 1 }; | ||
let currentGroup = [previousPeak]; | ||
let groups = []; | ||
for (let peak of peaks) { | ||
if ( | ||
(peak.x - previousPeak.x) / (peak.width + previousPeak.width) <= | ||
factor / 2 | ||
) { | ||
currentGroup.push(peak); | ||
} else { | ||
currentGroup = [peak]; | ||
groups.push(currentGroup); | ||
} | ||
peak.group = groups.length - 1; | ||
previousPeak = peak; | ||
} | ||
return groups; | ||
} | ||
/** | ||
* Optimize the position (x), max intensity (y), full width at half maximum (width) | ||
@@ -361,3 +393,3 @@ * and the ratio of gaussian contribution (mu) if it's required. It supports three kind of shapes: gaussian, lorentzian and pseudovoigt | ||
* @param {number} [options.factorWidth = 1] - times of width to group peaks. | ||
* @param {object} [options.joinPeaks = true] - if true the peaks could be grouped if the separation between them are inside of a range of factorWidth * width | ||
* @param {number} [options.factorLimits = 2] - times of width to use to optimize peaks | ||
* @param {object} [options.shape={}] - it's specify the kind of shape used to fitting. | ||
@@ -373,3 +405,3 @@ * @param {string} [options.shape.kind = 'gaussian'] - kind of shape; lorentzian, gaussian and pseudovoigt are supported. | ||
factorWidth = 1, | ||
joinPeaks = true, | ||
factorLimits = 2, | ||
shape = { | ||
@@ -383,152 +415,33 @@ kind: 'gaussian', | ||
let { x, y } = data; | ||
let lastIndex = [0]; | ||
let groups = groupPeaks(peakList, factorWidth, joinPeaks); | ||
let result = []; | ||
let sampling; | ||
for (let i = 0; i < groups.length; i++) { | ||
let peaks = groups[i].group; | ||
if (peaks.length > 1) { | ||
// Multiple peaks | ||
sampling = sampleFunction( | ||
groups[i].limits[0] - groups[i].limits[1], | ||
groups[i].limits[0] + groups[i].limits[1], | ||
x, | ||
y, | ||
lastIndex, | ||
); | ||
if (sampling.x.length > 5) { | ||
let { peaks: optPeaks } = mlSpectraFitting.optimize(sampling, peaks, { | ||
shape, | ||
optimization, | ||
}); | ||
for (let j = 0; j < optPeaks.length; j++) { | ||
optPeaks[j].index = peaks.index; | ||
optPeaks[j].group = i; | ||
result.push(optPeaks[j]); | ||
} | ||
} | ||
} else { | ||
// Single peak | ||
peaks = peaks[0]; | ||
sampling = sampleFunction( | ||
peaks.x - factorWidth * peaks.width, | ||
peaks.x + factorWidth * peaks.width, | ||
x, | ||
y, | ||
lastIndex, | ||
); | ||
if (sampling.x.length > 5) { | ||
let fitResult = mlSpectraFitting.optimize(sampling, [peaks], { | ||
shape, | ||
optimization, | ||
}); | ||
let { peaks: optPeaks } = fitResult; | ||
optPeaks[0].index = peaks.index; | ||
optPeaks[0].group = i; | ||
result.push(optPeaks[0]); | ||
} | ||
} | ||
if (data.x[0] > data.x[1]) { | ||
data.x.reverse(); | ||
data.y.reverse(); | ||
} | ||
return result; | ||
} | ||
function sampleFunction(from, to, x, y, lastIndex) { | ||
let nbPoints = x.length; | ||
let sampleX = []; | ||
let sampleY = []; | ||
let direction = Math.sign(x[1] - x[0]); // Direction of the derivative | ||
if (direction === -1) { | ||
lastIndex[0] = x.length - 1; | ||
} | ||
let groups = groupPeaks(peakList, factorWidth); | ||
let delta = Math.abs(to - from) / 2; | ||
let mid = (from + to) / 2; | ||
let stop = false; | ||
let index = lastIndex[0]; | ||
while (!stop && index < nbPoints && index >= 0) { | ||
if (Math.abs(x[index] - mid) <= delta) { | ||
sampleX.push(x[index]); | ||
sampleY.push(y[index]); | ||
index += direction; | ||
} else { | ||
// It is outside the range. | ||
if (Math.sign(mid - x[index]) === 1) { | ||
// We'll reach the mid going in the current direction | ||
index += direction; | ||
} else { | ||
// There is not more peaks in the current range | ||
stop = true; | ||
} | ||
} | ||
} | ||
lastIndex[0] = index; | ||
return { x: sampleX, y: sampleY }; | ||
} | ||
let results = []; | ||
for (const peaks of groups) { | ||
const firstPeak = peaks[0]; | ||
const lastPeak = peaks[peaks.length - 1]; | ||
function groupPeaks(peakList, nL, joinPeaks) { | ||
let group = []; | ||
let groups = []; | ||
let limits = [peakList[0].x, nL * peakList[0].width]; | ||
let upperLimit, lowerLimit; | ||
// Merge forward | ||
for (let i = 0; i < peakList.length; i++) { | ||
// If the 2 things overlaps | ||
if ( | ||
joinPeaks && | ||
Math.abs(peakList[i].x - limits[0]) < nL * peakList[i].width + limits[1] | ||
) { | ||
// Add the peak to the group | ||
group.push(peakList[i]); | ||
// Update the group limits | ||
upperLimit = limits[0] + limits[1]; | ||
if (peakList[i].x + nL * peakList[i].width > upperLimit) { | ||
upperLimit = peakList[i].x + nL * peakList[i].width; | ||
} | ||
lowerLimit = limits[0] - limits[1]; | ||
if (peakList[i].x - nL * peakList[i].width < lowerLimit) { | ||
lowerLimit = peakList[i].x - nL * peakList[i].width; | ||
} | ||
limits = [ | ||
(upperLimit + lowerLimit) / 2, | ||
Math.abs(upperLimit - lowerLimit) / 2, | ||
]; | ||
const from = firstPeak.x - firstPeak.width * factorLimits; | ||
const to = lastPeak.x + lastPeak.width * factorLimits; | ||
const { fromIndex, toIndex } = mlSpectraProcessing.xGetFromToIndex(data.x, { from, to }); | ||
// Multiple peaks | ||
const currentRange = { | ||
x: data.x.slice(fromIndex, toIndex), | ||
y: data.y.slice(fromIndex, toIndex), | ||
}; | ||
if (currentRange.x.length > 5) { | ||
let { peaks: optimizedPeaks } = mlSpectraFitting.optimize(currentRange, peaks, { | ||
shape, | ||
optimization, | ||
}); | ||
results = results.concat(optimizedPeaks); | ||
} else { | ||
groups.push({ limits: limits, group: group }); | ||
group = [peakList[i]]; | ||
limits = [peakList[i].x, nL * peakList[i].width]; | ||
results = results.concat(peaks); | ||
} | ||
} | ||
groups.push({ limits: limits, group: group }); | ||
// Merge backward | ||
for (let i = groups.length - 2; i >= 0; i--) { | ||
// The groups overlaps | ||
if ( | ||
Math.abs(groups[i].limits[0] - groups[i + 1].limits[0]) < | ||
(groups[i].limits[1] + groups[i + 1].limits[1]) / 2 | ||
) { | ||
for (let j = 0; j < groups[i + 1].group.length; j++) { | ||
groups[i].group.push(groups[i + 1].group[j]); | ||
} | ||
upperLimit = groups[i].limits[0] + groups[i].limits[1]; | ||
if (groups[i + 1].limits[0] + groups[i + 1].limits[1] > upperLimit) { | ||
upperLimit = groups[i + 1].limits[0] + groups[i + 1].limits[1]; | ||
} | ||
lowerLimit = groups[i].limits[0] - groups[i].limits[1]; | ||
if (groups[i + 1].limits[0] - groups[i + 1].limits[1] < lowerLimit) { | ||
lowerLimit = groups[i + 1].limits[0] - groups[i + 1].limits[1]; | ||
} | ||
groups[i].limits = [ | ||
(upperLimit + lowerLimit) / 2, | ||
Math.abs(upperLimit - lowerLimit) / 2, | ||
]; | ||
groups.splice(i + 1, 1); | ||
} | ||
} | ||
return groups; | ||
return results; | ||
} | ||
@@ -535,0 +448,0 @@ |
{ | ||
"name": "ml-gsd", | ||
"version": "6.1.2", | ||
"version": "6.2.0", | ||
"description": "Global Spectra Deconvolution", | ||
@@ -16,2 +16,3 @@ "main": "lib/index.js", | ||
"build": "rollup -c && cheminfo-build --entry src/index.js --root GSD", | ||
"example": "nodemon -w src -w examples/example.js -r esm examples/example.js", | ||
"eslint": "eslint src --cache", | ||
@@ -57,9 +58,10 @@ "eslint-fix": "npm run eslint -- --fix", | ||
"@babel/plugin-transform-modules-commonjs": "^7.12.1", | ||
"@types/jest": "^26.0.16", | ||
"chemcalc": "^3.4.1", | ||
"cheminfo-build": "^1.1.8", | ||
"eslint": "^7.13.0", | ||
"eslint": "^7.14.0", | ||
"eslint-config-cheminfo": "^5.2.2", | ||
"eslint-plugin-import": "^2.22.1", | ||
"eslint-plugin-jest": "^24.1.3", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"eslint-plugin-prettier": "^3.2.0", | ||
"esm": "^3.2.25", | ||
@@ -70,12 +72,14 @@ "jest": "^26.6.3", | ||
"ml-stat": "^1.3.3", | ||
"prettier": "^2.1.2", | ||
"rollup": "^2.33.3", | ||
"spectrum-generator": "^4.4.0", | ||
"nodemon": "^2.0.6", | ||
"prettier": "^2.2.1", | ||
"rollup": "^2.34.1", | ||
"spectrum-generator": "^4.4.2", | ||
"xy-parser": "^3.0.0" | ||
}, | ||
"dependencies": { | ||
"ml-savitzky-golay-generalized": "2.0.2", | ||
"ml-spectra-fitting": "^0.7.1", | ||
"ml-peak-shape-generator": "^0.10.2" | ||
"ml-peak-shape-generator": "^0.10.2", | ||
"ml-savitzky-golay-generalized": "2.0.3", | ||
"ml-spectra-fitting": "0.9.0", | ||
"ml-spectra-processing": "^4.9.4" | ||
} | ||
} |
@@ -6,12 +6,10 @@ import { gsd, optimizePeaks } from '..'; | ||
describe('Global spectra deconvolution with simulated spectra', () => { | ||
// Test case obtained from Pag 443, Chap 8. | ||
it('Should provide the right result ...', () => { | ||
it('Overlapping peaks', () => { | ||
const peaks = [ | ||
{ x: -0.1, y: 0.2, width: 0.3 }, | ||
{ x: 0.1, y: 0.2, width: 0.1 }, | ||
{ x: -0.1, y: 0.2, width: 0.3 }, | ||
]; | ||
const data = generateSpectrum(peaks, { from: -1, to: 1, nbPoints: 101 }); | ||
data.y.reverse(); | ||
data.x.reverse(); | ||
let peakList = gsd(data, { | ||
@@ -22,31 +20,121 @@ minMaxRatio: 0, | ||
heightFactor: 1, | ||
shape: { kind: 'gaussian' }, | ||
}); | ||
let optPeaks = optimizePeaks(data, peakList); | ||
let optimizedPeaks = optimizePeaks(data, peakList); | ||
expect(optPeaks[0].x).toBeCloseTo(-0.1, 2); | ||
expect(optPeaks[0].y).toBeCloseTo(0.2, 2); | ||
expect(optPeaks[0].width).toBeCloseTo(0.3, 2); | ||
expect(optPeaks[1].x).toBeCloseTo(0.1, 2); | ||
expect(optPeaks[1].y).toBeCloseTo(0.2, 2); | ||
expect(optPeaks[1].width).toBeCloseTo(0.1, 2); | ||
expect(peakList[0].x).toBeCloseTo(-0.1, 2); | ||
expect(peakList[0].y).toBeCloseTo(0.2, 2); | ||
expect(peakList[0].width).toBeCloseTo(0.3, 2); | ||
expect(peakList[1].x).toBeCloseTo(0.1, 2); | ||
expect(peakList[1].y).toBeCloseTo(0.2, 2); | ||
expect(peakList[1].width).toBeCloseTo(0.1, 2); | ||
expect(optimizedPeaks[0].x).toBeCloseTo(-0.1, 2); | ||
expect(optimizedPeaks[0].y).toBeCloseTo(0.2, 2); | ||
expect(optimizedPeaks[0].width).toBeCloseTo(0.3, 2); | ||
expect(optimizedPeaks[0].group).toBe(0); | ||
expect(optimizedPeaks[1].x).toBeCloseTo(0.1, 2); | ||
expect(optimizedPeaks[1].y).toBeCloseTo(0.2, 2); | ||
expect(optimizedPeaks[1].width).toBeCloseTo(0.1, 2); | ||
expect(optimizedPeaks[1].group).toBe(1); | ||
}); | ||
it('Overlaped peaks', () => { | ||
it('Check gaussian shapes with shape specification', () => { | ||
const peaks = [ | ||
{ x: 0.1, y: 0.4, width: 0.0 }, | ||
{ x: 0.101, y: 0.5, width: 0.01 }, | ||
{ x: 0.15, y: 0.4, width: 0.01 }, | ||
{ x: 0.151, y: 0.3, width: 0.03 }, | ||
{ x: -0.5, y: 1, width: 0.2 }, | ||
{ x: 0.5, y: 1, width: 0.1 }, | ||
]; | ||
const data = generateSpectrum(peaks, { from: 0, to: 1, nbPoints: 101 }); | ||
const data = generateSpectrum(peaks, { from: -1, to: 1, nbPoints: 10001 }); | ||
let optPeaks = optimizePeaks(data, peaks, { | ||
factorWidth: 1, | ||
optimization: { kind: 'lm', options: { maxIterations: 300 } }, | ||
let peakList = gsd(data, { | ||
minMaxRatio: 0, | ||
realTopDetection: false, | ||
smoothY: false, | ||
heightFactor: 1, | ||
shape: { kind: 'gaussian' }, // we specifiy we are expecting a gaussian shape | ||
}); | ||
expect(optPeaks[0].x).toBeCloseTo(0.15, 2); | ||
expect(optPeaks[0].y).toBeCloseTo(0.4, 2); | ||
expect(optPeaks[0].width).toBeCloseTo(0.01, 2); | ||
expect(peakList[0].x).toBeCloseTo(-0.5, 2); | ||
expect(peakList[0].y).toBeCloseTo(1, 2); | ||
expect(peakList[0].width).toBeCloseTo(0.2, 2); // inflection points in gaussian are higher tha FWHM | ||
expect(peakList[1].x).toBeCloseTo(0.5, 2); | ||
expect(peakList[1].y).toBeCloseTo(1, 2); | ||
expect(peakList[1].width).toBeCloseTo(0.1, 2); | ||
let optimizedPeaks = optimizePeaks(data, peakList); | ||
expect(optimizedPeaks[0].x).toBeCloseTo(-0.5, 2); | ||
expect(optimizedPeaks[0].y).toBeCloseTo(1, 2); | ||
expect(optimizedPeaks[0].width).toBeCloseTo(0.2, 2); // optimization by default expect a gaussian shape | ||
expect(optimizedPeaks[0].group).toBe(0); | ||
expect(optimizedPeaks[1].x).toBeCloseTo(0.5, 2); | ||
expect(optimizedPeaks[1].y).toBeCloseTo(1, 2); | ||
expect(optimizedPeaks[1].width).toBeCloseTo(0.1, 2); | ||
expect(optimizedPeaks[1].group).toBe(1); | ||
}); | ||
it('Check gaussian shapes without specifying shape', () => { | ||
const peaks = [ | ||
{ x: -0.5, y: 1, width: 0.2 }, | ||
{ x: 0.5, y: 1, width: 0.1 }, | ||
]; | ||
const data = generateSpectrum(peaks, { from: -1, to: 1, nbPoints: 10001 }); | ||
let peakList = gsd(data, { | ||
minMaxRatio: 0, | ||
realTopDetection: false, | ||
smoothY: false, | ||
heightFactor: 1, | ||
}); | ||
expect(peakList[0].x).toBeCloseTo(-0.5, 2); | ||
expect(peakList[0].y).toBeCloseTo(1, 2); | ||
expect(peakList[0].width).toBeCloseTo(0.17, 2); // inflection points in gaussian are higher tha FWHM | ||
expect(peakList[1].x).toBeCloseTo(0.5, 2); | ||
expect(peakList[1].y).toBeCloseTo(1, 2); | ||
expect(peakList[1].width).toBeCloseTo(0.085, 2); | ||
let optimizedPeaks = optimizePeaks(data, peakList); | ||
expect(optimizedPeaks[0].x).toBeCloseTo(-0.5, 2); | ||
expect(optimizedPeaks[0].y).toBeCloseTo(1, 2); | ||
expect(optimizedPeaks[0].width).toBeCloseTo(0.2, 2); // optimization by default expect a gaussian shape | ||
expect(optimizedPeaks[0].group).toBe(0); | ||
expect(optimizedPeaks[1].x).toBeCloseTo(0.5, 2); | ||
expect(optimizedPeaks[1].y).toBeCloseTo(1, 2); | ||
expect(optimizedPeaks[1].width).toBeCloseTo(0.1, 2); | ||
expect(optimizedPeaks[1].group).toBe(1); | ||
}); | ||
it('Should provide 1 peak', () => { | ||
const peaks = [{ x: 0, y: 1, width: 0.12 }]; | ||
const data = generateSpectrum(peaks, { | ||
from: -0.5, | ||
to: 0.5, | ||
nbPoints: 10001, | ||
shape: { | ||
kind: 'gaussian', | ||
options: { | ||
fwhm: 10000, | ||
}, | ||
}, | ||
}); | ||
let peakList = gsd(data, { | ||
minMaxRatio: 0, | ||
realTopDetection: false, | ||
smoothY: false, | ||
heightFactor: 1, | ||
shape: { kind: 'gaussian' }, | ||
}); | ||
expect(peakList).toHaveLength(1); | ||
expect(peakList[0].x).toBeCloseTo(0, 2); | ||
expect(peakList[0].y).toBeCloseTo(1, 2); | ||
expect(peakList[0].width).toBeCloseTo(0.12, 3); | ||
}); | ||
}); |
import { optimize } from 'ml-spectra-fitting'; | ||
import { xGetFromToIndex } from 'ml-spectra-processing'; | ||
import { groupPeaks } from './groupPeaks'; | ||
/** | ||
@@ -10,3 +13,3 @@ * Optimize the position (x), max intensity (y), full width at half maximum (width) | ||
* @param {number} [options.factorWidth = 1] - times of width to group peaks. | ||
* @param {object} [options.joinPeaks = true] - if true the peaks could be grouped if the separation between them are inside of a range of factorWidth * width | ||
* @param {number} [options.factorLimits = 2] - times of width to use to optimize peaks | ||
* @param {object} [options.shape={}] - it's specify the kind of shape used to fitting. | ||
@@ -22,3 +25,3 @@ * @param {string} [options.shape.kind = 'gaussian'] - kind of shape; lorentzian, gaussian and pseudovoigt are supported. | ||
factorWidth = 1, | ||
joinPeaks = true, | ||
factorLimits = 2, | ||
shape = { | ||
@@ -32,152 +35,33 @@ kind: 'gaussian', | ||
let { x, y } = data; | ||
let lastIndex = [0]; | ||
let groups = groupPeaks(peakList, factorWidth, joinPeaks); | ||
let result = []; | ||
let sampling; | ||
for (let i = 0; i < groups.length; i++) { | ||
let peaks = groups[i].group; | ||
if (peaks.length > 1) { | ||
// Multiple peaks | ||
sampling = sampleFunction( | ||
groups[i].limits[0] - groups[i].limits[1], | ||
groups[i].limits[0] + groups[i].limits[1], | ||
x, | ||
y, | ||
lastIndex, | ||
); | ||
if (sampling.x.length > 5) { | ||
let { peaks: optPeaks } = optimize(sampling, peaks, { | ||
shape, | ||
optimization, | ||
}); | ||
for (let j = 0; j < optPeaks.length; j++) { | ||
optPeaks[j].index = peaks.index; | ||
optPeaks[j].group = i; | ||
result.push(optPeaks[j]); | ||
} | ||
} | ||
} else { | ||
// Single peak | ||
peaks = peaks[0]; | ||
sampling = sampleFunction( | ||
peaks.x - factorWidth * peaks.width, | ||
peaks.x + factorWidth * peaks.width, | ||
x, | ||
y, | ||
lastIndex, | ||
); | ||
if (sampling.x.length > 5) { | ||
let fitResult = optimize(sampling, [peaks], { | ||
shape, | ||
optimization, | ||
}); | ||
let { peaks: optPeaks } = fitResult; | ||
optPeaks[0].index = peaks.index; | ||
optPeaks[0].group = i; | ||
result.push(optPeaks[0]); | ||
} | ||
} | ||
if (data.x[0] > data.x[1]) { | ||
data.x.reverse(); | ||
data.y.reverse(); | ||
} | ||
return result; | ||
} | ||
function sampleFunction(from, to, x, y, lastIndex) { | ||
let nbPoints = x.length; | ||
let sampleX = []; | ||
let sampleY = []; | ||
let direction = Math.sign(x[1] - x[0]); // Direction of the derivative | ||
if (direction === -1) { | ||
lastIndex[0] = x.length - 1; | ||
} | ||
let groups = groupPeaks(peakList, factorWidth); | ||
let delta = Math.abs(to - from) / 2; | ||
let mid = (from + to) / 2; | ||
let stop = false; | ||
let index = lastIndex[0]; | ||
while (!stop && index < nbPoints && index >= 0) { | ||
if (Math.abs(x[index] - mid) <= delta) { | ||
sampleX.push(x[index]); | ||
sampleY.push(y[index]); | ||
index += direction; | ||
} else { | ||
// It is outside the range. | ||
if (Math.sign(mid - x[index]) === 1) { | ||
// We'll reach the mid going in the current direction | ||
index += direction; | ||
} else { | ||
// There is not more peaks in the current range | ||
stop = true; | ||
} | ||
} | ||
} | ||
lastIndex[0] = index; | ||
return { x: sampleX, y: sampleY }; | ||
} | ||
let results = []; | ||
for (const peaks of groups) { | ||
const firstPeak = peaks[0]; | ||
const lastPeak = peaks[peaks.length - 1]; | ||
function groupPeaks(peakList, nL, joinPeaks) { | ||
let group = []; | ||
let groups = []; | ||
let limits = [peakList[0].x, nL * peakList[0].width]; | ||
let upperLimit, lowerLimit; | ||
// Merge forward | ||
for (let i = 0; i < peakList.length; i++) { | ||
// If the 2 things overlaps | ||
if ( | ||
joinPeaks && | ||
Math.abs(peakList[i].x - limits[0]) < nL * peakList[i].width + limits[1] | ||
) { | ||
// Add the peak to the group | ||
group.push(peakList[i]); | ||
// Update the group limits | ||
upperLimit = limits[0] + limits[1]; | ||
if (peakList[i].x + nL * peakList[i].width > upperLimit) { | ||
upperLimit = peakList[i].x + nL * peakList[i].width; | ||
} | ||
lowerLimit = limits[0] - limits[1]; | ||
if (peakList[i].x - nL * peakList[i].width < lowerLimit) { | ||
lowerLimit = peakList[i].x - nL * peakList[i].width; | ||
} | ||
limits = [ | ||
(upperLimit + lowerLimit) / 2, | ||
Math.abs(upperLimit - lowerLimit) / 2, | ||
]; | ||
const from = firstPeak.x - firstPeak.width * factorLimits; | ||
const to = lastPeak.x + lastPeak.width * factorLimits; | ||
const { fromIndex, toIndex } = xGetFromToIndex(data.x, { from, to }); | ||
// Multiple peaks | ||
const currentRange = { | ||
x: data.x.slice(fromIndex, toIndex), | ||
y: data.y.slice(fromIndex, toIndex), | ||
}; | ||
if (currentRange.x.length > 5) { | ||
let { peaks: optimizedPeaks } = optimize(currentRange, peaks, { | ||
shape, | ||
optimization, | ||
}); | ||
results = results.concat(optimizedPeaks); | ||
} else { | ||
groups.push({ limits: limits, group: group }); | ||
group = [peakList[i]]; | ||
limits = [peakList[i].x, nL * peakList[i].width]; | ||
results = results.concat(peaks); | ||
} | ||
} | ||
groups.push({ limits: limits, group: group }); | ||
// Merge backward | ||
for (let i = groups.length - 2; i >= 0; i--) { | ||
// The groups overlaps | ||
if ( | ||
Math.abs(groups[i].limits[0] - groups[i + 1].limits[0]) < | ||
(groups[i].limits[1] + groups[i + 1].limits[1]) / 2 | ||
) { | ||
for (let j = 0; j < groups[i + 1].group.length; j++) { | ||
groups[i].group.push(groups[i + 1].group[j]); | ||
} | ||
upperLimit = groups[i].limits[0] + groups[i].limits[1]; | ||
if (groups[i + 1].limits[0] + groups[i + 1].limits[1] > upperLimit) { | ||
upperLimit = groups[i + 1].limits[0] + groups[i + 1].limits[1]; | ||
} | ||
lowerLimit = groups[i].limits[0] - groups[i].limits[1]; | ||
if (groups[i + 1].limits[0] - groups[i + 1].limits[1] < lowerLimit) { | ||
lowerLimit = groups[i + 1].limits[0] - groups[i + 1].limits[1]; | ||
} | ||
groups[i].limits = [ | ||
(upperLimit + lowerLimit) / 2, | ||
Math.abs(upperLimit - lowerLimit) / 2, | ||
]; | ||
groups.splice(i + 1, 1); | ||
} | ||
} | ||
return groups; | ||
return results; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5862705
31
4
19
1738
+ Addedml-spectra-processing@^4.9.4
+ Added@types/estree@1.0.6(transitive)
+ Added@types/node@22.10.2(transitive)
+ Addedacorn@7.4.1(transitive)
+ Addedassign-deep@1.0.1(transitive)
+ Addedassign-symbols@2.0.2(transitive)
+ Addedcheminfo-types@0.5.0(transitive)
+ Addedd3-array@0.7.1(transitive)
+ Addedfft.js@4.0.4(transitive)
+ Addedis-any-array@0.1.01.0.1(transitive)
+ Addedmedian-quickselect@1.0.1(transitive)
+ Addedml-array-mean@1.1.6(transitive)
+ Addedml-array-median@1.1.6(transitive)
+ Addedml-array-sequential-fill@1.1.8(transitive)
+ Addedml-array-standard-deviation@1.1.8(transitive)
+ Addedml-array-sum@1.1.6(transitive)
+ Addedml-array-variance@1.1.8(transitive)
+ Addedml-gsd@6.9.2(transitive)
+ Addedml-levenberg-marquardt@3.1.1(transitive)
+ Addedml-peak-shape-generator@2.0.2(transitive)
+ Addedml-savitzky-golay-generalized@2.0.3(transitive)
+ Addedml-spectra-fitting@0.9.01.0.0(transitive)
+ Addedml-spectra-processing@4.12.06.8.0(transitive)
+ Addedrollup@1.32.1(transitive)
+ Addedspline-interpolator@1.0.0(transitive)
+ Addedundici-types@6.20.0(transitive)
- Removedd3-random@2.2.2(transitive)
- Removedesm@3.2.25(transitive)
- Removedis-any-array@0.1.1(transitive)
- Removedml-levenberg-marquardt@2.1.1(transitive)
- Removedml-savitzky-golay-generalized@2.0.2(transitive)
- Removedml-spectra-fitting@0.7.1(transitive)
- Removedml-xsadd@2.0.0(transitive)
- Removedspectrum-generator@4.8.1(transitive)
Updatedml-spectra-fitting@0.9.0