Skip to content

Commit f22f5a5

Browse files
authored
feat: peak picking returns id and keep id if it exists (#108)
* chore: initial steps to keep id property * feat(broadenPeaks): conditional type * chore(joinBroadPeaks): conditional type * feat(setShape): generic function n output option pure function now * chore(gsd): peaks with id * chore: refactor broadenPeaks * chore(joinBroadPeaks): refactor * chore(joinBroadPeaks): remove conditional type * chore(optimizePeaks): conditional types * fix(addMissingShape): convert to pure * fix(optimizePeaks): convert to generic n conditional types * feat: addMissingIDs
1 parent 928ea11 commit f22f5a5

21 files changed

+234
-101
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@
7979
"xy-parser": "^5.0.2"
8080
},
8181
"dependencies": {
82+
"@lukeed/uuid": "^2.0.0",
8283
"cheminfo-types": "^1.1.0",
8384
"ml-peak-shape-generator": "^4.1.1",
8485
"ml-savitzky-golay-generalized": "^4.0.1",
85-
"ml-spectra-fitting": "^4.1.0",
86+
"ml-spectra-fitting": "^4.1.1",
8687
"ml-spectra-processing": "^11.6.0"
8788
}
8889
}

src/GSDBroadenPeak.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import { Shape1D } from 'ml-peak-shape-generator';
2+
13
export interface GSDBroadenPeak {
4+
id?: string;
25
x: number;
36
y: number;
47
width: number;
8+
shape?: Shape1D;
59
index: number;
610
from: { x: number };
711
to: { x: number };

src/GSDPeak.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Shape1D } from 'ml-peak-shape-generator';
22

33
export interface GSDPeak {
4+
id?: string;
45
x: number;
56
y: number;
67
/**

src/GSDPeakOptimized.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Shape1D } from 'ml-peak-shape-generator';
22

33
export interface GSDPeakOptimized {
4+
id?: string;
45
x: number;
56
y: number;
67
width: number;

src/__tests__/gaussian-smooth.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe('gaussian simulated peaks', () => {
3333
});
3434
expect(peakList).toBeDeepCloseTo([
3535
{
36+
id: peakList[0].id,
3637
x: -0.5,
3738
y: 0.6945098953985852,
3839
ddY: -259.83290100626783,
@@ -48,6 +49,7 @@ describe('gaussian simulated peaks', () => {
4849
},
4950
},
5051
{
52+
id: peakList[1].id,
5153
x: 0.5,
5254
y: 0.6945098953985852,
5355
ddY: -259.83290100626783,

src/__tests__/gaussian.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('smooth:false option', () => {
6363
},
6464
},
6565
];
66-
expect(peakList).toBeDeepCloseTo(expected);
66+
expect(peakList).toBeDeepCloseTo(addMissingID(peakList, expected));
6767
});
6868

6969
it('negative maxima peaks', () => {
@@ -101,7 +101,7 @@ describe('smooth:false option', () => {
101101
},
102102
},
103103
];
104-
expect(peakList).toBeDeepCloseTo(expected);
104+
expect(peakList).toBeDeepCloseTo(addMissingID(peakList, expected));
105105
});
106106

107107
it('Negative peaks', () => {
@@ -143,7 +143,7 @@ describe('smooth:false option', () => {
143143
},
144144
];
145145

146-
expect(peakList).toBeDeepCloseTo(expected);
146+
expect(peakList).toBeDeepCloseTo(addMissingID(peakList, expected));
147147
});
148148

149149
it('minima peaks', () => {
@@ -185,7 +185,7 @@ describe('smooth:false option', () => {
185185
},
186186
];
187187

188-
expect(peakList).toBeDeepCloseTo(expected);
188+
expect(peakList).toBeDeepCloseTo(addMissingID(peakList, expected));
189189
});
190190

191191
it('negative peaks with maxCriteria true', () => {
@@ -196,3 +196,10 @@ describe('smooth:false option', () => {
196196
expect(peakList).toHaveLength(0);
197197
});
198198
});
199+
200+
function addMissingID(peaks, expected) {
201+
for (let i = 0; i < expected.length; i++) {
202+
expected[i].id = peaks[i].id;
203+
}
204+
return expected;
205+
}

src/__tests__/power.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ describe('power', () => {
5151
let peakList = gsd(data);
5252
let expected = [
5353
{
54+
id: peakList[0].id,
5455
x: 5,
5556
y: 10000,
5657
width: 0.3,
@@ -77,6 +78,7 @@ describe('power', () => {
7778
// This shape is anyway quite strange
7879
const expected = [
7980
{
81+
id: peakList[0].id,
8082
x: 4.45,
8183
y: 7921,
8284
width: 0.05,
@@ -92,6 +94,7 @@ describe('power', () => {
9294
},
9395
},
9496
{
97+
id: peakList[1].id,
9598
x: 5,
9699
y: 10000,
97100
width: 0.2,
@@ -107,6 +110,7 @@ describe('power', () => {
107110
},
108111
},
109112
{
113+
id: peakList[2].id,
110114
x: 5.55,
111115
y: 7921,
112116
width: 0.05,

src/__tests__/ubiquitin.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ describe('Global spectra deconvolution ubiquitin', () => {
2222
realTopDetection: true,
2323
sgOptions: { windowSize: 7, polynomial: 3 },
2424
});
25+
2526
expect(peaks[0]).toBeDeepCloseTo({
27+
id: peaks[0].id,
2628
x: 200.05527917306466,
2729
y: 28.795378784444413,
2830
ddY: -15468134.039875854,

src/gsd.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { v4 as generateID } from '@lukeed/uuid';
12
import type { DataXY } from 'cheminfo-types';
23
import { Shape1D } from 'ml-peak-shape-generator';
34
import { sgg, SGGOptions } from 'ml-savitzky-golay-generalized';
@@ -10,6 +11,8 @@ import {
1011
} from 'ml-spectra-processing';
1112

1213
import { GSDPeak } from './GSDPeak';
14+
import { MakeMandatory } from './utils/MakeMandatory';
15+
import { MakeOptional } from './utils/MakeOptional';
1316
import { optimizeTop } from './utils/optimizeTop';
1417
import { setShape } from './utils/setShape';
1518

@@ -52,7 +55,8 @@ export interface GSDOptions {
5255
*/
5356
shape?: Shape1D;
5457
}
55-
58+
export type GSDPeakID = MakeMandatory<GSDPeak, 'id'>;
59+
export type GSDPeakIDOptionalShape = MakeOptional<GSDPeak, 'shape'>;
5660
/**
5761
* Global spectra deconvolution
5862
* @param data - Object data with x and y arrays. Values in x has to be growing
@@ -61,7 +65,7 @@ export interface GSDOptions {
6165
6266
*/
6367

64-
export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] {
68+
export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeakID[] {
6569
let {
6670
sgOptions = {
6771
windowSize: 9,
@@ -217,7 +221,7 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] {
217221

218222
let lastK = -1;
219223

220-
const peaks: GSDPeak[] = [];
224+
const peaks: GSDPeakIDOptionalShape[] = [];
221225
for (const minddYIndex of minddY) {
222226
let deltaX = x[minddYIndex];
223227
let possible = -1;
@@ -245,6 +249,7 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] {
245249
if (yData[minddYIndex] > yThreshold) {
246250
let width = Math.abs(intervalR[possible].x - intervalL[possible].x);
247251
peaks.push({
252+
id: generateID(),
248253
x: deltaX,
249254
y: yData[minddYIndex],
250255
width,
@@ -254,7 +259,7 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] {
254259
from: intervalL[possible],
255260
to: intervalR[possible],
256261
},
257-
} as GSDPeak);
262+
});
258263
}
259264
}
260265
}
@@ -274,5 +279,5 @@ export function gsd(data: DataXY, options: GSDOptions = {}): GSDPeak[] {
274279
return a.x - b.x;
275280
});
276281

277-
return setShape(peaks, { shape });
282+
return setShape(peaks, { shape }) as GSDPeakID[];
278283
}

src/post/__tests__/broadenPeaks.test.ts

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ describe('broadenPeaks', () => {
300300
let result = broadenPeaks(
301301
[
302302
{
303+
id: '1',
303304
x: -0.5,
304305
y: 1,
305306
ddY: 0,
@@ -327,6 +328,7 @@ describe('broadenPeaks', () => {
327328
ddY: 0,
328329
width: 0.08,
329330
index: 575,
331+
shape: { kind: 'gaussian' },
330332
inflectionPoints: {
331333
from: { index: 573, x: 10.46 },
332334
to: { index: 577, x: 10.54 },
@@ -335,31 +337,36 @@ describe('broadenPeaks', () => {
335337
],
336338
{ factor: 20 },
337339
);
338-
expect(result).toBeDeepCloseTo([
339-
{
340-
x: -0.5,
341-
y: 1,
342-
index: 25,
343-
width: 1.3,
344-
from: { x: -1.3 },
345-
to: { x: 0 },
346-
},
347-
{
348-
x: 0.5,
349-
y: 1,
350-
index: 75,
351-
width: 1.3,
352-
from: { x: 0 },
353-
to: { x: 1.3 },
354-
},
355-
{
356-
x: 10.5,
357-
y: 1,
358-
index: 575,
359-
width: 1.6,
360-
from: { x: 9.7 },
361-
to: { x: 11.3 },
362-
},
363-
]);
340+
expect(result).toBeDeepCloseTo(
341+
[
342+
{
343+
id: '1',
344+
x: -0.5,
345+
y: 1,
346+
index: 25,
347+
width: 1.3,
348+
from: { x: -1.3 },
349+
to: { x: 0 },
350+
},
351+
{
352+
x: 0.5,
353+
y: 1,
354+
index: 75,
355+
width: 1.3,
356+
from: { x: 0 },
357+
to: { x: 1.3 },
358+
},
359+
{
360+
x: 10.5,
361+
y: 1,
362+
index: 575,
363+
shape: { kind: 'gaussian' },
364+
width: 1.6,
365+
from: { x: 9.7 },
366+
to: { x: 11.3 },
367+
},
368+
],
369+
1,
370+
);
364371
});
365372
});

0 commit comments

Comments
 (0)