Skip to content

Commit 8013ab1

Browse files
committed
Updated to use the Savitzky-Golay library
With this update, it is possible to use different parameters for the Savitzky-Golay filters
1 parent 1005b35 commit 8013ab1

File tree

4 files changed

+88
-69
lines changed

4 files changed

+88
-69
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
"dependencies": {
4141
"ml-optimize-lorentzian": "0.0.2",
4242
"ml-stat": "^1.0.1",
43-
"xy-parser": "^1.1.0"
43+
"xy-parser": "^1.1.0",
44+
"ml-savitzky-golay-generalized":"1.0.0",
45+
"extend":"3.0.0"
4446
}
4547
}

src/gsd.js

Lines changed: 35 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
var Opt = require("ml-optimize-lorentzian");
22
var stats = require("ml-stat");
3+
var extend = require('extend');
4+
var SG = require('ml-savitzky-golay-generalized');
5+
6+
var sgDefOptions = {
7+
windowSize: 5,
8+
polynomial: 3,
9+
};
10+
11+
312
function gsd(x, y, options){
13+
//options = extend({}, defaultOptions, options);
414
var options=Object.create(options || {});
515
if (options.minMaxRatio===undefined) options.minMaxRatio=0.00025;
616
if (options.broadRatio===undefined) options.broadRatio=0.00;
@@ -9,6 +19,7 @@ function gsd(x, y, options){
919
if (options.smoothY===undefined) options.smoothY=true;
1020
if (options.realTopDetection===undefined) options.realTopDetection=false;
1121

22+
var sgOptions = extend({}, sgDefOptions, options.sgOptions);
1223

1324
//Transform y to use the standard algorithm.
1425
var yCorrection = {m:1, b:0};
@@ -29,28 +40,32 @@ function gsd(x, y, options){
2940
}
3041
}
3142
}
32-
// fill convolution frequency axis
33-
var X = [];//x[2:(x.length-2)];
3443

35-
// fill Savitzky-Golay polynomes
36-
var size= x.length-4;
37-
var Y = new Array(size);
38-
var dY = new Array(size);
39-
var ddY = new Array(size);
40-
//var dX = new Array(size);
41-
var dx = x[1]-x[0];
42-
43-
for (var j = 2; j < size+2; j++) {
44-
dx = x[j]-x[j-1];
45-
if(options.smoothY)
46-
Y[j-2]=(1/35.0)*(-3*y[j-2] + 12*y[j-1] + 17*y[j] + 12*y[j+1] - 3*y[j+2]);
47-
else
48-
Y[j-2]=y[j];
49-
X[j-2]=x[j];
50-
dY[j-2]=(1/(12*dx))*(y[j-2] - 8*y[j-1] + 8*y[j+1] - y[j+2]);
51-
ddY[j-2]=(1/(7*dx*dx))*(2*y[j-2] - y[j-1] - 2*y[j] - y[j+1] + 2*y[j+2]);
44+
//We have to know if x is equally spaced
45+
var maxDx=0, minDx=Number.MAX_VALUE,tmp;
46+
for(var i=0;i< x.length-1;i++){
47+
var tmp = Math.abs(x[i+1]-x[i]);
48+
if(tmp<minDx){
49+
minDx = tmp;
50+
}
51+
if(tmp>maxDx){
52+
maxDx = tmp;
53+
}
5254
}
53-
55+
//If the max difference between delta x is less than 5%, then, we can assume it to be equally spaced variable
56+
if((maxDx-minDx)/maxDx<0.05){
57+
var Y = SG(y, x[1]-x[0], {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:0});
58+
var dY = SG(y, x[1]-x[0], {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:1});
59+
var ddY = SG(y, x[1]-x[0], {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:2});
60+
}
61+
else{
62+
var Y = SG(y, x, {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:0});
63+
var dY = SG(y, x, {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:1});
64+
var ddY = SG(y, x, {windowSize:sgOptions.windowSize, polynomial:sgOptions.polynomial,derivative:2});
65+
}
66+
67+
var X = x;
68+
var dx = x[1]-x[0];
5469
var maxDdy=0;
5570
var maxY = 0;
5671
//console.log(Y.length);
@@ -138,53 +153,6 @@ function gsd(x, y, options){
138153
// console.log("Nested "+possible);
139154
}
140155
}
141-
/*if(options.broadRatio>0){
142-
var broadLines=[[Number.MAX_VALUE,0,0]];
143-
//Optimize the possible broad lines
144-
var max=0, maxI=0,count=0;
145-
var candidates = [],broadLinesS=[];
146-
var isPartOf = false;
147-
148-
for(var i=broadLines.length-1;i>0;i--){
149-
//console.log(broadLines[i][0]+" "+rangeX+" "+Math.abs(broadLines[i-1][0]-broadLines[i][0]));
150-
if(Math.abs(broadLines[i-1][0]-broadLines[i][0])<rangeX){
151-
152-
candidates.push(broadLines[i]);
153-
if(broadLines[i][1]>max){
154-
max = broadLines[i][1];
155-
maxI = i;
156-
}
157-
count++;
158-
}
159-
else{
160-
isPartOf = true;
161-
if(count>30){ // TODO, an options ?
162-
isPartOf = false;
163-
//for(var j=0;j<signals.length;j++){
164-
// if(Math.abs(broadLines[maxI][0]-signals[j][0])<rangeX)
165-
// isPartOf = true;
166-
// }
167-
//console.log("Was part of "+isPartOf);
168-
}
169-
if(isPartOf){
170-
for(var j=0;j<candidates.length;j++){
171-
signals.push([candidates[j][0], candidates[j][1], dx]);
172-
}
173-
}
174-
else{
175-
var fitted = Opt.optimizeSingleLorentzian(candidates,{x:candidates[maxI][0],
176-
width:Math.abs(candidates[0][0]-candidates[candidates.length-1][0])},
177-
[]);
178-
//console.log(fitted);
179-
signals.push([fitted[0][0],fitted[0][1],fitted[0][2]]);
180-
}
181-
candidates = [];
182-
max = 0;
183-
maxI = 0;
184-
count = 0;
185-
}
186-
}
187-
}*/
188156

189157
signals.sort(function (a, b) {
190158
return a.x - b.x;

src/optimize.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,53 @@ function groupPeaks(peakList,nL){
160160
return groups;
161161
}
162162

163+
/*if(options.broadRatio>0){
164+
var broadLines=[[Number.MAX_VALUE,0,0]];
165+
//Optimize the possible broad lines
166+
var max=0, maxI=0,count=0;
167+
var candidates = [],broadLinesS=[];
168+
var isPartOf = false;
169+
170+
for(var i=broadLines.length-1;i>0;i--){
171+
//console.log(broadLines[i][0]+" "+rangeX+" "+Math.abs(broadLines[i-1][0]-broadLines[i][0]));
172+
if(Math.abs(broadLines[i-1][0]-broadLines[i][0])<rangeX){
173+
174+
candidates.push(broadLines[i]);
175+
if(broadLines[i][1]>max){
176+
max = broadLines[i][1];
177+
maxI = i;
178+
}
179+
count++;
180+
}
181+
else{
182+
isPartOf = true;
183+
if(count>30){ // TODO, an options ?
184+
isPartOf = false;
185+
//for(var j=0;j<signals.length;j++){
186+
// if(Math.abs(broadLines[maxI][0]-signals[j][0])<rangeX)
187+
// isPartOf = true;
188+
// }
189+
//console.log("Was part of "+isPartOf);
190+
}
191+
if(isPartOf){
192+
for(var j=0;j<candidates.length;j++){
193+
signals.push([candidates[j][0], candidates[j][1], dx]);
194+
}
195+
}
196+
else{
197+
var fitted = Opt.optimizeSingleLorentzian(candidates,{x:candidates[maxI][0],
198+
width:Math.abs(candidates[0][0]-candidates[candidates.length-1][0])},
199+
[]);
200+
//console.log(fitted);
201+
signals.push([fitted[0][0],fitted[0][1],fitted[0][2]]);
202+
}
203+
candidates = [];
204+
max = 0;
205+
maxI = 0;
206+
count = 0;
207+
}
208+
}
209+
}*/
210+
163211
module.exports=optimizePeaks;
164212

test/ethylvinylether.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('Global spectra deconvolution NMR spectra', function () {
99
it('Ethylvinylether should have 21 peaks', function () {
1010
var spectrum=JSON.parse(fs.readFileSync('./test//ethylvinylether.json', 'utf-8'));
1111
var result = peakPicking.gsd(spectrum[0],spectrum[1], {noiseLevel: 1049200.537996172, minMaxRatio:0.03});
12-
console.log(result);
12+
//console.log(result)
13+
result.length.should.equal(21);
1314
});
1415
});

0 commit comments

Comments
 (0)