Comparing version 4.0.0 to 5.0.0
@@ -0,1 +1,5 @@ | ||
# [5.0.0](https://github.com/mljs/global-spectral-deconvolution/compare/v4.0.0...v5.0.0) (2020-05-19) | ||
# [4.0.0](https://github.com/mljs/global-spectral-deconvolution/compare/v3.0.1...v4.0.0) (2020-03-20) | ||
@@ -2,0 +6,0 @@ |
308
lib/index.js
@@ -8,18 +8,4 @@ 'use strict'; | ||
var SG = _interopDefault(require('ml-savitzky-golay-generalized')); | ||
var optimize = _interopDefault(require('ml-optimize-lorentzian')); | ||
var mlOptimizeLorentzian = require('ml-optimize-lorentzian'); | ||
const defaultOptions = { | ||
sgOptions: { | ||
windowSize: 9, | ||
polynomial: 3, | ||
}, | ||
minMaxRatio: 0.00025, | ||
broadRatio: 0.0, | ||
maxCriteria: true, | ||
smoothY: true, | ||
realTopDetection: false, | ||
heightFactor: 0, | ||
derivativeThreshold: -1, | ||
}; | ||
/** | ||
@@ -46,31 +32,28 @@ * Global spectra deconvolution | ||
*/ | ||
function gsd(x, yIn, options) { | ||
options = Object.assign({}, defaultOptions, options); | ||
let sgOptions = options.sgOptions; | ||
function gsd(x, yIn, options = {}) { | ||
let { | ||
noiseLevel, | ||
sgOptions = { | ||
windowSize: 9, | ||
polynomial: 3, | ||
}, | ||
smoothY = true, | ||
heightFactor = 0, | ||
broadRatio = 0.0, | ||
maxCriteria = true, | ||
minMaxRatio = 0.00025, | ||
derivativeThreshold = -1, | ||
realTopDetection = false, | ||
} = options; | ||
const y = yIn.slice(); | ||
let equalSpaced = isEqualSpaced(x); | ||
let maxDx = 0; | ||
let minDx = Number.MAX_VALUE; | ||
if (!('noiseLevel' in options)) { | ||
// We have to know if x is equally spaced | ||
if (noiseLevel === undefined) { | ||
noiseLevel = equalSpaced ? getNoiseLevel(y) : 0; | ||
} | ||
let tmp; | ||
for (let i = 0; i < x.length - 1; ++i) { | ||
tmp = Math.abs(x[i + 1] - x[i]); | ||
if (tmp < minDx) { | ||
minDx = tmp; | ||
} | ||
if (tmp > maxDx) { | ||
maxDx = tmp; | ||
} | ||
} | ||
const yCorrection = { m: 1, b: noiseLevel }; | ||
if ((maxDx - minDx) / maxDx < 0.05) { | ||
options.noiseLevel = getNoiseLevel(y); | ||
} else { | ||
options.noiseLevel = 0; | ||
} | ||
} | ||
const yCorrection = { m: 1, b: options.noiseLevel }; | ||
if (!options.maxCriteria) { | ||
if (!maxCriteria) { | ||
yCorrection.m = -1; | ||
@@ -89,10 +72,13 @@ yCorrection.b *= -1; | ||
} | ||
// If the max difference between delta x is less than 5%, then, we can assume it to be equally spaced variable | ||
// If the max difference between delta x is less than 5%, then, | ||
// we can assume it to be equally spaced variable | ||
let Y = y; | ||
let dY, ddY; | ||
if ((maxDx - minDx) / maxDx < 0.05) { | ||
if (options.smoothY) { | ||
const { windowSize, polynomial } = sgOptions; | ||
if (equalSpaced) { | ||
if (smoothY) { | ||
Y = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 0, | ||
@@ -102,16 +88,16 @@ }); | ||
dY = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 1, | ||
}); | ||
ddY = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 2, | ||
}); | ||
} else { | ||
if (options.smoothY) { | ||
if (smoothY) { | ||
Y = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 0, | ||
@@ -121,13 +107,13 @@ }); | ||
dY = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 1, | ||
}); | ||
ddY = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 2, | ||
}); | ||
} | ||
// console.log('this is 2', y) | ||
const X = x; | ||
@@ -159,3 +145,4 @@ const dx = x[1] - x[0]; | ||
// filter based on derivativeThreshold | ||
if (Math.abs(dY[i]) > options.derivativeThreshold) { | ||
// console.log('pasa', y[i], dY[i], ddY[i]); | ||
if (Math.abs(dY[i]) > derivativeThreshold) { | ||
// Minimum in first derivative | ||
@@ -196,4 +183,3 @@ if ( | ||
minddY[minddYLen++] = i; // ( [X[i], Y[i], i] ); | ||
broadMask[broadMaskLen++] = | ||
Math.abs(ddY[i]) <= options.broadRatio * maxDdy; | ||
broadMask[broadMaskLen++] = Math.abs(ddY[i]) <= broadRatio * maxDdy; | ||
} | ||
@@ -234,3 +220,3 @@ } | ||
if (possible !== -1) { | ||
if (Math.abs(Y[minddY[j]]) > options.minMaxRatio * maxY) { | ||
if (Math.abs(Y[minddY[j]]) > minMaxRatio * maxY) { | ||
signals[signalsLen++] = { | ||
@@ -247,8 +233,7 @@ index: minddY[j], | ||
if (options.heightFactor) { | ||
if (heightFactor) { | ||
let yLeft = Y[intervalL[possible].index]; | ||
let yRight = Y[intervalR[possible].index]; | ||
signals[signalsLen - 1].height = | ||
options.heightFactor * | ||
(signals[signalsLen - 1].y - (yLeft + yRight) / 2); | ||
heightFactor * (signals[signalsLen - 1].y - (yLeft + yRight) / 2); | ||
} | ||
@@ -260,4 +245,4 @@ } | ||
if (options.realTopDetection) { | ||
realTopDetection(signals, X, Y); | ||
if (realTopDetection) { | ||
determineRealTop(signals, X, Y); | ||
} | ||
@@ -267,6 +252,6 @@ | ||
for (let j = 0; j < signals.length; j++) { | ||
signals[j].base = options.noiseLevel; | ||
signals[j].base = noiseLevel; | ||
} | ||
signals.sort(function(a, b) { | ||
signals.sort(function (a, b) { | ||
return a.x - b.x; | ||
@@ -278,3 +263,19 @@ }); | ||
function getNoiseLevel(y) { | ||
const isEqualSpaced = (x) => { | ||
let tmp; | ||
let maxDx = 0; | ||
let minDx = Number.MAX_SAFE_INTEGER; | ||
for (let i = 0; i < x.length - 1; ++i) { | ||
tmp = Math.abs(x[i + 1] - x[i]); | ||
if (tmp < minDx) { | ||
minDx = tmp; | ||
} | ||
if (tmp > maxDx) { | ||
maxDx = tmp; | ||
} | ||
} | ||
return (maxDx - minDx) / maxDx < 0.05; | ||
}; | ||
const getNoiseLevel = (y) => { | ||
let mean = 0; | ||
@@ -303,5 +304,5 @@ | ||
return stddev; | ||
} | ||
}; | ||
function realTopDetection(peakList, x, y) { | ||
const determineRealTop = (peakList, x, y) => { | ||
let alpha, beta, gamma, p, currentPoint; | ||
@@ -360,57 +361,27 @@ for (let j = 0; j < peakList.length; j++) { | ||
} | ||
} | ||
}; | ||
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; | ||
} | ||
function optimizePeaks(peakList, x, y, options = {}) { | ||
const { | ||
functionName = 'gaussian', | ||
factorWidth = 4, | ||
optimizationOptions = { | ||
damping: 1.5, | ||
maxIterations: 100, | ||
errorTolerance: 10e-5, | ||
}, | ||
} = options; | ||
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; | ||
} | ||
} | ||
// console.log(sampleX); | ||
} | ||
lastIndex[0] = index; | ||
return [sampleX, sampleY]; | ||
} | ||
function optimizePeaks(peakList, x, y, n, fnType) { | ||
let i; | ||
let j; | ||
let lastIndex = [0]; | ||
let groups = groupPeaks(peakList, n); | ||
let groups = groupPeaks(peakList, factorWidth); | ||
let result = []; | ||
let factor = 1; | ||
if (fnType === 'gaussian') { | ||
if (functionName === 'gaussian') { | ||
factor = 1.17741; | ||
} // From https://en.wikipedia.org/wiki/Gaussian_function#Properties | ||
let sampling, error, opts; | ||
for (i = 0; i < groups.length; i++) { | ||
let sampling; | ||
for (let i = 0; i < groups.length; i++) { | ||
let peaks = groups[i].group; | ||
if (peaks.length > 1) { | ||
// Multiple peaks | ||
// console.log("Pending group of overlaped peaks "+peaks.length); | ||
// console.log("here1"); | ||
// console.log(groups[i].limits); | ||
sampling = sampleFunction( | ||
@@ -423,17 +394,16 @@ groups[i].limits[0] - groups[i].limits[1], | ||
); | ||
// console.log(sampling); | ||
if (sampling[0].length > 5) { | ||
error = peaks[0].width / 1000; | ||
opts = [3, 100, error, error, error, error * 10, error * 10, 11, 9, 1]; | ||
// var gauss = Opt.optimizeSingleGaussian(sampling[0], sampling[1], opts, peaks); | ||
let optPeaks = []; | ||
if (fnType === 'gaussian') { | ||
optPeaks = optimize.optimizeGaussianSum(sampling, peaks, opts); | ||
if (functionName === 'gaussian') { | ||
optPeaks = mlOptimizeLorentzian.optimizeGaussianSum(sampling, peaks, optimizationOptions); | ||
} else { | ||
if (fnType === 'lorentzian') { | ||
optPeaks = optimize.optimizeLorentzianSum(sampling, peaks, opts); | ||
if (functionName === 'lorentzian') { | ||
optPeaks = mlOptimizeLorentzian.optimizeLorentzianSum( | ||
sampling, | ||
peaks, | ||
optimizationOptions, | ||
); | ||
} | ||
} | ||
// console.log(optPeak); | ||
for (j = 0; j < optPeaks.length; j++) { | ||
for (let j = 0; j < optPeaks.length; j++) { | ||
result.push({ | ||
@@ -450,4 +420,4 @@ x: optPeaks[j][0][0], | ||
sampling = sampleFunction( | ||
peaks.x - n * peaks.width, | ||
peaks.x + n * peaks.width, | ||
peaks.x - factorWidth * peaks.width, | ||
peaks.x + factorWidth * peaks.width, | ||
x, | ||
@@ -457,30 +427,25 @@ y, | ||
); | ||
// console.log("here2"); | ||
// console.log(groups[i].limits); | ||
if (sampling[0].length > 5) { | ||
error = peaks.width / 1000; | ||
opts = [3, 100, error, error, error, error * 10, error * 10, 11, 9, 1]; | ||
// var gauss = Opt.optimizeSingleGaussian(sampling[0], sampling[1], opts, peaks); | ||
// var gauss = Opt.optimizeSingleGaussian([sampling[0],sampling[1]], peaks, opts); | ||
let optPeak = []; | ||
if (fnType === 'gaussian') { | ||
optPeak = optimize.optimizeSingleGaussian( | ||
let fitResult = []; | ||
if (functionName === 'gaussian') { | ||
fitResult = mlOptimizeLorentzian.optimizeSingleGaussian( | ||
[sampling[0], sampling[1]], | ||
peaks, | ||
opts, | ||
optimizationOptions, | ||
); | ||
} else { | ||
if (fnType === 'lorentzian') { | ||
optPeak = optimize.optimizeSingleLorentzian( | ||
if (functionName === 'lorentzian') { | ||
fitResult = mlOptimizeLorentzian.optimizeSingleLorentzian( | ||
[sampling[0], sampling[1]], | ||
peaks, | ||
opts, | ||
optimizationOptions, | ||
); | ||
} | ||
} | ||
// console.log(optPeak); | ||
let { parameters } = fitResult; | ||
result.push({ | ||
x: optPeak[0][0], | ||
y: optPeak[1][0], | ||
width: optPeak[2][0] * factor, | ||
x: parameters[0], | ||
y: parameters[1], | ||
width: parameters[2] * factor, | ||
}); // From https://en.wikipedia.org/wiki/Gaussian_function#Properties} | ||
@@ -493,10 +458,42 @@ } | ||
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 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 [sampleX, sampleY]; | ||
} | ||
function groupPeaks(peakList, nL) { | ||
let group = []; | ||
let groups = []; | ||
let i, j; | ||
let limits = [peakList[0].x, nL * peakList[0].width]; | ||
let upperLimit, lowerLimit; | ||
// Merge forward | ||
for (i = 0; i < peakList.length; i++) { | ||
for (let i = 0; i < peakList.length; i++) { | ||
// If the 2 things overlaps | ||
@@ -531,3 +528,3 @@ if ( | ||
// Merge backward | ||
for (i = groups.length - 2; i >= 0; i--) { | ||
for (let i = groups.length - 2; i >= 0; i--) { | ||
// The groups overlaps | ||
@@ -538,3 +535,3 @@ if ( | ||
) { | ||
for (j = 0; j < groups[i + 1].group.length; j++) { | ||
for (let j = 0; j < groups[i + 1].group.length; j++) { | ||
groups[i].group.push(groups[i + 1].group[j]); | ||
@@ -550,3 +547,3 @@ } | ||
} | ||
// console.log(limits); | ||
groups[i].limits = [ | ||
@@ -600,3 +597,3 @@ (upperLimit + lowerLimit) / 2, | ||
if (count > 2) { | ||
let fitted = optimize.optimizeSingleLorentzian(candidates, { | ||
let fitted = mlOptimizeLorentzian.optimizeSingleLorentzian(candidates, { | ||
x: broadLines[maxI].x, | ||
@@ -608,6 +605,7 @@ y: max, | ||
}); | ||
let { parameters } = fitted; | ||
peakList.push({ | ||
x: fitted[0][0], | ||
y: fitted[1][0], | ||
width: fitted[2][0], | ||
x: parameters[0], | ||
y: parameters[1], | ||
width: parameters[2], | ||
soft: false, | ||
@@ -629,3 +627,3 @@ }); | ||
peakList.sort(function(a, b) { | ||
peakList.sort(function (a, b) { | ||
return a.x - b.x; | ||
@@ -632,0 +630,0 @@ }); |
{ | ||
"name": "ml-gsd", | ||
"version": "4.0.0", | ||
"version": "5.0.0", | ||
"description": "Global Spectra Deconvolution", | ||
@@ -44,20 +44,20 @@ "main": "lib/index.js", | ||
"devDependencies": { | ||
"@babel/plugin-transform-modules-commonjs": "^7.8.3", | ||
"@babel/plugin-transform-modules-commonjs": "^7.9.6", | ||
"chemcalc": "^3.4.1", | ||
"cheminfo-tools": "^1.23.3", | ||
"eslint": "^6.8.0", | ||
"eslint-config-cheminfo": "^2.0.4", | ||
"eslint-plugin-import": "^2.20.1", | ||
"eslint-plugin-jest": "^23.8.2", | ||
"eslint-plugin-prettier": "^3.1.2", | ||
"jest": "^25.1.0", | ||
"eslint": "^7.0.0", | ||
"eslint-config-cheminfo": "^3.0.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-jest": "^23.13.1", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"jest": "^26.0.1", | ||
"ml-stat": "^1.3.3", | ||
"prettier": "^1.19.1", | ||
"rollup": "^2.1.0", | ||
"spectrum-generator": "^4.0.0", | ||
"prettier": "^2.0.5", | ||
"rollup": "^2.10.4", | ||
"spectrum-generator": "^4.0.2", | ||
"xy-parser": "^3.0.0" | ||
}, | ||
"dependencies": { | ||
"ml-optimize-lorentzian": "0.1.4", | ||
"ml-savitzky-golay-generalized": "2.0.1" | ||
"ml-optimize-lorentzian": "^0.2.0", | ||
"ml-savitzky-golay-generalized": "2.0.2" | ||
}, | ||
@@ -64,0 +64,0 @@ "jest": { |
@@ -5,5 +5,5 @@ import { readFileSync } from 'fs'; | ||
describe('Global spectra deconvolution Infrared spectra', function() { | ||
describe('Global spectra deconvolution Infrared spectra', function () { | ||
// Test case obtained from Pag 443, Chap 8. | ||
it('Should get the correct result', function() { | ||
it('Should get the correct result', function () { | ||
let spectrum = JSON.parse( | ||
@@ -10,0 +10,0 @@ readFileSync(`${__dirname}/data/infraRed.json`, 'utf-8'), |
@@ -35,4 +35,6 @@ import { gsd, optimizePeaks } from '..'; | ||
}); | ||
result = optimizePeaks(result, x, y, 1, 'gaussian'); | ||
result = optimizePeaks(result, x, y, { | ||
factorWidth: 1, | ||
functionName: 'gaussian', | ||
}); | ||
expect(result[0].x).toBeCloseTo(69.938, 1); | ||
@@ -39,0 +41,0 @@ expect(result[0].y).toBeCloseTo(max, 2); |
@@ -29,20 +29,5 @@ import { gsd } from '..'; | ||
expect(peaks).toStrictEqual([ | ||
{ | ||
base: 1.1956287811760204, | ||
index: 15, | ||
soft: false, | ||
width: 2, | ||
x: 15, | ||
y: 4.657142857142857, | ||
left: { | ||
index: 14, | ||
x: 14, | ||
}, | ||
right: { | ||
index: 16, | ||
x: 16, | ||
}, | ||
}, | ||
]); | ||
expect(peaks[0].y).toBeCloseTo(4.657, 3); | ||
expect(peaks[0].base).toBeCloseTo(1.1956, 3); | ||
expect(peaks[0].x).toStrictEqual(15); | ||
}); | ||
@@ -87,3 +72,3 @@ | ||
realTopDetection: true, | ||
smoothY: true, | ||
smoothY: false, | ||
sgOptions: { | ||
@@ -94,3 +79,2 @@ windowSize: 5, | ||
}); | ||
expect(peaks).toStrictEqual([ | ||
@@ -110,4 +94,4 @@ { | ||
width: 3, | ||
x: 14.755485084898725, | ||
y: 3.7914767697342637, | ||
x: 14.5, | ||
y: 4.006546067576939, | ||
}, | ||
@@ -114,0 +98,0 @@ ]); |
143
src/gsd.js
import SG from 'ml-savitzky-golay-generalized'; | ||
const defaultOptions = { | ||
sgOptions: { | ||
windowSize: 9, | ||
polynomial: 3, | ||
}, | ||
minMaxRatio: 0.00025, | ||
broadRatio: 0.0, | ||
maxCriteria: true, | ||
smoothY: true, | ||
realTopDetection: false, | ||
heightFactor: 0, | ||
derivativeThreshold: -1, | ||
}; | ||
/** | ||
@@ -38,31 +24,28 @@ * Global spectra deconvolution | ||
*/ | ||
export function gsd(x, yIn, options) { | ||
options = Object.assign({}, defaultOptions, options); | ||
let sgOptions = options.sgOptions; | ||
export function gsd(x, yIn, options = {}) { | ||
let { | ||
noiseLevel, | ||
sgOptions = { | ||
windowSize: 9, | ||
polynomial: 3, | ||
}, | ||
smoothY = true, | ||
heightFactor = 0, | ||
broadRatio = 0.0, | ||
maxCriteria = true, | ||
minMaxRatio = 0.00025, | ||
derivativeThreshold = -1, | ||
realTopDetection = false, | ||
} = options; | ||
const y = yIn.slice(); | ||
let equalSpaced = isEqualSpaced(x); | ||
let maxDx = 0; | ||
let minDx = Number.MAX_VALUE; | ||
if (!('noiseLevel' in options)) { | ||
// We have to know if x is equally spaced | ||
if (noiseLevel === undefined) { | ||
noiseLevel = equalSpaced ? getNoiseLevel(y) : 0; | ||
} | ||
let tmp; | ||
for (let i = 0; i < x.length - 1; ++i) { | ||
tmp = Math.abs(x[i + 1] - x[i]); | ||
if (tmp < minDx) { | ||
minDx = tmp; | ||
} | ||
if (tmp > maxDx) { | ||
maxDx = tmp; | ||
} | ||
} | ||
const yCorrection = { m: 1, b: noiseLevel }; | ||
if ((maxDx - minDx) / maxDx < 0.05) { | ||
options.noiseLevel = getNoiseLevel(y); | ||
} else { | ||
options.noiseLevel = 0; | ||
} | ||
} | ||
const yCorrection = { m: 1, b: options.noiseLevel }; | ||
if (!options.maxCriteria) { | ||
if (!maxCriteria) { | ||
yCorrection.m = -1; | ||
@@ -81,10 +64,13 @@ yCorrection.b *= -1; | ||
} | ||
// If the max difference between delta x is less than 5%, then, we can assume it to be equally spaced variable | ||
// If the max difference between delta x is less than 5%, then, | ||
// we can assume it to be equally spaced variable | ||
let Y = y; | ||
let dY, ddY; | ||
if ((maxDx - minDx) / maxDx < 0.05) { | ||
if (options.smoothY) { | ||
const { windowSize, polynomial } = sgOptions; | ||
if (equalSpaced) { | ||
if (smoothY) { | ||
Y = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 0, | ||
@@ -94,16 +80,16 @@ }); | ||
dY = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 1, | ||
}); | ||
ddY = SG(y, x[1] - x[0], { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 2, | ||
}); | ||
} else { | ||
if (options.smoothY) { | ||
if (smoothY) { | ||
Y = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 0, | ||
@@ -113,13 +99,13 @@ }); | ||
dY = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 1, | ||
}); | ||
ddY = SG(y, x, { | ||
windowSize: sgOptions.windowSize, | ||
polynomial: sgOptions.polynomial, | ||
windowSize, | ||
polynomial, | ||
derivative: 2, | ||
}); | ||
} | ||
// console.log('this is 2', y) | ||
const X = x; | ||
@@ -151,3 +137,4 @@ const dx = x[1] - x[0]; | ||
// filter based on derivativeThreshold | ||
if (Math.abs(dY[i]) > options.derivativeThreshold) { | ||
// console.log('pasa', y[i], dY[i], ddY[i]); | ||
if (Math.abs(dY[i]) > derivativeThreshold) { | ||
// Minimum in first derivative | ||
@@ -188,4 +175,3 @@ if ( | ||
minddY[minddYLen++] = i; // ( [X[i], Y[i], i] ); | ||
broadMask[broadMaskLen++] = | ||
Math.abs(ddY[i]) <= options.broadRatio * maxDdy; | ||
broadMask[broadMaskLen++] = Math.abs(ddY[i]) <= broadRatio * maxDdy; | ||
} | ||
@@ -226,3 +212,3 @@ } | ||
if (possible !== -1) { | ||
if (Math.abs(Y[minddY[j]]) > options.minMaxRatio * maxY) { | ||
if (Math.abs(Y[minddY[j]]) > minMaxRatio * maxY) { | ||
signals[signalsLen++] = { | ||
@@ -239,8 +225,7 @@ index: minddY[j], | ||
if (options.heightFactor) { | ||
if (heightFactor) { | ||
let yLeft = Y[intervalL[possible].index]; | ||
let yRight = Y[intervalR[possible].index]; | ||
signals[signalsLen - 1].height = | ||
options.heightFactor * | ||
(signals[signalsLen - 1].y - (yLeft + yRight) / 2); | ||
heightFactor * (signals[signalsLen - 1].y - (yLeft + yRight) / 2); | ||
} | ||
@@ -252,4 +237,4 @@ } | ||
if (options.realTopDetection) { | ||
realTopDetection(signals, X, Y); | ||
if (realTopDetection) { | ||
determineRealTop(signals, X, Y); | ||
} | ||
@@ -259,6 +244,6 @@ | ||
for (let j = 0; j < signals.length; j++) { | ||
signals[j].base = options.noiseLevel; | ||
signals[j].base = noiseLevel; | ||
} | ||
signals.sort(function(a, b) { | ||
signals.sort(function (a, b) { | ||
return a.x - b.x; | ||
@@ -270,3 +255,19 @@ }); | ||
function getNoiseLevel(y) { | ||
const isEqualSpaced = (x) => { | ||
let tmp; | ||
let maxDx = 0; | ||
let minDx = Number.MAX_SAFE_INTEGER; | ||
for (let i = 0; i < x.length - 1; ++i) { | ||
tmp = Math.abs(x[i + 1] - x[i]); | ||
if (tmp < minDx) { | ||
minDx = tmp; | ||
} | ||
if (tmp > maxDx) { | ||
maxDx = tmp; | ||
} | ||
} | ||
return (maxDx - minDx) / maxDx < 0.05; | ||
}; | ||
const getNoiseLevel = (y) => { | ||
let mean = 0; | ||
@@ -295,5 +296,5 @@ | ||
return stddev; | ||
} | ||
}; | ||
function realTopDetection(peakList, x, y) { | ||
const determineRealTop = (peakList, x, y) => { | ||
let alpha, beta, gamma, p, currentPoint; | ||
@@ -352,2 +353,2 @@ for (let j = 0; j < peakList.length; j++) { | ||
} | ||
} | ||
}; |
@@ -1,2 +0,2 @@ | ||
import Opt from 'ml-optimize-lorentzian'; | ||
import { optimizeSingleLorentzian } from 'ml-optimize-lorentzian'; | ||
@@ -40,3 +40,3 @@ /** | ||
if (count > 2) { | ||
let fitted = Opt.optimizeSingleLorentzian(candidates, { | ||
let fitted = optimizeSingleLorentzian(candidates, { | ||
x: broadLines[maxI].x, | ||
@@ -48,6 +48,7 @@ y: max, | ||
}); | ||
let { parameters } = fitted; | ||
peakList.push({ | ||
x: fitted[0][0], | ||
y: fitted[1][0], | ||
width: fitted[2][0], | ||
x: parameters[0], | ||
y: parameters[1], | ||
width: parameters[2], | ||
soft: false, | ||
@@ -69,3 +70,3 @@ }); | ||
peakList.sort(function(a, b) { | ||
peakList.sort(function (a, b) { | ||
return a.x - b.x; | ||
@@ -72,0 +73,0 @@ }); |
@@ -1,56 +0,31 @@ | ||
import optimize from 'ml-optimize-lorentzian'; | ||
import { | ||
optimizeGaussianSum, | ||
optimizeLorentzianSum, | ||
optimizeSingleGaussian, | ||
optimizeSingleLorentzian, | ||
} from 'ml-optimize-lorentzian'; | ||
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; | ||
} | ||
export function optimizePeaks(peakList, x, y, options = {}) { | ||
const { | ||
functionName = 'gaussian', | ||
factorWidth = 4, | ||
optimizationOptions = { | ||
damping: 1.5, | ||
maxIterations: 100, | ||
errorTolerance: 10e-5, | ||
}, | ||
} = options; | ||
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; | ||
} | ||
} | ||
// console.log(sampleX); | ||
} | ||
lastIndex[0] = index; | ||
return [sampleX, sampleY]; | ||
} | ||
export function optimizePeaks(peakList, x, y, n, fnType) { | ||
let i; | ||
let j; | ||
let lastIndex = [0]; | ||
let groups = groupPeaks(peakList, n); | ||
let groups = groupPeaks(peakList, factorWidth); | ||
let result = []; | ||
let factor = 1; | ||
if (fnType === 'gaussian') { | ||
if (functionName === 'gaussian') { | ||
factor = 1.17741; | ||
} // From https://en.wikipedia.org/wiki/Gaussian_function#Properties | ||
let sampling, error, opts; | ||
for (i = 0; i < groups.length; i++) { | ||
let sampling; | ||
for (let i = 0; i < groups.length; i++) { | ||
let peaks = groups[i].group; | ||
if (peaks.length > 1) { | ||
// Multiple peaks | ||
// console.log("Pending group of overlaped peaks "+peaks.length); | ||
// console.log("here1"); | ||
// console.log(groups[i].limits); | ||
sampling = sampleFunction( | ||
@@ -63,17 +38,16 @@ groups[i].limits[0] - groups[i].limits[1], | ||
); | ||
// console.log(sampling); | ||
if (sampling[0].length > 5) { | ||
error = peaks[0].width / 1000; | ||
opts = [3, 100, error, error, error, error * 10, error * 10, 11, 9, 1]; | ||
// var gauss = Opt.optimizeSingleGaussian(sampling[0], sampling[1], opts, peaks); | ||
let optPeaks = []; | ||
if (fnType === 'gaussian') { | ||
optPeaks = optimize.optimizeGaussianSum(sampling, peaks, opts); | ||
if (functionName === 'gaussian') { | ||
optPeaks = optimizeGaussianSum(sampling, peaks, optimizationOptions); | ||
} else { | ||
if (fnType === 'lorentzian') { | ||
optPeaks = optimize.optimizeLorentzianSum(sampling, peaks, opts); | ||
if (functionName === 'lorentzian') { | ||
optPeaks = optimizeLorentzianSum( | ||
sampling, | ||
peaks, | ||
optimizationOptions, | ||
); | ||
} | ||
} | ||
// console.log(optPeak); | ||
for (j = 0; j < optPeaks.length; j++) { | ||
for (let j = 0; j < optPeaks.length; j++) { | ||
result.push({ | ||
@@ -90,4 +64,4 @@ x: optPeaks[j][0][0], | ||
sampling = sampleFunction( | ||
peaks.x - n * peaks.width, | ||
peaks.x + n * peaks.width, | ||
peaks.x - factorWidth * peaks.width, | ||
peaks.x + factorWidth * peaks.width, | ||
x, | ||
@@ -97,30 +71,25 @@ y, | ||
); | ||
// console.log("here2"); | ||
// console.log(groups[i].limits); | ||
if (sampling[0].length > 5) { | ||
error = peaks.width / 1000; | ||
opts = [3, 100, error, error, error, error * 10, error * 10, 11, 9, 1]; | ||
// var gauss = Opt.optimizeSingleGaussian(sampling[0], sampling[1], opts, peaks); | ||
// var gauss = Opt.optimizeSingleGaussian([sampling[0],sampling[1]], peaks, opts); | ||
let optPeak = []; | ||
if (fnType === 'gaussian') { | ||
optPeak = optimize.optimizeSingleGaussian( | ||
let fitResult = []; | ||
if (functionName === 'gaussian') { | ||
fitResult = optimizeSingleGaussian( | ||
[sampling[0], sampling[1]], | ||
peaks, | ||
opts, | ||
optimizationOptions, | ||
); | ||
} else { | ||
if (fnType === 'lorentzian') { | ||
optPeak = optimize.optimizeSingleLorentzian( | ||
if (functionName === 'lorentzian') { | ||
fitResult = optimizeSingleLorentzian( | ||
[sampling[0], sampling[1]], | ||
peaks, | ||
opts, | ||
optimizationOptions, | ||
); | ||
} | ||
} | ||
// console.log(optPeak); | ||
let { parameters } = fitResult; | ||
result.push({ | ||
x: optPeak[0][0], | ||
y: optPeak[1][0], | ||
width: optPeak[2][0] * factor, | ||
x: parameters[0], | ||
y: parameters[1], | ||
width: parameters[2] * factor, | ||
}); // From https://en.wikipedia.org/wiki/Gaussian_function#Properties} | ||
@@ -133,10 +102,42 @@ } | ||
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 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 [sampleX, sampleY]; | ||
} | ||
function groupPeaks(peakList, nL) { | ||
let group = []; | ||
let groups = []; | ||
let i, j; | ||
let limits = [peakList[0].x, nL * peakList[0].width]; | ||
let upperLimit, lowerLimit; | ||
// Merge forward | ||
for (i = 0; i < peakList.length; i++) { | ||
for (let i = 0; i < peakList.length; i++) { | ||
// If the 2 things overlaps | ||
@@ -171,3 +172,3 @@ if ( | ||
// Merge backward | ||
for (i = groups.length - 2; i >= 0; i--) { | ||
for (let i = groups.length - 2; i >= 0; i--) { | ||
// The groups overlaps | ||
@@ -178,3 +179,3 @@ if ( | ||
) { | ||
for (j = 0; j < groups[i + 1].group.length; j++) { | ||
for (let j = 0; j < groups[i + 1].group.length; j++) { | ||
groups[i].group.push(groups[i + 1].group[j]); | ||
@@ -190,3 +191,3 @@ } | ||
} | ||
// console.log(limits); | ||
groups[i].limits = [ | ||
@@ -193,0 +194,0 @@ (upperLimit + lowerLimit) / 2, |
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
5855830
1720
+ Addedis-any-array@0.1.12.0.1(transitive)
+ Addedml-array-max@1.2.4(transitive)
+ Addedml-array-min@1.2.3(transitive)
+ Addedml-array-rescale@1.3.7(transitive)
+ Addedml-levenberg-marquardt@2.1.1(transitive)
+ Addedml-matrix@6.12.0(transitive)
+ Addedml-optimize-lorentzian@0.2.0(transitive)
+ Addedml-savitzky-golay-generalized@2.0.2(transitive)
- Removedml-array-utils@0.3.0(transitive)
- Removedml-curve-fitting@0.0.7(transitive)
- Removedml-matrix@2.3.0(transitive)
- Removedml-optimize-lorentzian@0.1.4(transitive)
- Removedml-savitzky-golay-generalized@2.0.1(transitive)
- Removedml-stat@1.3.3(transitive)