@datagrok/peptides 1.17.2 → 1.17.3
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.
- package/.eslintrc.json +2 -3
- package/CHANGELOG.md +6 -1
- package/dist/356.js +2 -0
- package/dist/796.js +2 -0
- package/dist/8473fcbfb6e85ca6c852.wasm +0 -0
- package/dist/9a8fbf37666e32487835.wasm +0 -0
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +3 -2
- package/src/model.ts +168 -156
- package/src/package.ts +2 -4
- package/src/tests/benchmarks.ts +11 -17
- package/src/tests/core.ts +1 -1
- package/src/utils/algorithms.ts +15 -30
- package/src/utils/misc.ts +1 -2
- package/src/utils/types.ts +5 -2
- package/src/viewers/logo-summary.ts +27 -47
- package/src/viewers/sar-viewer.ts +60 -103
- package/src/widgets/peptides.ts +41 -8
- package/src/widgets/settings.ts +25 -17
|
@@ -154,28 +154,25 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
154
154
|
* @return - position columns.
|
|
155
155
|
*/
|
|
156
156
|
get positionColumns(): DG.Column<string>[] {
|
|
157
|
-
if (this._positionColumns != null)
|
|
157
|
+
if (this._positionColumns != null)
|
|
158
158
|
return this._positionColumns;
|
|
159
|
-
}
|
|
160
159
|
|
|
161
160
|
|
|
162
161
|
const getSharedPositionColumns = (viewerType: VIEWER_TYPE): DG.Column<string>[] | null => {
|
|
163
162
|
const viewer = this.model.findViewer(viewerType) as SARViewer | LogoSummaryTable | null;
|
|
164
|
-
if (this.sequenceColumnName === viewer?.sequenceColumnName)
|
|
163
|
+
if (this.sequenceColumnName === viewer?.sequenceColumnName)
|
|
165
164
|
return viewer._positionColumns;
|
|
166
|
-
}
|
|
167
165
|
|
|
168
166
|
|
|
169
167
|
return null;
|
|
170
168
|
};
|
|
171
169
|
|
|
172
|
-
if (this.model.positionColumns != null && this.sequenceColumnName === this.model.settings?.sequenceColumnName)
|
|
170
|
+
if (this.model.positionColumns != null && this.sequenceColumnName === this.model.settings?.sequenceColumnName)
|
|
173
171
|
this._positionColumns = this.model.positionColumns;
|
|
174
|
-
|
|
172
|
+
else if (this instanceof MonomerPosition)
|
|
175
173
|
this._positionColumns = getSharedPositionColumns(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
176
|
-
|
|
174
|
+
else if (this instanceof MostPotentResidues)
|
|
177
175
|
this._positionColumns = getSharedPositionColumns(VIEWER_TYPE.MONOMER_POSITION);
|
|
178
|
-
}
|
|
179
176
|
|
|
180
177
|
|
|
181
178
|
this._positionColumns ??= getSharedPositionColumns(VIEWER_TYPE.LOGO_SUMMARY_TABLE) ??
|
|
@@ -191,9 +188,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
191
188
|
* @return - monomer-position statistics.
|
|
192
189
|
*/
|
|
193
190
|
get monomerPositionStats(): MonomerPositionStats {
|
|
194
|
-
if (this._monomerPositionStats != null)
|
|
191
|
+
if (this._monomerPositionStats != null)
|
|
195
192
|
return this._monomerPositionStats;
|
|
196
|
-
}
|
|
197
193
|
|
|
198
194
|
|
|
199
195
|
const isMonomerPositionStatsEqual = (other: SARViewer | PeptidesSettings | null): boolean =>
|
|
@@ -203,21 +199,19 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
203
199
|
|
|
204
200
|
const getSharedStats = (viewerType: VIEWER_TYPE): MonomerPositionStats | null => {
|
|
205
201
|
const viewer = this.model.findViewer(viewerType) as SARViewer | null;
|
|
206
|
-
if (isMonomerPositionStatsEqual(viewer))
|
|
202
|
+
if (isMonomerPositionStatsEqual(viewer))
|
|
207
203
|
return viewer!._monomerPositionStats;
|
|
208
|
-
}
|
|
209
204
|
|
|
210
205
|
|
|
211
206
|
return null;
|
|
212
207
|
};
|
|
213
208
|
|
|
214
|
-
if (this.model.monomerPositionStats !== null && isMonomerPositionStatsEqual(this.model.settings))
|
|
209
|
+
if (this.model.monomerPositionStats !== null && isMonomerPositionStatsEqual(this.model.settings))
|
|
215
210
|
this._monomerPositionStats = this.model.monomerPositionStats;
|
|
216
|
-
|
|
211
|
+
else if (this instanceof MonomerPosition)
|
|
217
212
|
this._monomerPositionStats = getSharedStats(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
218
|
-
|
|
213
|
+
else if (this instanceof MostPotentResidues)
|
|
219
214
|
this._monomerPositionStats = getSharedStats(VIEWER_TYPE.MONOMER_POSITION);
|
|
220
|
-
}
|
|
221
215
|
|
|
222
216
|
|
|
223
217
|
this._monomerPositionStats ??= calculateMonomerPositionStatistics(this.getScaledActivityColumn(),
|
|
@@ -233,9 +227,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
233
227
|
* @return - mutation cliffs.
|
|
234
228
|
*/
|
|
235
229
|
get mutationCliffs(): type.MutationCliffs | null {
|
|
236
|
-
if (this._mutationCliffs != null)
|
|
230
|
+
if (this._mutationCliffs != null)
|
|
237
231
|
return this._mutationCliffs;
|
|
238
|
-
}
|
|
239
232
|
|
|
240
233
|
|
|
241
234
|
const isMutationCliffsEqual = (v1: SARViewer, v2: SARViewer | null): boolean =>
|
|
@@ -249,19 +242,17 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
249
242
|
|
|
250
243
|
const getSharedMutationCliffs = (viewerType: VIEWER_TYPE): type.MutationCliffs | null => {
|
|
251
244
|
const viewer = this.model.findViewer(viewerType) as SARViewer | null;
|
|
252
|
-
if (isMutationCliffsEqual(this, viewer))
|
|
245
|
+
if (isMutationCliffsEqual(this, viewer))
|
|
253
246
|
return viewer!._mutationCliffs;
|
|
254
|
-
}
|
|
255
247
|
|
|
256
248
|
|
|
257
249
|
return null;
|
|
258
250
|
};
|
|
259
251
|
|
|
260
|
-
if (this instanceof MonomerPosition)
|
|
252
|
+
if (this instanceof MonomerPosition)
|
|
261
253
|
this._mutationCliffs = getSharedMutationCliffs(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
262
|
-
|
|
254
|
+
else if (this instanceof MostPotentResidues)
|
|
263
255
|
this._mutationCliffs = getSharedMutationCliffs(VIEWER_TYPE.MONOMER_POSITION);
|
|
264
|
-
}
|
|
265
256
|
|
|
266
257
|
|
|
267
258
|
return this._mutationCliffs;
|
|
@@ -316,9 +307,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
316
307
|
*/
|
|
317
308
|
getScaledActivityColumn(isFiltered: boolean = false): DG.Column<number> {
|
|
318
309
|
if (this.model.settings?.activityColumnName === this.activityColumnName &&
|
|
319
|
-
this.model.settings?.activityScaling === this.activityScaling)
|
|
310
|
+
this.model.settings?.activityScaling === this.activityScaling)
|
|
320
311
|
this._scaledActivityColumn = this.model.getScaledActivityColumn(isFiltered);
|
|
321
|
-
}
|
|
322
312
|
|
|
323
313
|
|
|
324
314
|
this._scaledActivityColumn ??= scaleActivity(this.dataFrame.getCol(this.activityColumnName),
|
|
@@ -343,11 +333,10 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
343
333
|
shiftPressed: false,
|
|
344
334
|
ctrlPressed: false,
|
|
345
335
|
}, notify: boolean = true): void {
|
|
346
|
-
if (notify)
|
|
336
|
+
if (notify)
|
|
347
337
|
this.mutationCliffsSelection = modifySelection(this.mutationCliffsSelection, monomerPosition, options);
|
|
348
|
-
|
|
338
|
+
else
|
|
349
339
|
this._mutationCliffsSelection = modifySelection(this.mutationCliffsSelection, monomerPosition, options);
|
|
350
|
-
}
|
|
351
340
|
}
|
|
352
341
|
|
|
353
342
|
/**
|
|
@@ -383,16 +372,14 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
383
372
|
break;
|
|
384
373
|
case SAR_PROPERTIES.COLUMNS:
|
|
385
374
|
case SAR_PROPERTIES.AGGREGATION:
|
|
386
|
-
if (this instanceof MostPotentResidues)
|
|
375
|
+
if (this instanceof MostPotentResidues)
|
|
387
376
|
this._viewerGrid = null;
|
|
388
|
-
}
|
|
389
377
|
|
|
390
378
|
|
|
391
379
|
break;
|
|
392
380
|
}
|
|
393
|
-
if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
381
|
+
if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
394
382
|
this.calculateMutationCliffs().then((mc) => this.mutationCliffs = mc);
|
|
395
|
-
}
|
|
396
383
|
}
|
|
397
384
|
|
|
398
385
|
/**
|
|
@@ -432,9 +419,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
432
419
|
?.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
|
|
433
420
|
this.getProperty(`${SAR_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
|
|
434
421
|
?.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
|
|
435
|
-
if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
422
|
+
if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
436
423
|
this.calculateMutationCliffs().then((mc) => this.mutationCliffs = mc);
|
|
437
|
-
}
|
|
438
424
|
} else {
|
|
439
425
|
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
440
426
|
grok.log.error(msg);
|
|
@@ -554,11 +540,10 @@ export class MonomerPosition extends SARViewer {
|
|
|
554
540
|
shiftPressed: false,
|
|
555
541
|
ctrlPressed: false,
|
|
556
542
|
}, notify: boolean = true): void {
|
|
557
|
-
if (notify)
|
|
543
|
+
if (notify)
|
|
558
544
|
this.invariantMapSelection = modifySelection(this.invariantMapSelection, monomerPosition, options);
|
|
559
|
-
|
|
545
|
+
else
|
|
560
546
|
this._invariantMapSelection = modifySelection(this.invariantMapSelection, monomerPosition, options);
|
|
561
|
-
}
|
|
562
547
|
}
|
|
563
548
|
|
|
564
549
|
/**
|
|
@@ -581,9 +566,8 @@ export class MonomerPosition extends SARViewer {
|
|
|
581
566
|
this.model.df.columns.toList().forEach((col) => {
|
|
582
567
|
col.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = null;
|
|
583
568
|
});
|
|
584
|
-
if (this.doRender)
|
|
569
|
+
if (this.doRender)
|
|
585
570
|
this.render();
|
|
586
|
-
}
|
|
587
571
|
}
|
|
588
572
|
|
|
589
573
|
/**
|
|
@@ -596,18 +580,16 @@ export class MonomerPosition extends SARViewer {
|
|
|
596
580
|
for (const col of splitSeqCols) {
|
|
597
581
|
const colCat = col.categories;
|
|
598
582
|
for (const cat of colCat) {
|
|
599
|
-
if (cat !== '')
|
|
583
|
+
if (cat !== '')
|
|
600
584
|
uniqueMonomers.add(cat);
|
|
601
|
-
}
|
|
602
585
|
}
|
|
603
586
|
}
|
|
604
587
|
|
|
605
588
|
const monomerCol = DG.Column.fromStrings(C.COLUMNS_NAMES.MONOMER, Array.from(uniqueMonomers));
|
|
606
589
|
const monomerPositionDf = DG.DataFrame.fromColumns([monomerCol]);
|
|
607
590
|
monomerPositionDf.name = 'SAR';
|
|
608
|
-
for (const col of splitSeqCols)
|
|
591
|
+
for (const col of splitSeqCols)
|
|
609
592
|
monomerPositionDf.columns.addNewBool(col.name);
|
|
610
|
-
}
|
|
611
593
|
|
|
612
594
|
|
|
613
595
|
return monomerPositionDf;
|
|
@@ -647,9 +629,8 @@ export class MonomerPosition extends SARViewer {
|
|
|
647
629
|
grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
648
630
|
DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
|
|
649
631
|
try {
|
|
650
|
-
if (!this.keyPressed)
|
|
632
|
+
if (!this.keyPressed)
|
|
651
633
|
return;
|
|
652
|
-
}
|
|
653
634
|
|
|
654
635
|
|
|
655
636
|
if (this.currentGridCell !== null) {
|
|
@@ -671,9 +652,9 @@ export class MonomerPosition extends SARViewer {
|
|
|
671
652
|
}
|
|
672
653
|
}
|
|
673
654
|
const monomerPosition = this.getMonomerPosition(gridCell);
|
|
674
|
-
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
655
|
+
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
675
656
|
this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, true);
|
|
676
|
-
|
|
657
|
+
else {
|
|
677
658
|
const hasMutationCliffs = this.mutationCliffs
|
|
678
659
|
?.get(monomerPosition.monomerOrCluster)?.get(monomerPosition.positionOrClusterType)?.size;
|
|
679
660
|
if (hasMutationCliffs) {
|
|
@@ -690,17 +671,15 @@ export class MonomerPosition extends SARViewer {
|
|
|
690
671
|
});
|
|
691
672
|
grid.root.addEventListener('keydown', (ev) => {
|
|
692
673
|
this.keyPressed = ev.key.startsWith('Arrow');
|
|
693
|
-
if (this.keyPressed)
|
|
674
|
+
if (this.keyPressed)
|
|
694
675
|
return;
|
|
695
|
-
}
|
|
696
676
|
|
|
697
677
|
|
|
698
678
|
if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey)) {
|
|
699
|
-
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
679
|
+
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
700
680
|
this._invariantMapSelection = initSelection(this.positionColumns);
|
|
701
|
-
|
|
681
|
+
else
|
|
702
682
|
this._mutationCliffsSelection = initSelection(this.positionColumns);
|
|
703
|
-
}
|
|
704
683
|
} else if (ev.code === 'KeyA' && ev.ctrlKey) {
|
|
705
684
|
const positions = Object.keys(this.monomerPositionStats).filter((pos) => pos !== 'general');
|
|
706
685
|
for (const position of positions) {
|
|
@@ -708,9 +687,9 @@ export class MonomerPosition extends SARViewer {
|
|
|
708
687
|
.filter((monomer) => monomer !== 'general');
|
|
709
688
|
for (const monomer of monomers) {
|
|
710
689
|
const monomerPosition = {monomerOrCluster: monomer, positionOrClusterType: position};
|
|
711
|
-
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
690
|
+
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
712
691
|
this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, false);
|
|
713
|
-
|
|
692
|
+
else {
|
|
714
693
|
this.modifyMutationCliffsSelection(monomerPosition, {
|
|
715
694
|
shiftPressed: true,
|
|
716
695
|
ctrlPressed: false,
|
|
@@ -718,9 +697,8 @@ export class MonomerPosition extends SARViewer {
|
|
|
718
697
|
}
|
|
719
698
|
}
|
|
720
699
|
}
|
|
721
|
-
} else
|
|
700
|
+
} else
|
|
722
701
|
return;
|
|
723
|
-
}
|
|
724
702
|
|
|
725
703
|
|
|
726
704
|
this.model.fireBitsetChanged(VIEWER_TYPE.MONOMER_POSITION);
|
|
@@ -728,17 +706,15 @@ export class MonomerPosition extends SARViewer {
|
|
|
728
706
|
});
|
|
729
707
|
grid.root.addEventListener('click', (ev) => {
|
|
730
708
|
const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
731
|
-
if (!gridCell?.isTableCell || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
|
|
709
|
+
if (!gridCell?.isTableCell || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
|
|
732
710
|
return;
|
|
733
|
-
}
|
|
734
711
|
|
|
735
712
|
|
|
736
713
|
const monomerPosition = this.getMonomerPosition(gridCell);
|
|
737
714
|
if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
|
|
738
715
|
this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
|
|
739
|
-
if (isSelectionEmpty(this.invariantMapSelection))
|
|
716
|
+
if (isSelectionEmpty(this.invariantMapSelection))
|
|
740
717
|
monomerPositionDf.currentRowIdx = -1;
|
|
741
|
-
}
|
|
742
718
|
} else {
|
|
743
719
|
const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
|
|
744
720
|
?.get(monomerPosition.positionOrClusterType)?.size;
|
|
@@ -801,9 +777,8 @@ export class MonomerPosition extends SARViewer {
|
|
|
801
777
|
const columnProperty = this.getProperty(MONOMER_POSITION_PROPERTIES.COLOR);
|
|
802
778
|
if (columnProperty) {
|
|
803
779
|
columnProperty.choices = wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name);
|
|
804
|
-
if (columnProperty.get(this) === C.COLUMNS_NAMES.ACTIVITY_SCALED)
|
|
780
|
+
if (columnProperty.get(this) === C.COLUMNS_NAMES.ACTIVITY_SCALED)
|
|
805
781
|
columnProperty.set(this, C.COLUMNS_NAMES.ACTIVITY);
|
|
806
|
-
}
|
|
807
782
|
}
|
|
808
783
|
|
|
809
784
|
$(this.root).empty();
|
|
@@ -875,9 +850,8 @@ export class MostPotentResidues extends SARViewer {
|
|
|
875
850
|
*/
|
|
876
851
|
onPropertyChanged(property: DG.Property): void {
|
|
877
852
|
super.onPropertyChanged(property);
|
|
878
|
-
if (this.doRender)
|
|
853
|
+
if (this.doRender)
|
|
879
854
|
this.render();
|
|
880
|
-
}
|
|
881
855
|
}
|
|
882
856
|
|
|
883
857
|
/**
|
|
@@ -900,48 +874,40 @@ export class MostPotentResidues extends SARViewer {
|
|
|
900
874
|
let i = 0;
|
|
901
875
|
for (const [position, positionStats] of monomerPositionStatsEntries) {
|
|
902
876
|
const generalPositionStats = positionStats.general;
|
|
903
|
-
if (!generalPositionStats)
|
|
877
|
+
if (!generalPositionStats)
|
|
904
878
|
continue;
|
|
905
|
-
}
|
|
906
879
|
|
|
907
880
|
|
|
908
|
-
if (Object.entries(positionStats).length === 1)
|
|
881
|
+
if (Object.entries(positionStats).length === 1)
|
|
909
882
|
continue;
|
|
910
|
-
}
|
|
911
883
|
|
|
912
884
|
|
|
913
885
|
const filteredMonomerStats: [string, StatsItem][] = [];
|
|
914
886
|
for (const [monomer, monomerStats] of Object.entries(positionStats)) {
|
|
915
|
-
if (monomer === 'general')
|
|
887
|
+
if (monomer === 'general')
|
|
916
888
|
continue;
|
|
917
|
-
}
|
|
918
889
|
|
|
919
890
|
|
|
920
|
-
if ((monomerStats as StatsItem).count > 1 && (monomerStats as StatsItem).pValue === null)
|
|
891
|
+
if ((monomerStats as StatsItem).count > 1 && (monomerStats as StatsItem).pValue === null)
|
|
921
892
|
filteredMonomerStats.push([monomer, monomerStats as StatsItem]);
|
|
922
|
-
}
|
|
923
893
|
|
|
924
894
|
|
|
925
|
-
if ((monomerStats as StatsItem).pValue === generalPositionStats.minPValue)
|
|
895
|
+
if ((monomerStats as StatsItem).pValue === generalPositionStats.minPValue)
|
|
926
896
|
filteredMonomerStats.push([monomer, monomerStats as StatsItem]);
|
|
927
|
-
}
|
|
928
897
|
}
|
|
929
898
|
|
|
930
|
-
if (filteredMonomerStats.length === 0)
|
|
899
|
+
if (filteredMonomerStats.length === 0)
|
|
931
900
|
continue;
|
|
932
|
-
}
|
|
933
901
|
|
|
934
902
|
|
|
935
903
|
let maxEntry: [string, StatsItem] | null = null;
|
|
936
904
|
for (const [monomer, monomerStats] of filteredMonomerStats) {
|
|
937
|
-
if (maxEntry === null || maxEntry[1].meanDifference < monomerStats.meanDifference)
|
|
905
|
+
if (maxEntry === null || maxEntry[1].meanDifference < monomerStats.meanDifference)
|
|
938
906
|
maxEntry = [monomer, monomerStats];
|
|
939
|
-
}
|
|
940
907
|
}
|
|
941
908
|
|
|
942
|
-
if (maxEntry === null)
|
|
909
|
+
if (maxEntry === null)
|
|
943
910
|
continue;
|
|
944
|
-
}
|
|
945
911
|
|
|
946
912
|
|
|
947
913
|
posData[i] = parseInt(position);
|
|
@@ -1010,11 +976,10 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1010
976
|
highlightMonomerPosition(monomerPosition, this.dataFrame, this.monomerPositionStats);
|
|
1011
977
|
this.model.isHighlighting = true;
|
|
1012
978
|
|
|
1013
|
-
if (gridCell.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
|
|
979
|
+
if (gridCell.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
|
|
1014
980
|
monomerPosition.positionOrClusterType = C.COLUMNS_NAMES.MONOMER;
|
|
1015
|
-
|
|
981
|
+
else if (gridCell.tableColumn?.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
1016
982
|
return false;
|
|
1017
|
-
}
|
|
1018
983
|
|
|
1019
984
|
|
|
1020
985
|
const columnEntries = this.getTotalViewerAggColumns();
|
|
@@ -1028,9 +993,8 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1028
993
|
});
|
|
1029
994
|
DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
|
|
1030
995
|
try {
|
|
1031
|
-
if ((this.keyPressed && mprDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed)
|
|
996
|
+
if ((this.keyPressed && mprDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed)
|
|
1032
997
|
return;
|
|
1033
|
-
}
|
|
1034
998
|
|
|
1035
999
|
|
|
1036
1000
|
const monomerPosition = this.getMonomerPosition(gridCell);
|
|
@@ -1043,9 +1007,8 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1043
1007
|
}
|
|
1044
1008
|
const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
|
|
1045
1009
|
?.get(monomerPosition.positionOrClusterType)?.size;
|
|
1046
|
-
if (hasMutationCliffs)
|
|
1010
|
+
if (hasMutationCliffs)
|
|
1047
1011
|
this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false});
|
|
1048
|
-
}
|
|
1049
1012
|
|
|
1050
1013
|
|
|
1051
1014
|
grid.invalidate();
|
|
@@ -1056,21 +1019,19 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1056
1019
|
});
|
|
1057
1020
|
grid.root.addEventListener('keydown', (ev) => {
|
|
1058
1021
|
this.keyPressed = ev.key.startsWith('Arrow');
|
|
1059
|
-
if (this.keyPressed)
|
|
1022
|
+
if (this.keyPressed)
|
|
1060
1023
|
return;
|
|
1061
|
-
}
|
|
1062
1024
|
|
|
1063
1025
|
|
|
1064
|
-
if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey))
|
|
1026
|
+
if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey))
|
|
1065
1027
|
this._mutationCliffsSelection = initSelection(this.positionColumns);
|
|
1066
|
-
|
|
1028
|
+
else if (ev.code === 'KeyA' && ev.ctrlKey) {
|
|
1067
1029
|
for (let rowIdx = 0; rowIdx < mprDf.rowCount; ++rowIdx) {
|
|
1068
1030
|
const monomerPosition = this.getMonomerPosition(grid.cell('Diff', rowIdx));
|
|
1069
1031
|
this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, false);
|
|
1070
1032
|
}
|
|
1071
|
-
} else
|
|
1033
|
+
} else
|
|
1072
1034
|
return;
|
|
1073
|
-
}
|
|
1074
1035
|
|
|
1075
1036
|
|
|
1076
1037
|
this.model.fireBitsetChanged(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
@@ -1079,17 +1040,15 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1079
1040
|
grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
1080
1041
|
grid.root.addEventListener('click', (ev) => {
|
|
1081
1042
|
const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
1082
|
-
if (!gridCell?.isTableCell || gridCell!.tableColumn!.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
1043
|
+
if (!gridCell?.isTableCell || gridCell!.tableColumn!.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
1083
1044
|
return;
|
|
1084
|
-
}
|
|
1085
1045
|
|
|
1086
1046
|
|
|
1087
1047
|
const monomerPosition = this.getMonomerPosition(gridCell);
|
|
1088
1048
|
const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
|
|
1089
1049
|
?.get(monomerPosition.positionOrClusterType)?.size;
|
|
1090
|
-
if (!hasMutationCliffs)
|
|
1050
|
+
if (!hasMutationCliffs)
|
|
1091
1051
|
return;
|
|
1092
|
-
}
|
|
1093
1052
|
|
|
1094
1053
|
|
|
1095
1054
|
this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
|
|
@@ -1197,18 +1156,17 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
|
|
|
1197
1156
|
const positionCol = viewer.positionColumns.find((col) => col.name === currentPosition)!;
|
|
1198
1157
|
const colorCache: { [_: string]: number } = positionCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] ?? {};
|
|
1199
1158
|
let color: number;
|
|
1200
|
-
if (colorCache[currentMonomer])
|
|
1159
|
+
if (colorCache[currentMonomer])
|
|
1201
1160
|
color = colorCache[currentMonomer];
|
|
1202
|
-
|
|
1161
|
+
else {
|
|
1203
1162
|
const positionColData = positionCol.getRawData();
|
|
1204
1163
|
const positionColCategories = positionCol.categories;
|
|
1205
1164
|
|
|
1206
1165
|
const colorColData = colorCol!.getRawData();
|
|
1207
1166
|
const colorValuesIndexes: number[] = [];
|
|
1208
1167
|
for (let i = 0; i < positionCol.length; ++i) {
|
|
1209
|
-
if (positionColCategories[positionColData[i]] === currentMonomer)
|
|
1168
|
+
if (positionColCategories[positionColData[i]] === currentMonomer)
|
|
1210
1169
|
colorValuesIndexes.push(i);
|
|
1211
|
-
}
|
|
1212
1170
|
}
|
|
1213
1171
|
const cellColorDataCol = DG.Column.float('color', colorValuesIndexes.length)
|
|
1214
1172
|
.init((i) => colorColData[colorValuesIndexes[i]]);
|
|
@@ -1220,9 +1178,8 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
|
|
|
1220
1178
|
|
|
1221
1179
|
CR.renderInvariantMapCell(canvasContext, currentMonomer, currentPosition,
|
|
1222
1180
|
(viewer as MonomerPosition).invariantMapSelection, value, bound, color);
|
|
1223
|
-
} else
|
|
1181
|
+
} else
|
|
1224
1182
|
CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, viewer, bound);
|
|
1225
|
-
}
|
|
1226
1183
|
|
|
1227
1184
|
|
|
1228
1185
|
args.preventDefault();
|
package/src/widgets/peptides.ts
CHANGED
|
@@ -96,13 +96,29 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
96
96
|
const activityColumnChoice = ui.columnInput('Activity', df, defaultActivityColumn, activityScalingMethodState,
|
|
97
97
|
{filter: (col: DG.Column) => col.type === DG.TYPE.INT || col.type === DG.TYPE.FLOAT});
|
|
98
98
|
activityColumnChoice.setTooltip('Numerical activity column');
|
|
99
|
-
const clustersColumnChoice = ui.columnInput('Clusters', df, null,
|
|
99
|
+
const clustersColumnChoice = ui.columnInput('Clusters', df, null, () => {
|
|
100
|
+
if (clustersColumnChoice.value) {
|
|
101
|
+
generateClustersInput.value = false;
|
|
102
|
+
generateClustersInput.fireChanged();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
100
105
|
clustersColumnChoice.setTooltip('Optional. Clusters column is used to create Logo Summary Table');
|
|
101
106
|
clustersColumnChoice.nullable = true;
|
|
107
|
+
// clustering input
|
|
108
|
+
const generateClustersInput = ui.boolInput('Generate clusters', true, () => {
|
|
109
|
+
if (generateClustersInput.value) {
|
|
110
|
+
clustersColumnChoice.value = null;
|
|
111
|
+
clustersColumnChoice.fireChanged();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
generateClustersInput
|
|
115
|
+
.setTooltip('Generate clusters column based on sequence space embeddings for Logo Summary Table');
|
|
102
116
|
activityColumnChoice.fireChanged();
|
|
103
117
|
activityScalingMethod.fireChanged();
|
|
118
|
+
generateClustersInput.fireChanged();
|
|
119
|
+
|
|
104
120
|
|
|
105
|
-
const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice];
|
|
121
|
+
const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, generateClustersInput];
|
|
106
122
|
if (seqColInput !== null)
|
|
107
123
|
inputsList.splice(0, 0, seqColInput);
|
|
108
124
|
|
|
@@ -114,7 +130,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
114
130
|
bitsetChanged.unsubscribe();
|
|
115
131
|
if (sequencesCol) {
|
|
116
132
|
const model = await startAnalysis(activityColumnChoice.value!, sequencesCol, clustersColumnChoice.value, df,
|
|
117
|
-
scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, {addSequenceSpace: true
|
|
133
|
+
scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, {addSequenceSpace: true,
|
|
134
|
+
useEmbeddingsClusters: generateClustersInput.value ?? false});
|
|
118
135
|
return model !== null;
|
|
119
136
|
}
|
|
120
137
|
return false;
|
|
@@ -147,7 +164,10 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
147
164
|
return {host: mainHost, callback: startAnalysisCallback};
|
|
148
165
|
}
|
|
149
166
|
|
|
150
|
-
type AnalysisOptions = {
|
|
167
|
+
type AnalysisOptions = {
|
|
168
|
+
addSequenceSpace?: boolean,
|
|
169
|
+
useEmbeddingsClusters?: boolean,
|
|
170
|
+
};
|
|
151
171
|
|
|
152
172
|
/**
|
|
153
173
|
* Creates dataframe to use in analysis, model instance and adds viewers
|
|
@@ -163,7 +183,7 @@ type AnalysisOptions = { addSequenceSpace?: boolean };
|
|
|
163
183
|
export async function startAnalysis(activityColumn: DG.Column<number>, peptidesCol: DG.Column<string>,
|
|
164
184
|
clustersColumn: DG.Column | null, sourceDf: DG.DataFrame, scaledCol: DG.Column<number>, scaling: C.SCALING_METHODS,
|
|
165
185
|
options: AnalysisOptions = {}): Promise<PeptidesModel | null> {
|
|
166
|
-
let model = null;
|
|
186
|
+
let model: PeptidesModel | null = null;
|
|
167
187
|
if (activityColumn.type !== DG.COLUMN_TYPE.FLOAT && activityColumn.type !== DG.COLUMN_TYPE.INT) {
|
|
168
188
|
grok.shell.error('The activity column must be of numeric type!');
|
|
169
189
|
return model;
|
|
@@ -187,7 +207,8 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
187
207
|
|
|
188
208
|
const settings: type.PeptidesSettings = {
|
|
189
209
|
sequenceColumnName: peptidesCol.name, activityColumnName: activityColumn.name, activityScaling: scaling,
|
|
190
|
-
columns: {}, showDendrogram: false,
|
|
210
|
+
columns: {}, showDendrogram: false,
|
|
211
|
+
sequenceSpaceParams: new type.SequenceSpaceParams(!!options.useEmbeddingsClusters && !clustersColumn),
|
|
191
212
|
};
|
|
192
213
|
|
|
193
214
|
if (clustersColumn) {
|
|
@@ -215,8 +236,20 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
215
236
|
await model.addMostPotentResidues();
|
|
216
237
|
|
|
217
238
|
// FIXME: enable by default for tests
|
|
218
|
-
if (options.addSequenceSpace ?? false)
|
|
219
|
-
model.addSequenceSpace();
|
|
239
|
+
if (options.addSequenceSpace ?? false) {
|
|
240
|
+
await model.addSequenceSpace({clusterCol: clustersColumn, clusterEmbeddings: options.useEmbeddingsClusters});
|
|
241
|
+
if (!clustersColumn && (options.useEmbeddingsClusters ?? false)) {
|
|
242
|
+
const clusterCol = model._sequenceSpaceCols
|
|
243
|
+
.find((col) => model!.df.col(col) && model!.df.col(col)?.type === DG.COLUMN_TYPE.STRING);
|
|
244
|
+
if (clusterCol) {
|
|
245
|
+
const lstProps: ILogoSummaryTable = {
|
|
246
|
+
clustersColumnName: clusterCol, sequenceColumnName: peptidesCol.name, activityScaling: scaling,
|
|
247
|
+
activityColumnName: activityColumn.name,
|
|
248
|
+
};
|
|
249
|
+
await model.addLogoSummaryTable(lstProps);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
220
253
|
|
|
221
254
|
|
|
222
255
|
progress.close();
|
package/src/widgets/settings.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
|
9
9
|
import $ from 'cash-dom';
|
|
10
10
|
import wu from 'wu';
|
|
11
11
|
import {getTreeHelperInstance} from '../package';
|
|
12
|
-
import {
|
|
12
|
+
import {MmDistanceFunctionsNames as distFNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
|
|
13
13
|
|
|
14
14
|
type PaneInputs = { [paneName: string]: DG.InputBase[] };
|
|
15
15
|
type SettingsElements = { dialog: DG.Dialog, accordion: DG.Accordion, inputs: PaneInputs };
|
|
@@ -156,7 +156,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
156
156
|
|
|
157
157
|
// Sequence space pane options
|
|
158
158
|
const modifiedSeqSpaceParams: Partial<type.SequenceSpaceParams> = {};
|
|
159
|
-
function onSeqSpaceParamsChange(fieldName: keyof type.SequenceSpaceParams, value: any) {
|
|
159
|
+
function onSeqSpaceParamsChange(fieldName: keyof type.SequenceSpaceParams, value: any): void {
|
|
160
160
|
correctSeqSpaceInputs();
|
|
161
161
|
if (value === null || value === undefined || value === '')
|
|
162
162
|
return;
|
|
@@ -174,7 +174,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
174
174
|
result.sequenceSpaceParams = {...seqSpaceParams, ...modifiedSeqSpaceParams};
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
function toggleInputs(nwInputs: DG.InputBase[], condition: boolean) {
|
|
177
|
+
function toggleInputs(nwInputs: DG.InputBase[], condition: boolean): void {
|
|
178
178
|
nwInputs.forEach((input) => {
|
|
179
179
|
if (condition)
|
|
180
180
|
input.root.style.display = 'flex';
|
|
@@ -184,29 +184,37 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
const distanceFunctionInput = ui.choiceInput(SEQUENCE_SPACE_INPUTS.DISTANCE_FUNCTION, seqSpaceParams.distanceF,
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
[distFNames.NEEDLEMANN_WUNSCH, distFNames.HAMMING, distFNames.LEVENSHTEIN, distFNames.MONOMER_CHEMICAL_DISTANCE],
|
|
188
|
+
() => onSeqSpaceParamsChange('distanceF', distanceFunctionInput.value));
|
|
189
189
|
distanceFunctionInput.setTooltip('Distance function');
|
|
190
|
-
const gapOpenInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_OPEN, seqSpaceParams.gapOpen,
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
() => onSeqSpaceParamsChange('
|
|
190
|
+
const gapOpenInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_OPEN, seqSpaceParams.gapOpen,
|
|
191
|
+
() => onSeqSpaceParamsChange('gapOpen', gapOpenInput.value));
|
|
192
|
+
const gapExtendInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_EXTEND, seqSpaceParams.gapExtend,
|
|
193
|
+
() => onSeqSpaceParamsChange('gapExtend', gapExtendInput.value));
|
|
194
|
+
const clusterEmbeddingsInput =
|
|
195
|
+
ui.boolInput(SEQUENCE_SPACE_INPUTS.CLUSTER_EMBEDDINGS, seqSpaceParams.clusterEmbeddings ?? false,
|
|
196
|
+
() => onSeqSpaceParamsChange('clusterEmbeddings', clusterEmbeddingsInput.value));
|
|
194
197
|
clusterEmbeddingsInput.setTooltip('Cluster embeddings using DBSCAN algorithm');
|
|
195
|
-
const epsilonInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.EPSILON, seqSpaceParams.epsilon,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
+
const epsilonInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.EPSILON, seqSpaceParams.epsilon,
|
|
199
|
+
() => onSeqSpaceParamsChange('epsilon', epsilonInput.value));
|
|
200
|
+
epsilonInput.setTooltip(
|
|
201
|
+
'Epsilon parameter for DBSCAN. Minimum distance between two points to be considered as a cluster');
|
|
202
|
+
const minPtsInput = ui.intInput(SEQUENCE_SPACE_INPUTS.MIN_PTS, seqSpaceParams.minPts,
|
|
203
|
+
() => onSeqSpaceParamsChange('minPts', minPtsInput.value));
|
|
198
204
|
minPtsInput.setTooltip('Minimum number of points in a cluster');
|
|
199
|
-
const fingerprintTypesInput = ui.choiceInput('Fingerprint type', seqSpaceParams.fingerprintType,
|
|
200
|
-
() => onSeqSpaceParamsChange('fingerprintType', fingerprintTypesInput.value));
|
|
201
|
-
function correctSeqSpaceInputs() {
|
|
205
|
+
const fingerprintTypesInput = ui.choiceInput('Fingerprint type', seqSpaceParams.fingerprintType,
|
|
206
|
+
['Morgan', 'RDKit', 'Pattern'], () => onSeqSpaceParamsChange('fingerprintType', fingerprintTypesInput.value));
|
|
207
|
+
function correctSeqSpaceInputs(): void {
|
|
202
208
|
toggleInputs([gapOpenInput, gapExtendInput], distanceFunctionInput.value === distFNames.NEEDLEMANN_WUNSCH);
|
|
203
209
|
toggleInputs([epsilonInput, minPtsInput], clusterEmbeddingsInput.value === true);
|
|
204
210
|
toggleInputs([fingerprintTypesInput],
|
|
205
|
-
distanceFunctionInput.value === distFNames.MONOMER_CHEMICAL_DISTANCE ||
|
|
211
|
+
distanceFunctionInput.value === distFNames.MONOMER_CHEMICAL_DISTANCE ||
|
|
212
|
+
distanceFunctionInput.value === distFNames.NEEDLEMANN_WUNSCH);
|
|
206
213
|
}
|
|
207
214
|
correctSeqSpaceInputs();
|
|
208
215
|
|
|
209
|
-
const seqSpaceInputs = [distanceFunctionInput, fingerprintTypesInput, gapOpenInput,
|
|
216
|
+
const seqSpaceInputs = [distanceFunctionInput, fingerprintTypesInput, gapOpenInput,
|
|
217
|
+
gapExtendInput, clusterEmbeddingsInput, epsilonInput, minPtsInput];
|
|
210
218
|
accordion.addPane(SETTINGS_PANES.SEQUENCE_SPACE, () => ui.inputs(seqSpaceInputs), true);
|
|
211
219
|
inputs[SETTINGS_PANES.SEQUENCE_SPACE] = seqSpaceInputs;
|
|
212
220
|
const dialog = ui.dialog('Peptides settings').add(accordion);
|