@datagrok/peptides 0.8.9 → 0.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.eslintrc.json +2 -1
  2. package/dist/package-test.js +22626 -0
  3. package/dist/package.js +21429 -0
  4. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +8840 -0
  5. package/jest.config.js +33 -0
  6. package/package.json +75 -62
  7. package/src/__jest__/remote.test.ts +50 -0
  8. package/src/__jest__/test-node.ts +96 -0
  9. package/src/model.ts +950 -86
  10. package/src/monomer-library.ts +8 -0
  11. package/src/package-test.ts +3 -2
  12. package/src/package.ts +57 -22
  13. package/src/peptides.ts +165 -119
  14. package/src/styles.css +8 -0
  15. package/src/tests/peptides-tests.ts +17 -78
  16. package/src/tests/utils.ts +1 -7
  17. package/src/utils/SAR-multiple-filter.ts +439 -0
  18. package/src/utils/SAR-multiple-selection.ts +177 -0
  19. package/src/utils/cell-renderer.ts +49 -50
  20. package/src/utils/chem-palette.ts +61 -163
  21. package/src/utils/constants.ts +56 -0
  22. package/src/utils/filtering-statistics.ts +62 -0
  23. package/src/utils/multiple-sequence-alignment.ts +33 -2
  24. package/src/utils/multivariate-analysis.ts +79 -0
  25. package/src/utils/peptide-similarity-space.ts +12 -31
  26. package/src/utils/types.ts +10 -0
  27. package/src/viewers/logo-viewer.ts +2 -1
  28. package/src/viewers/peptide-space-viewer.ts +121 -0
  29. package/src/viewers/sar-viewer.ts +111 -313
  30. package/src/viewers/stacked-barchart-viewer.ts +126 -173
  31. package/src/widgets/analyze-peptides.ts +39 -18
  32. package/src/widgets/distribution.ts +61 -0
  33. package/src/widgets/manual-alignment.ts +3 -3
  34. package/src/widgets/peptide-molecule.ts +4 -4
  35. package/src/widgets/subst-table.ts +30 -22
  36. package/test-Peptides-f8114def7953-4bf59d70.html +256 -0
  37. package/src/describe.ts +0 -534
  38. package/src/utils/split-aligned.ts +0 -72
  39. package/src/viewers/subst-viewer.ts +0 -320
@@ -1,25 +1,32 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
- import {ChemPalette} from '../utils/chem-palette';
3
2
  import * as rxjs from 'rxjs';
4
3
  import * as ui from 'datagrok-api/ui';
5
- const cp = new ChemPalette('grok');
4
+ import {MonomerLibrary} from '../monomer-library';
5
+ import {PeptidesController} from '../peptides';
6
+
7
+ import * as C from '../utils/constants';
8
+ import * as type from '../utils/types';
6
9
 
7
10
  export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart) {
8
11
  if (grid.temp['containsBarchart'])
9
12
  return;
13
+
14
+ const compareBarParts = (bar1: type.BarChart.BarPart | null, bar2: type.BarChart.BarPart | null) =>
15
+ bar1 && bar2 && bar1.aaName === bar2.aaName && bar1.colName === bar2.colName;
10
16
 
11
- function eventAction(mouseMove: MouseEvent) {
17
+ const eventAction = (mouseMove: MouseEvent) => {
12
18
  const cell = grid.hitTest(mouseMove.offsetX, mouseMove.offsetY);
13
- if (cell !== null && cell?.isColHeader && cell.tableColumn?.semType == 'aminoAcids')
14
- barchart.highlight(cell, mouseMove.offsetX, mouseMove.offsetY, mouseMove);
15
- else
16
- return;
17
-
18
- if (cell?.isColHeader && cell.tableColumn?.semType == 'aminoAcids')
19
- barchart.beginSelection(mouseMove);
20
- else
21
- barchart.unhighlight();
22
- }
19
+ if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.AMINO_ACIDS) {
20
+ const newBarPart = barchart.findAARandPosition(cell, mouseMove);
21
+ const previousClickedBarPart = barchart._previousClickedBarPart;
22
+ if (mouseMove.type === 'click' && compareBarParts(newBarPart, previousClickedBarPart))
23
+ barchart.isSameBarClicked = true;
24
+ else
25
+ barchart.currentBarPart = newBarPart;
26
+ barchart.requestAction(mouseMove);
27
+ barchart.computeData();
28
+ }
29
+ };
23
30
 
24
31
  // The following events makes the barchart interactive
25
32
  rxjs.fromEvent<MouseEvent>(grid.overlay, 'mousemove').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
@@ -32,17 +39,18 @@ export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart) {
32
39
  grid.setOptions({'colHeaderHeight': 130});
33
40
 
34
41
  grid.onCellTooltip((cell, x, y) => {
35
- if (cell.tableColumn && ['aminoAcids', 'alignedSequence'].includes(cell.tableColumn.semType) ) {
36
- if (!cell.isColHeader)
37
- cp.showTooltip(cell, x, y);
38
- else {
39
- if (barchart.highlighted) {
40
- let elements: HTMLElement[] = [];
41
- elements = elements.concat([ui.divText(barchart.highlighted.aaName)]);
42
- ui.tooltip.show(ui.divV(elements), x, y);
43
- }
42
+ if (
43
+ cell.tableColumn &&
44
+ [C.SEM_TYPES.AMINO_ACIDS, C.SEM_TYPES.ALIGNED_SEQUENCE].includes(cell.tableColumn.semType as C.SEM_TYPES)
45
+ ) {
46
+ if (!cell.isColHeader) {
47
+ const monomerLib = cell.cell.dataFrame.temp[MonomerLibrary.id];
48
+ PeptidesController.chemPalette.showTooltip(cell, x, y, monomerLib);
49
+ } else if (barchart.currentBarPart) {
50
+ let elements: HTMLElement[] = [];
51
+ elements = elements.concat([ui.divText(barchart.currentBarPart.aaName)]);
52
+ ui.tooltip.show(ui.divV(elements), x, y);
44
53
  }
45
- return true;
46
54
  }
47
55
  return true;
48
56
  });
@@ -67,38 +75,39 @@ export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart) {
67
75
  });
68
76
 
69
77
  grid.temp['containsBarchart'] = true;
78
+ //FIXME: for some reason barchat didn't show when running analysis. This fixes it, but it's bad. Find a way to fix
79
+ // the problem
80
+ barchart.unhighlight();
70
81
  }
71
82
 
72
- type stackedBarChartDatatype = {
73
- 'name': string,
74
- 'data': {'name': string, 'count': number, 'selectedCount': number, 'fixedSelectedCount': number}[],
75
- }[];
76
-
77
- type bartStatsType = {
78
- [Key: string]: {'name': string, 'count': number, 'selectedCount': number, 'fixedSelectedCount': number}[],
79
- };
80
-
81
83
  export class StackedBarChart extends DG.JsViewer {
82
- public dataEmptyAA: string;
83
- public highlighted: {'colName' : string, 'aaName' : string} | null = null;
84
- public tableCanvas: HTMLCanvasElement | undefined;
85
- public aminoColumnNames: string[] = [];
86
- private ord: { [Key: string]: number; } = {};
87
- private aminoColumnIndices: {[Key: string]: number} = {};
88
- private aggregatedTables: {[Key: string]: DG.DataFrame} = {};
89
- private aggregatedHighlightedTables: {[Key: string]: DG.DataFrame} = {};
90
- private max = 0;
91
- private barStats: {[Key: string]: {'name': string, 'count': number, 'highlightedCount': number, 'selectedCount': number}[]} = {};
92
- private selected: {'colName' : string, 'aaName' : string}[] = [];
93
- private highlightedMask: DG.BitSet | null = null;
94
- private aggregatedSelectedTables: {[Key: string]: DG.DataFrame} = {};
84
+ dataEmptyAA: string;
85
+ _currentBarPart: type.BarChart.BarPart | null = null;
86
+ tableCanvas: HTMLCanvasElement | undefined;
87
+ aminoColumnNames: string[] = [];
88
+ ord: { [Key: string]: number; } = {};
89
+ aminoColumnIndices: {[Key: string]: number} = {};
90
+ aggregatedFilterTables: type.DataFrameDict = {};
91
+ max = 0;
92
+ barStats: {[Key: string]: type.BarChart.BarStatsObject[]} = {};
93
+ selected: type.BarChart.BarPart[] = [];
94
+ aggregatedSelectedTables: type.DataFrameDict = {};
95
+ controller!: PeptidesController;
96
+ isSameBarClicked: boolean = false;
97
+ _previousClickedBarPart: type.BarChart.BarPart | null = null;
95
98
 
96
99
  constructor() {
97
100
  super();
98
101
  this.dataEmptyAA = this.string('dataEmptyAA', '-');
99
102
  }
100
103
 
101
- init(): void {
104
+ get currentBarPart() { return this._currentBarPart; }
105
+ set currentBarPart(barPart: type.BarChart.BarPart | null) {
106
+ this._currentBarPart = barPart;
107
+ this.isSameBarClicked = false;
108
+ }
109
+
110
+ init() {
102
111
  const groups: {[key: string]: string[]} = {
103
112
  'yellow': ['C', 'U'],
104
113
  'red': ['G', 'P'],
@@ -108,35 +117,38 @@ export class StackedBarChart extends DG.JsViewer {
108
117
  'orange': ['S', 'T', 'N', 'Q'],
109
118
  };
110
119
  let i = 0;
111
-
112
- for (const value of Object.values(groups))
120
+
121
+ for (const value of Object.values(groups)) {
113
122
  for (const obj of value)
114
123
  this.ord[obj] = i++;
124
+ }
115
125
 
116
126
  this.aminoColumnNames = [];
117
127
  }
118
128
 
119
129
  // Stream subscriptions
120
- onTableAttached(): void {
130
+ async onTableAttached() {
121
131
  this.init();
132
+ this.controller = await PeptidesController.getInstance(this.dataFrame);
133
+ this.controller.init(this.dataFrame);
122
134
  if (this.dataFrame) {
123
135
  this.subs.push(DG.debounce(this.dataFrame.selection.onChanged, 50).subscribe((_) => this.computeData()));
124
136
  this.subs.push(DG.debounce(this.dataFrame.filter.onChanged, 50).subscribe((_) => this.computeData()));
125
- this.highlightedMask = DG.BitSet.create(this.dataFrame.rowCount);
137
+ this.subs.push(DG.debounce(this.dataFrame.onValuesChanged, 50).subscribe(() => this.computeData()));
126
138
  }
127
139
  }
128
140
 
129
141
  // Cancel subscriptions when the viewer is detached
130
- detach(): void {
142
+ detach() {
131
143
  this.subs.forEach((sub) => sub.unsubscribe());
132
144
  }
133
145
 
134
- computeData(): void {
146
+ computeData() {
135
147
  this.aminoColumnNames = [];
136
148
  this.aminoColumnIndices = {};
137
149
 
138
150
  this.dataFrame!.columns.names().forEach((name: string) => {
139
- if (this.dataFrame!.getCol(name).semType === 'aminoAcids' &&
151
+ if (this.dataFrame!.getCol(name).semType === C.SEM_TYPES.AMINO_ACIDS &&
140
152
  !this.dataFrame!.getCol(name).categories.includes('COOH') &&
141
153
  !this.dataFrame!.getCol(name).categories.includes('NH2')) {
142
154
  this.aminoColumnIndices[name] = this.aminoColumnNames.length + 1;
@@ -144,22 +156,15 @@ export class StackedBarChart extends DG.JsViewer {
144
156
  }
145
157
  });
146
158
 
147
- this.aggregatedTables = {};
148
- this.aggregatedHighlightedTables = {};
159
+ this.aggregatedFilterTables = {};
149
160
  this.aggregatedSelectedTables = {};
150
161
  //TODO: optimize it, why store so many tables?
151
162
  this.aminoColumnNames.forEach((name) => {
152
- this.aggregatedTables[name] = this.dataFrame!
163
+ this.aggregatedFilterTables[name] = this.dataFrame!
153
164
  .groupBy([name])
154
165
  .whereRowMask(this.dataFrame!.filter)
155
166
  .add('count', name, `${name}_count`)
156
167
  .aggregate();
157
-
158
- this.aggregatedHighlightedTables[name] = this.dataFrame!
159
- .groupBy([name])
160
- .whereRowMask(this.highlightedMask!)
161
- .add('count', name, `${name}_count`)
162
- .aggregate();
163
168
 
164
169
  this.aggregatedSelectedTables[name] = this.dataFrame!
165
170
  .groupBy([name])
@@ -170,64 +175,41 @@ export class StackedBarChart extends DG.JsViewer {
170
175
 
171
176
  this.barStats = {};
172
177
 
173
- for (const [name, df] of Object.entries(this.aggregatedTables)) {
174
- const colObj: {
175
- 'name': string,
176
- 'data': { 'name': string, 'count': number, 'highlightedCount': number, 'selectedCount': number}[],
177
- } = {'name': name, 'data': []};
178
+ for (const [name, df] of Object.entries(this.aggregatedFilterTables)) {
179
+ const colData: {'name': string, 'count': number, 'selectedCount': number}[] = [];
178
180
  const aminoCol = df.getCol(name);
179
181
  const aminoCountCol = df.getCol(`${name}_count`);
180
- this.barStats[colObj['name']] = colObj['data'];
181
-
182
+ this.barStats[name] = colData;
183
+
182
184
  for (let i = 0; i < df.rowCount; i++) {
183
185
  const amino = aminoCol.get(i);
184
186
  const aminoCount = aminoCountCol.get(i);
185
- const aminoObj = {'name': amino, 'count': aminoCount, 'highlightedCount': 0, 'selectedCount': 0};
186
- const aggHighlightedAminoCol = this.aggregatedHighlightedTables[name].getCol(`${name}`);
187
- const aggHighlightedCountCol = this.aggregatedHighlightedTables[name].getCol(`${name}_count`);
187
+ const aminoObj = {'name': amino, 'count': aminoCount, 'selectedCount': 0};
188
188
  const aggSelectedAminoCol = this.aggregatedSelectedTables[name].getCol(`${name}`);
189
189
  const aggSelectedCountCol = this.aggregatedSelectedTables[name].getCol(`${name}_count`);
190
190
 
191
191
  if (!amino || amino === this.dataEmptyAA)
192
192
  continue;
193
193
 
194
- colObj['data'].push(aminoObj);
195
-
196
- for (const col of [aggHighlightedCountCol, aggSelectedCountCol]) {
197
- for (let j = 0; j < col.length; j++) {
198
- const highlightedAmino = aggHighlightedAminoCol.get(j);
199
- const selectedAmino = aggSelectedAminoCol.get(j);
200
- const curAmino = (col == aggHighlightedCountCol ? highlightedAmino : selectedAmino);
201
- if (curAmino == amino) {
202
- aminoObj[col == aggHighlightedCountCol ? 'highlightedCount' : 'selectedCount'] = col.get(j);
203
- break;
204
- }
194
+ colData.push(aminoObj);
195
+
196
+ for (let j = 0; j < aggSelectedCountCol.length; j++) {
197
+ const selectedAmino = aggSelectedAminoCol.get(j);
198
+ const curAmino = (selectedAmino);
199
+ if (curAmino == amino) {
200
+ aminoObj['selectedCount'] = aggSelectedCountCol.get(j);
201
+ break;
205
202
  }
206
203
  }
207
204
  }
208
205
 
209
- colObj['data'] = colObj['data'].sort((o1, o2) => {
210
- if (this.ord[o1['name']] > this.ord[o2['name']])
211
- return -1;
212
-
213
- if (this.ord[o1['name']] < this.ord[o2['name']])
214
- return 1;
215
-
216
- return 0;
217
- });
206
+ colData.sort((o1, o2) => this.ord[o2['name']] - this.ord[o1['name']]);
218
207
  }
219
208
 
220
209
  this.max = this.dataFrame!.filter.trueCount;
221
210
  }
222
211
 
223
- renderBarToCanvas(
224
- g: CanvasRenderingContext2D,
225
- cell: DG.GridCell,
226
- x: number,
227
- y: number,
228
- w: number,
229
- h: number,
230
- ): void {
212
+ renderBarToCanvas(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number) {
231
213
  const name = cell.tableColumn!.name;
232
214
  const colNameSize = g.measureText(name).width;
233
215
  const barData = this.barStats[name];
@@ -254,7 +236,7 @@ export class StackedBarChart extends DG.JsViewer {
254
236
  const sBarHeight = h * obj['count'] / this.max;
255
237
  const gapSize = sBarHeight * innerMargin;
256
238
  const verticalShift = (this.max - sum) / this.max;
257
- const [color, aarOuter,,] = cp.getColorAAPivot(obj['name']);
239
+ const [color, aarOuter] = PeptidesController.chemPalette.getColorAAPivot(obj['name']);
258
240
  const textSize = g.measureText(aarOuter);
259
241
  const fontSize = 11;
260
242
  const leftMargin = (w - (aarOuter.length > 1 ? fontSize : textSize.width - 8)) / 2;
@@ -270,11 +252,10 @@ export class StackedBarChart extends DG.JsViewer {
270
252
  if (textSize.width <= subBartHeight) {
271
253
  const origTransform = g.getTransform();
272
254
 
273
- if (color != ChemPalette.undefinedColor) {
255
+ if (color != PeptidesController.chemPalette.undefinedColor) {
274
256
  g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
275
257
  g.fillStyle = 'black';
276
- }
277
- else
258
+ } else
278
259
  g.strokeRect(x + xStart + 0.5, y + yStart, barWidth - 1, subBartHeight);
279
260
 
280
261
  g.font = `${fontSize}px monospace`;
@@ -289,9 +270,8 @@ export class StackedBarChart extends DG.JsViewer {
289
270
 
290
271
  g.fillText(aarOuter, absX, absY);
291
272
  g.setTransform(origTransform);
292
- } else {
273
+ } else
293
274
  g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
294
- }
295
275
 
296
276
  if (obj['selectedCount'] > eps) {
297
277
  g.fillStyle = 'rgb(255,165,0)';
@@ -299,29 +279,20 @@ export class StackedBarChart extends DG.JsViewer {
299
279
  x + xStart - w * selectLineRatio * 2,
300
280
  y + yStart,
301
281
  barWidth * selectLineRatio,
302
- h * obj['selectedCount'] / this.max - gapSize
303
- );
304
- }
305
-
306
- if (obj['highlightedCount'] > eps && obj['highlightedCount'] > obj['selectedCount']) {
307
- g.fillStyle = 'rgb(209,242,251)';
308
- g.fillRect(
309
- x + xStart - w * selectLineRatio * 2,
310
- y + yStart + h * obj['selectedCount'] / this.max - gapSize,
311
- barWidth * selectLineRatio,
312
- h * (obj['highlightedCount'] - obj['selectedCount']) / this.max - gapSize
282
+ h * obj['selectedCount'] / this.max - gapSize,
313
283
  );
314
284
  }
315
285
 
316
286
  sum -= obj['count'];
317
287
  });
318
- return;
319
288
  }
320
289
 
321
- highlight(cell: DG.GridCell, offsetX:number, offsetY:number, mouseEvent: MouseEvent): void {
290
+ findAARandPosition(cell: DG.GridCell, mouseEvent: MouseEvent) {
322
291
  if (!cell.tableColumn?.name || !this.aminoColumnNames.includes(cell.tableColumn.name))
323
- return;
324
-
292
+ return null;
293
+
294
+ const offsetX = mouseEvent.offsetX;
295
+ const offsetY = mouseEvent.offsetY;
325
296
  const colName = cell.tableColumn?.name;
326
297
  const innerMargin = 0.02;
327
298
  const margin = 0.2;
@@ -339,71 +310,53 @@ export class StackedBarChart extends DG.JsViewer {
339
310
  sum += obj['count'];
340
311
  });
341
312
 
342
- this.highlighted = null;
343
- barData.forEach((obj) => {
313
+ const xStart = x + (w - barWidth) / 2;
314
+ for (const obj of barData) {
344
315
  const sBarHeight = h * obj['count'] / this.max;
345
316
  const gapSize = sBarHeight * innerMargin;
346
317
  const verticalShift = (this.max - sum) / this.max;
347
318
  const subBartHeight = sBarHeight - gapSize;
348
- const yStart = h * verticalShift + gapSize / 2;
349
- const xStart = (w - barWidth) / 2;
350
-
351
- if (offsetX >= x + xStart &&
352
- offsetY >= y + yStart &&
353
- offsetX <= x + xStart + barWidth &&
354
- offsetY <= y + yStart + subBartHeight) {
355
- this.highlighted = {'colName': colName, 'aaName': obj['name']};
356
- }
357
-
358
- sum -= obj['count'];
359
- });
360
-
361
- if (!this.highlighted)
362
- return;
319
+ const yStart = y + h * verticalShift + gapSize / 2;
363
320
 
364
- if (mouseEvent.type == 'click') {
365
- let idx = -1;
321
+ const isIntersectingX = offsetX >= xStart && offsetX <= xStart + barWidth;
322
+ const isIntersectingY = offsetY >= yStart && offsetY <= yStart + subBartHeight;
366
323
 
367
- for (let i = 0; i < this.selected.length; ++i)
368
- if (JSON.stringify(this.selected[i]) == JSON.stringify(this.highlighted))
369
- idx = i;
324
+ if (isIntersectingX && isIntersectingY)
325
+ return {'colName': colName, 'aaName': obj['name']};
370
326
 
371
- if (mouseEvent.shiftKey && idx == -1)
372
- this.selected.push(this.highlighted);
327
+ sum -= obj['count'];
328
+ }
373
329
 
374
- if (mouseEvent.shiftKey && (mouseEvent.ctrlKey || mouseEvent.metaKey) && idx != -1)
375
- this.selected.splice(idx, 1);
376
- }
330
+ return null;
377
331
  }
378
332
 
379
- unhighlight(): void {
380
- this.highlighted = null;
381
- this.highlightedMask!.setAll(false);
333
+ unhighlight() {
334
+ ui.tooltip.hide();
382
335
  this.computeData();
383
336
  }
384
337
 
385
- beginSelection(event: MouseEvent): void {
386
- if (!this.dataFrame)
338
+ /**
339
+ * Requests highlight/select/filter action based on currentBarPart
340
+ * @param event
341
+ * @returns
342
+ */
343
+ requestAction(event: MouseEvent) {
344
+ if (!this._currentBarPart)
387
345
  return;
388
-
389
- this.highlightedMask!.setAll(false);
390
-
391
- this.dataFrame.selection.handleClick((i: number) => {
392
- for (const high of this.selected)
393
- if (high['aaName'] === (this.dataFrame!.getCol(high['colName']).get(i)))
394
- return true;
395
- return false;
396
- }, event);
397
-
398
- if (this.highlighted) {
399
- this.dataFrame.rows.match({[this.highlighted['colName']]: this.highlighted['aaName']}).highlight();
400
- this.highlightedMask!.handleClick((i: number) => {
401
- if (this.highlighted!['aaName'] === (this.dataFrame!.getCol(this.highlighted!['colName']).get(i)))
402
- return true;
403
- return false;
404
- }, event);
405
- }
406
-
407
- this.computeData();
346
+ let aar = this._currentBarPart!['aaName'];
347
+ let position = this._currentBarPart!['colName'];
348
+ if (event.type === 'click') {
349
+ if (this.isSameBarClicked) {
350
+ aar = position = C.CATEGORIES.ALL;
351
+ this.currentBarPart = null;
352
+ }
353
+ this.controller.setSARGridCellAt(aar, position);
354
+ this._previousClickedBarPart = this._currentBarPart;
355
+ } else {
356
+ ui.tooltip.showRowGroup(this.dataFrame, (i) => {
357
+ const currentAAR = this.dataFrame.get(position, i);
358
+ return currentAAR === aar;
359
+ }, event.offsetX, event.offsetY);
360
+ }
408
361
  }
409
362
  }
@@ -1,9 +1,12 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
+ import {callMVA} from '../utils/multivariate-analysis';
4
5
  import {PeptidesController} from '../peptides';
5
6
  import '../styles.css';
6
7
  import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
8
+ import * as C from '../utils/constants';
9
+ import {FilteringStatistics} from '../utils/filtering-statistics';
7
10
 
8
11
  /**
9
12
  * Peptide analysis widget.
@@ -15,9 +18,7 @@ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations'
15
18
  * @param {DG.DataFrame} currentDf Working table.
16
19
  * @return {Promise<DG.Widget>} Widget containing peptide analysis.
17
20
  */
18
- export async function analyzePeptidesWidget(
19
- col: DG.Column, view: DG.TableView, tableGrid: DG.Grid, currentDf: DG.DataFrame,
20
- ): Promise<DG.Widget> {
21
+ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Column): Promise<DG.Widget> {
21
22
  let tempCol = null;
22
23
  let tempDf: DG.DataFrame;
23
24
  let newScaledColName: string;
@@ -32,13 +33,14 @@ export async function analyzePeptidesWidget(
32
33
  'Scaling', 'none', ['none', 'lg', '-lg'],
33
34
  async (currentMethod: string) => {
34
35
  const currentActivityCol = activityColumnChoice.value.name;
36
+
35
37
 
36
38
  [tempDf, newScaledColName] = await PeptidesController.scaleActivity(
37
- currentMethod, currentActivityCol, `${currentActivityCol}Scaled`, currentDf);
39
+ currentMethod, currentDf, currentActivityCol, true);
38
40
 
39
41
  const hist = tempDf.plot.histogram({
40
42
  filteringEnabled: false,
41
- valueColumnName: `${currentActivityCol}Scaled`,
43
+ valueColumnName: C.COLUMNS_NAMES.ACTIVITY_SCALED,
42
44
  legendVisibility: 'Never',
43
45
  showXAxis: true,
44
46
  showColumnSelector: false,
@@ -68,30 +70,49 @@ export async function analyzePeptidesWidget(
68
70
  const startBtn = ui.button('Launch SAR', async () => {
69
71
  const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
70
72
  if (activityColumnChoice.value.type === DG.TYPE.FLOAT) {
71
- const activityColumn = activityColumnChoice.value.name;
72
- const activityColumnScaled = `${activityColumn}Scaled`;
73
- const originalDfColumns = (currentDf.columns as DG.ColumnList).names();
73
+ const activityColumn: string = activityColumnChoice.value.name;
74
+ // const activityColumnScaled = `${activityColumn}Scaled`;
75
+ // const originalDfColumns = (currentDf.columns as DG.ColumnList).names();
74
76
  const options: StringDictionary = {
75
- 'activityColumnName': activityColumn,
76
77
  'scaling': activityScalingMethod.value,
77
78
  };
78
79
 
79
- const scaledCol = tempDf.getCol(activityColumnScaled);
80
- (currentDf.columns as DG.ColumnList).add(scaledCol);
81
- tableGrid.col(activityColumnScaled)!.name = newScaledColName;
82
- scaledCol.temp['gridName'] = newScaledColName;
83
- if (newScaledColName === activityColumn)
84
- tableGrid.col(activityColumn)!.name = `~${activityColumn}`;
85
- tableGrid.columns.setOrder([newScaledColName]);
80
+ //prepare new DF
81
+ const newDf = currentDf.clone(currentDf.filter, [col.name, activityColumn]);
82
+ newDf.getCol(activityColumn).name = C.COLUMNS_NAMES.ACTIVITY;
83
+ newDf.getCol(col.name).name = C.COLUMNS_NAMES.ALIGNED_SEQUENCE;
84
+ const activityScaledCol = tempDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
85
+ (newDf.columns as DG.ColumnList).add(activityScaledCol);
86
+ // newDf.temp[C.COLUMNS_NAMES.ACTIVITY] = activityColumn;
87
+ newDf.name = 'Peptides analysis';
88
+ newDf.temp[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newScaledColName;
89
+ newDf.tags['isPeptidesAnalysis'] = 'true';
86
90
 
87
- const peptides = PeptidesController.getInstance(currentDf);
88
- await peptides.init(tableGrid, view, options, col, originalDfColumns);
91
+ const peptides = await PeptidesController.getInstance(newDf);
92
+ await peptides.init(newDf);
89
93
  } else
90
94
  grok.shell.error('The activity column must be of floating point number type!');
91
95
  progress.close();
92
96
  });
93
97
  startBtn.style.alignSelf = 'center';
94
98
 
99
+ // const startMVABtn = ui.button('Launch MVA', async () => {
100
+ // if (activityColumnChoice.value.type === DG.TYPE.FLOAT) {
101
+ // const progress = DG.TaskBarProgressIndicator.create('Loading MVA...');
102
+
103
+ // const options: {[key: string]: string} = {
104
+ // 'activityColumnName': activityColumnChoice.value.name,
105
+ // 'scaling': activityScalingMethod.value,
106
+ // };
107
+
108
+ // await callMVA(tableGrid, view, currentDf, options, col);
109
+
110
+ // progress.close();
111
+ // } else
112
+ // grok.shell.error('The activity column must be of floating point number type!');
113
+ // });
114
+
115
+
95
116
  const viewer = await currentDf.plot.fromType('peptide-logo-viewer');
96
117
 
97
118
  return new DG.Widget(
@@ -0,0 +1,61 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import * as DG from 'datagrok-api/dg';
3
+
4
+ import * as C from '../utils/constants';
5
+ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
6
+ import {FilteringStatistics} from '../utils/filtering-statistics';
7
+
8
+ export function getDistributionPlot(df: DG.DataFrame, valueCol: string, splitCol: string) {
9
+ return df.plot.histogram({
10
+ // const hist = originalDf.plot.histogram({
11
+ filteringEnabled: false,
12
+ valueColumnName: valueCol,
13
+ splitColumnName: splitCol,
14
+ legendVisibility: 'Never',
15
+ showXAxis: true,
16
+ showColumnSelector: false,
17
+ showRangeSlider: false,
18
+ });
19
+ }
20
+
21
+ export function getDistributionWidget(table: DG.DataFrame): DG.Widget {
22
+ // const labelStr = this.multipleFilter.filterLabel;
23
+ const splitCol = table.col(C.COLUMNS_NAMES.SPLIT_COL);
24
+ if (!splitCol)
25
+ return new DG.Widget(ui.divText('No distribution'));
26
+ let [aarStr, otherStr] = splitCol.categories;
27
+ if (typeof otherStr === 'undefined')
28
+ [aarStr, otherStr] = [otherStr, aarStr];
29
+ // const currentColor = DG.Color.toHtml(DG.Color.orange);
30
+ // const otherColor = DG.Color.toHtml(DG.Color.blue);
31
+ const currentColor = DG.Color.toHtml(DG.Color.getCategoryColor(splitCol, aarStr));
32
+ const otherColor = DG.Color.toHtml(DG.Color.getCategoryColor(splitCol, otherStr));
33
+
34
+ const currentLabel = ui.label(aarStr, {style: {color: currentColor}});
35
+ const otherLabel = ui.label(otherStr, {style: {color: otherColor}});
36
+ const elements: (HTMLLabelElement | HTMLElement)[] = [currentLabel, otherLabel];
37
+
38
+ const getContent = () => {
39
+ // const processedDf =
40
+ // table.clone(null, [C.COLUMNS_NAMES.ACTIVITY_SCALED, C.COLUMNS_NAMES.SPLIT_COL]);
41
+ const hist = getDistributionPlot(table, C.COLUMNS_NAMES.ACTIVITY_SCALED, C.COLUMNS_NAMES.SPLIT_COL).root;
42
+
43
+ hist.style.width = 'auto';
44
+ elements.push(hist);
45
+
46
+ const stats = (table.temp[C.STATS] as FilteringStatistics).result;
47
+ if (stats) {
48
+ const tableMap: StringDictionary = {
49
+ 'Statistics:': '',
50
+ 'Count': stats.count.toString(),
51
+ 'p-value': stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(2),
52
+ 'Mean difference': stats.meanDifference.toFixed(2),
53
+ };
54
+
55
+ elements.push(ui.tableFromMap(tableMap));
56
+ }
57
+ return ui.divV(elements);
58
+ };
59
+
60
+ return new DG.Widget(getContent());
61
+ }
@@ -33,9 +33,9 @@ export function manualAlignmentWidget(alignedSequenceCol: DG.Column, currentDf:
33
33
  grok.shell.o = null;
34
34
  grok.shell.o = temp;
35
35
 
36
- // PeptidesModel.getOrInit(currentDf).updateDefault();
37
- // Peptides.getOrInitModel(currentDf);
38
- await PeptidesController.getInstance(currentDf).updateDefault();
36
+ const peptidesController = await PeptidesController.getInstance(currentDf);
37
+ peptidesController.init(currentDf);
38
+ peptidesController.updateDefault();
39
39
  });
40
40
 
41
41
  const resetBtn = ui.button(
@@ -44,16 +44,16 @@ export async function peptideMoleculeWidget(pep: string): Promise<DG.Widget> {
44
44
  export function getMolecule(pep: string): string {
45
45
  const split = pep.split('-');
46
46
  const mols = [];
47
- const chemPalette = PeptidesController.getChemPalette();
47
+ const chemPalette = PeptidesController.chemPalette;
48
48
  for (let i = 1; i < split.length - 1; i++) {
49
49
  if (split[i] in chemPalette.AASmiles) {
50
50
  const aar = chemPalette.AASmiles[split[i]];
51
- mols[i] = aar.substr(0, aar.length - 1);
51
+ mols[i] = aar.substring(0, aar.length - 1);
52
52
  } else if (!split[i] || split[i] == '-')
53
53
  mols[i] = '';
54
54
  else
55
55
  return '';
56
56
  }
57
- const smiles = mols.join('') + 'O';
58
- return smiles;
57
+
58
+ return mols.join('') + 'O';
59
59
  }