@datagrok/peptides 1.7.2 → 1.8.0
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/README.md +3 -3
- package/dist/563.js +2 -0
- package/dist/611.js +2 -0
- package/dist/802.js +2 -0
- package/dist/96.js +2 -0
- package/dist/package-test.js +2 -29778
- package/dist/package.js +2 -28285
- package/files/icons/logo-summary-viewer.svg +13 -0
- package/files/icons/peptide-sar-vertical-viewer.svg +13 -0
- package/files/icons/peptide-sar-viewer.svg +19 -0
- package/files/icons/peptide-space-viewer.svg +40 -0
- package/package.json +5 -6
- package/src/model.ts +262 -196
- package/src/package.ts +44 -29
- package/src/tests/peptide-space-test.ts +1 -2
- package/src/tests/viewers.ts +0 -2
- package/src/utils/cell-renderer.ts +1 -1
- package/src/utils/distance-matrix.worker.ts +16 -0
- package/src/utils/peptide-similarity-space.ts +0 -1
- package/src/utils/statistics.ts +9 -0
- package/src/utils/types.ts +5 -1
- package/src/utils/worker-creator.ts +11 -0
- package/src/viewers/logo-summary.ts +232 -133
- package/src/viewers/peptide-space-viewer.ts +6 -6
- package/src/viewers/sar-viewer.ts +3 -3
- package/src/widgets/distribution.ts +14 -17
- package/src/widgets/peptides.ts +4 -1
- package/src/widgets/settings.ts +30 -4
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +0 -9077
package/src/model.ts
CHANGED
|
@@ -14,9 +14,9 @@ import * as CR from './utils/cell-renderer';
|
|
|
14
14
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
15
15
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
16
16
|
import {getStats, Stats} from './utils/statistics';
|
|
17
|
-
import {
|
|
17
|
+
import {LogoSummaryTable} from './viewers/logo-summary';
|
|
18
18
|
import {getSettingsDialog} from './widgets/settings';
|
|
19
|
-
import {
|
|
19
|
+
import {_package, getMonomerWorksInstance, getTreeHelperInstance} from './package';
|
|
20
20
|
import {findMutations} from './utils/algorithms';
|
|
21
21
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
22
22
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
@@ -24,6 +24,12 @@ import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
|
24
24
|
import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
25
25
|
import {pickUpPalette, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
26
26
|
|
|
27
|
+
import {DistanceMatrix} from '@datagrok-libraries/bio/src/trees/distance-matrix';
|
|
28
|
+
import {StringMetricsNames} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
29
|
+
import {ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
|
|
30
|
+
import {TAGS as treeTAGS} from '@datagrok-libraries/bio/src/trees';
|
|
31
|
+
import {createDistanceMatrixWorker} from './utils/worker-creator';
|
|
32
|
+
|
|
27
33
|
export type SummaryStats = {
|
|
28
34
|
minCount: number, maxCount: number,
|
|
29
35
|
minMeanDifference: number, maxMeanDifference: number,
|
|
@@ -32,25 +38,38 @@ export type SummaryStats = {
|
|
|
32
38
|
};
|
|
33
39
|
export type PositionStats = { [monomer: string]: Stats } & { general: SummaryStats };
|
|
34
40
|
export type MonomerPositionStats = { [position: string]: PositionStats } & { general: SummaryStats };
|
|
41
|
+
export type ClusterStats = {[cluster: string]: Stats};
|
|
42
|
+
export enum CLUSTER_TYPE {
|
|
43
|
+
ORIGINAL = 'original',
|
|
44
|
+
CUSTOM = 'custom',
|
|
45
|
+
};
|
|
46
|
+
export type ClusterType = `${CLUSTER_TYPE}`;
|
|
47
|
+
export type ClusterTypeStats = {[clusterType in ClusterType]: ClusterStats};
|
|
48
|
+
export enum VIEWER_TYPE {
|
|
49
|
+
MONOMER_POSITION = 'Monomer-Position',
|
|
50
|
+
MOST_POTENT_RESIDUES = 'Most Potent Residues',
|
|
51
|
+
LOGO_SUMMARY_TABLE = 'Logo Summary Table',
|
|
52
|
+
DENDROGRAM = 'Dendrogram',
|
|
53
|
+
};
|
|
35
54
|
|
|
36
55
|
export class PeptidesModel {
|
|
37
56
|
static modelName = 'peptidesModel';
|
|
38
57
|
|
|
39
58
|
settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
|
|
40
59
|
_mutatinCliffsSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
41
|
-
_newClusterSubject: rxjs.Subject<undefined>
|
|
42
|
-
_removeClusterSubject: rxjs.Subject<undefined>
|
|
43
|
-
_filterChangedSubject: rxjs.Subject<undefined>
|
|
60
|
+
_newClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
61
|
+
_removeClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
62
|
+
_filterChangedSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
44
63
|
|
|
45
64
|
_isUpdating: boolean = false;
|
|
46
65
|
isBitsetChangedInitialized = false;
|
|
47
66
|
isCellChanging = false;
|
|
67
|
+
isUserChangedSelection = true;
|
|
48
68
|
|
|
49
69
|
df: DG.DataFrame;
|
|
50
70
|
splitCol!: DG.Column<boolean>;
|
|
51
|
-
edf: DG.DataFrame | null = null;
|
|
52
71
|
_monomerPositionStats?: MonomerPositionStats;
|
|
53
|
-
_clusterStats?:
|
|
72
|
+
_clusterStats?: ClusterTypeStats;
|
|
54
73
|
_mutationCliffsSelection!: type.PositionToAARList;
|
|
55
74
|
_invariantMapSelection!: type.PositionToAARList;
|
|
56
75
|
_logoSummarySelection!: string[];
|
|
@@ -58,9 +77,6 @@ export class PeptidesModel {
|
|
|
58
77
|
isInitialized = false;
|
|
59
78
|
_analysisView?: DG.TableView;
|
|
60
79
|
|
|
61
|
-
isPeptideSpaceChangingBitset = false;
|
|
62
|
-
isChangingEdfBitset = false;
|
|
63
|
-
|
|
64
80
|
monomerMap: { [key: string]: { molfile: string, fullName: string } } = {};
|
|
65
81
|
monomerLib: IMonomerLib | null = null; // To get monomers from lib(s)
|
|
66
82
|
monomerWorks: MonomerWorks | null = null; // To get processed monomers
|
|
@@ -79,6 +95,9 @@ export class PeptidesModel {
|
|
|
79
95
|
_mostPotentResiduesDf?: DG.DataFrame;
|
|
80
96
|
_matrixDf?: DG.DataFrame;
|
|
81
97
|
_splitSeqDf?: DG.DataFrame;
|
|
98
|
+
_distanceMatrix!: DistanceMatrix;
|
|
99
|
+
_treeHelper!: ITreeHelper;
|
|
100
|
+
_dm!: DistanceMatrix;
|
|
82
101
|
|
|
83
102
|
private constructor(dataFrame: DG.DataFrame) {
|
|
84
103
|
this.df = dataFrame;
|
|
@@ -91,6 +110,11 @@ export class PeptidesModel {
|
|
|
91
110
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
92
111
|
}
|
|
93
112
|
|
|
113
|
+
get treeHelper(): ITreeHelper {
|
|
114
|
+
this._treeHelper ??= getTreeHelperInstance();
|
|
115
|
+
return this._treeHelper;
|
|
116
|
+
}
|
|
117
|
+
|
|
94
118
|
get monomerPositionDf(): DG.DataFrame {
|
|
95
119
|
this._monomerPositionDf ??= this.createMonomerPositionDf();
|
|
96
120
|
return this._monomerPositionDf;
|
|
@@ -156,12 +180,12 @@ export class PeptidesModel {
|
|
|
156
180
|
this._substitutionsInfo = si;
|
|
157
181
|
}
|
|
158
182
|
|
|
159
|
-
get clusterStats():
|
|
183
|
+
get clusterStats(): ClusterTypeStats {
|
|
160
184
|
this._clusterStats ??= this.calculateClusterStatistics();
|
|
161
185
|
return this._clusterStats;
|
|
162
186
|
}
|
|
163
187
|
|
|
164
|
-
set clusterStats(clusterStats:
|
|
188
|
+
set clusterStats(clusterStats: ClusterTypeStats) {
|
|
165
189
|
this._clusterStats = clusterStats;
|
|
166
190
|
}
|
|
167
191
|
|
|
@@ -226,7 +250,7 @@ export class PeptidesModel {
|
|
|
226
250
|
this._invariantMapSelection = selection;
|
|
227
251
|
this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
|
|
228
252
|
this.isInvariantMapTrigger = true;
|
|
229
|
-
this.fireBitsetChanged(
|
|
253
|
+
this.fireBitsetChanged(true);
|
|
230
254
|
this.isInvariantMapTrigger = false;
|
|
231
255
|
this.analysisView.grid.invalidate();
|
|
232
256
|
}
|
|
@@ -305,17 +329,28 @@ export class PeptidesModel {
|
|
|
305
329
|
updateVars.add('mutationCliffs');
|
|
306
330
|
updateVars.add('stats');
|
|
307
331
|
break;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
332
|
+
// case 'columns':
|
|
333
|
+
// updateVars.add('grid');
|
|
334
|
+
// break;
|
|
311
335
|
case 'maxMutations':
|
|
312
336
|
case 'minActivityDelta':
|
|
313
337
|
updateVars.add('mutationCliffs');
|
|
314
338
|
break;
|
|
339
|
+
case 'showDendrogram':
|
|
340
|
+
updateVars.add('dendrogram');
|
|
341
|
+
break;
|
|
342
|
+
case 'showLogoSummaryTable':
|
|
343
|
+
updateVars.add('logoSummaryTable');
|
|
344
|
+
break;
|
|
345
|
+
case 'showMonomerPosition':
|
|
346
|
+
updateVars.add('monomerPosition');
|
|
347
|
+
break;
|
|
348
|
+
case 'showMostPotentResidues':
|
|
349
|
+
updateVars.add('mostPotentResidues');
|
|
350
|
+
break;
|
|
315
351
|
}
|
|
316
352
|
}
|
|
317
353
|
this.df.setTag('settings', JSON.stringify(this._settings));
|
|
318
|
-
// this.updateDefault();
|
|
319
354
|
for (const variable of updateVars) {
|
|
320
355
|
switch (variable) {
|
|
321
356
|
case 'activity':
|
|
@@ -324,8 +359,8 @@ export class PeptidesModel {
|
|
|
324
359
|
case 'mutationCliffs':
|
|
325
360
|
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
326
361
|
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
327
|
-
const
|
|
328
|
-
this.substitutionsInfo = findMutations(scaledActivityCol.getRawData(),
|
|
362
|
+
const monomerCols: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
363
|
+
this.substitutionsInfo = findMutations(scaledActivityCol.getRawData(), monomerCols, this.settings);
|
|
329
364
|
break;
|
|
330
365
|
case 'stats':
|
|
331
366
|
this.monomerPositionStats = this.calculateMonomerPositionStatistics();
|
|
@@ -336,6 +371,21 @@ export class PeptidesModel {
|
|
|
336
371
|
case 'grid':
|
|
337
372
|
this.updateGrid();
|
|
338
373
|
break;
|
|
374
|
+
case 'dendrogram':
|
|
375
|
+
this.settings.showDendrogram ? this.addDendrogram() : this.closeViewer(VIEWER_TYPE.DENDROGRAM);
|
|
376
|
+
break;
|
|
377
|
+
case 'logoSummaryTable':
|
|
378
|
+
this.settings.showLogoSummaryTable ? this.addLogoSummaryTable() :
|
|
379
|
+
this.closeViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
380
|
+
break;
|
|
381
|
+
case 'monomerPosition':
|
|
382
|
+
this.settings.showMonomerPosition ? this.addMonomerPosition() :
|
|
383
|
+
this.closeViewer(VIEWER_TYPE.MONOMER_POSITION);
|
|
384
|
+
break;
|
|
385
|
+
case 'mostPotentResidues':
|
|
386
|
+
this.settings.showMostPotentResidues ? this.addMostPotentResidues() :
|
|
387
|
+
this.closeViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
388
|
+
break;
|
|
339
389
|
}
|
|
340
390
|
}
|
|
341
391
|
|
|
@@ -347,9 +397,6 @@ export class PeptidesModel {
|
|
|
347
397
|
const positions = this.splitSeqDf.columns.names();
|
|
348
398
|
const matrixDf = this.matrixDf
|
|
349
399
|
.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
350
|
-
// .pivot(C.COLUMNS_NAMES.POSITION)
|
|
351
|
-
// .add('values')
|
|
352
|
-
// .add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
353
400
|
.aggregate();
|
|
354
401
|
for (const pos of positions)
|
|
355
402
|
matrixDf.columns.addNewString(pos);
|
|
@@ -400,7 +447,7 @@ export class PeptidesModel {
|
|
|
400
447
|
acc.addTitle(ui.h1(`${filterAndSelectionBs.trueCount} selected rows${filteredTitlePart}`));
|
|
401
448
|
if (filterAndSelectionBs.anyTrue) {
|
|
402
449
|
acc.addPane('Actions', () => {
|
|
403
|
-
const newViewButton = ui.button('New view',
|
|
450
|
+
const newViewButton = ui.button('New view', () => trueModel.createNewView(),
|
|
404
451
|
'Creates a new view from current selection');
|
|
405
452
|
const newCluster = ui.button('New cluster', () => trueModel._newClusterSubject.next(),
|
|
406
453
|
'Creates a new cluster from selection');
|
|
@@ -423,7 +470,8 @@ export class PeptidesModel {
|
|
|
423
470
|
|
|
424
471
|
this.createScaledCol();
|
|
425
472
|
|
|
426
|
-
this.
|
|
473
|
+
this.initInvariantMapSelection();
|
|
474
|
+
this.initMutationCliffsSelection();
|
|
427
475
|
|
|
428
476
|
this.setWebLogoInteraction();
|
|
429
477
|
this.webLogoBounds = {};
|
|
@@ -437,15 +485,25 @@ export class PeptidesModel {
|
|
|
437
485
|
this.postProcessGrids();
|
|
438
486
|
}
|
|
439
487
|
|
|
440
|
-
|
|
488
|
+
initInvariantMapSelection(cleanInit = false): void {
|
|
441
489
|
const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
|
|
442
|
-
const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
443
490
|
const positionColumns = this.splitSeqDf.columns.names();
|
|
444
491
|
for (const pos of positionColumns) {
|
|
445
|
-
tempInvariantMapSelection
|
|
446
|
-
|
|
492
|
+
if (cleanInit || !tempInvariantMapSelection.hasOwnProperty(pos))
|
|
493
|
+
tempInvariantMapSelection[pos] = [];
|
|
447
494
|
}
|
|
495
|
+
|
|
448
496
|
this.invariantMapSelection = tempInvariantMapSelection;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
initMutationCliffsSelection(cleanInit = false): void {
|
|
500
|
+
const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
501
|
+
const positionColumns = this.splitSeqDf.columns.names();
|
|
502
|
+
for (const pos of positionColumns) {
|
|
503
|
+
if (cleanInit || !mutationCliffsSelection.hasOwnProperty(pos))
|
|
504
|
+
mutationCliffsSelection[pos] = [];
|
|
505
|
+
}
|
|
506
|
+
|
|
449
507
|
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
450
508
|
}
|
|
451
509
|
|
|
@@ -538,93 +596,96 @@ export class PeptidesModel {
|
|
|
538
596
|
return monomerPositionObject;
|
|
539
597
|
}
|
|
540
598
|
|
|
541
|
-
getSummaryStats(
|
|
599
|
+
getSummaryStats(genObj: SummaryStats, stats: Stats | null = null, summaryStats: SummaryStats | null = null): void {
|
|
542
600
|
if (stats == null && summaryStats == null)
|
|
543
601
|
throw new Error(`MonomerPositionStatsError: either stats or summaryStats must be present`);
|
|
544
602
|
|
|
545
603
|
const possibleMaxCount = stats?.count ?? summaryStats!.maxCount;
|
|
546
|
-
|
|
547
|
-
if (
|
|
548
|
-
|
|
604
|
+
genObj.maxCount ??= possibleMaxCount;
|
|
605
|
+
if (genObj.maxCount < possibleMaxCount)
|
|
606
|
+
genObj.maxCount = possibleMaxCount;
|
|
549
607
|
|
|
550
608
|
const possibleMinCount = stats?.count ?? summaryStats!.minCount;
|
|
551
|
-
|
|
552
|
-
if (
|
|
553
|
-
|
|
609
|
+
genObj.minCount ??= possibleMinCount;
|
|
610
|
+
if (genObj.minCount > possibleMinCount)
|
|
611
|
+
genObj.minCount = possibleMinCount;
|
|
554
612
|
|
|
555
613
|
const possibleMaxMeanDifference = stats?.meanDifference ?? summaryStats!.maxMeanDifference;
|
|
556
|
-
|
|
557
|
-
if (
|
|
558
|
-
|
|
614
|
+
genObj.maxMeanDifference ??= possibleMaxMeanDifference;
|
|
615
|
+
if (genObj.maxMeanDifference < possibleMaxMeanDifference)
|
|
616
|
+
genObj.maxMeanDifference = possibleMaxMeanDifference;
|
|
559
617
|
|
|
560
618
|
const possibleMinMeanDifference = stats?.meanDifference ?? summaryStats!.minMeanDifference;
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
|
|
619
|
+
genObj.minMeanDifference ??= possibleMinMeanDifference;
|
|
620
|
+
if (genObj.minMeanDifference > possibleMinMeanDifference)
|
|
621
|
+
genObj.minMeanDifference = possibleMinMeanDifference;
|
|
564
622
|
|
|
565
623
|
const possibleMaxPValue = stats?.pValue ?? summaryStats!.maxPValue;
|
|
566
|
-
|
|
567
|
-
if (
|
|
568
|
-
|
|
624
|
+
genObj.maxPValue ??= possibleMaxPValue;
|
|
625
|
+
if (genObj.maxPValue < possibleMaxPValue)
|
|
626
|
+
genObj.maxPValue = possibleMaxPValue;
|
|
569
627
|
|
|
570
628
|
const possibleMinPValue = stats?.pValue ?? summaryStats!.minPValue;
|
|
571
|
-
|
|
572
|
-
if (
|
|
573
|
-
|
|
629
|
+
genObj.minPValue ??= possibleMinPValue;
|
|
630
|
+
if (genObj.minPValue > possibleMinPValue)
|
|
631
|
+
genObj.minPValue = possibleMinPValue;
|
|
574
632
|
|
|
575
633
|
const possibleMaxRatio = stats?.ratio ?? summaryStats!.maxRatio;
|
|
576
|
-
|
|
577
|
-
if (
|
|
578
|
-
|
|
634
|
+
genObj.maxRatio ??= possibleMaxRatio;
|
|
635
|
+
if (genObj.maxRatio < possibleMaxRatio)
|
|
636
|
+
genObj.maxRatio = possibleMaxRatio;
|
|
579
637
|
|
|
580
638
|
const possibleMinRatio = stats?.ratio ?? summaryStats!.minRatio;
|
|
581
|
-
|
|
582
|
-
if (
|
|
583
|
-
|
|
639
|
+
genObj.minRatio ??= possibleMinRatio;
|
|
640
|
+
if (genObj.minRatio > possibleMinRatio)
|
|
641
|
+
genObj.minRatio = possibleMinRatio;
|
|
584
642
|
}
|
|
585
643
|
|
|
586
|
-
calculateClusterStatistics():
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
644
|
+
calculateClusterStatistics(): ClusterTypeStats {
|
|
645
|
+
const origClustCol = this.df.getCol(this.settings.clustersColumnName!);
|
|
646
|
+
const origClustColData = origClustCol.getRawData();
|
|
647
|
+
const origClustColCat = origClustCol.categories;
|
|
590
648
|
|
|
591
|
-
const
|
|
649
|
+
const customClustColList = wu(this.customClusters).toArray();
|
|
650
|
+
const customClustColDataList = customClustColList.map((v) => v.toList() as boolean[]);
|
|
651
|
+
const customClustColNamesList = customClustColList.map((v) => v.name);
|
|
592
652
|
|
|
593
|
-
const
|
|
594
|
-
const activityColLen = activityColData.length;
|
|
595
|
-
|
|
596
|
-
// const resultStats: Stats[] = new Array(originalClustersColCategories.length + customClustersColumnsList.length);
|
|
597
|
-
const resultStats: {[cluster: string]: Stats} = {};
|
|
598
|
-
|
|
599
|
-
const clusterCount = originalClustersColCategories.length + customClustersColumnsList.length;
|
|
600
|
-
for (let clusterIdx = 0; clusterIdx < clusterCount; ++clusterIdx) {
|
|
601
|
-
const customClusterIdx = clusterIdx - originalClustersColCategories.length;
|
|
602
|
-
const customClusterColData = customClustersColumnsList[customClusterIdx]?.toList();
|
|
603
|
-
const isAcitvityIdxValid = customClusterIdx < 0 ?
|
|
604
|
-
(i: number) => clusterIdx == originalClustersColData[i] :
|
|
605
|
-
(i: number) => customClusterColData[i];
|
|
606
|
-
|
|
607
|
-
const mask: boolean[] = new Array(activityColLen);
|
|
608
|
-
let trueCount = 0;
|
|
609
|
-
for (let maskIdx = 0; maskIdx < activityColLen; ++maskIdx) {
|
|
610
|
-
mask[maskIdx] = isAcitvityIdxValid(maskIdx);
|
|
611
|
-
|
|
612
|
-
if (mask[maskIdx])
|
|
613
|
-
++trueCount;
|
|
614
|
-
}
|
|
653
|
+
const rowCount = this.df.rowCount;
|
|
615
654
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
};
|
|
655
|
+
const origClustMasks: boolean[][] = Array.from({length: origClustColCat.length},
|
|
656
|
+
() => new Array(rowCount) as boolean[]);
|
|
657
|
+
const customClustMasks: boolean[][] = Array.from({length: customClustColList.length},
|
|
658
|
+
() => new Array(rowCount) as boolean[]);
|
|
621
659
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
660
|
+
// get original cluster masks in one pass
|
|
661
|
+
// complexity is O(N * (M + 1)) where N is the number of rows and M is the number of custom clusters
|
|
662
|
+
for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx) {
|
|
663
|
+
origClustMasks[origClustColData[rowIdx]][rowIdx] = true;
|
|
664
|
+
for (let customClustIdx = 0; customClustIdx < customClustColList.length; ++customClustIdx)
|
|
665
|
+
customClustMasks[customClustIdx][rowIdx] = customClustColDataList[customClustIdx][rowIdx];
|
|
626
666
|
}
|
|
627
667
|
|
|
668
|
+
const activityColData: type.RawData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
669
|
+
|
|
670
|
+
const origClustStats: ClusterStats = {};
|
|
671
|
+
const customClustStats: ClusterStats = {};
|
|
672
|
+
|
|
673
|
+
for (let clustType = 0; clustType < 2; ++clustType) {
|
|
674
|
+
const masks = clustType == 0 ? origClustMasks : customClustMasks;
|
|
675
|
+
const clustNames = clustType == 0 ? origClustColCat : customClustColNamesList;
|
|
676
|
+
const resultStats = clustType == 0 ? origClustStats : customClustStats;
|
|
677
|
+
for (let maskIdx = 0; maskIdx < masks.length; ++maskIdx) {
|
|
678
|
+
const mask = masks[maskIdx];
|
|
679
|
+
const trueCount = mask.filter((v) => v).length;
|
|
680
|
+
const maskInfo = {trueCount: trueCount, falseCount: rowCount - trueCount, mask: mask};
|
|
681
|
+
const stats = getStats(activityColData, maskInfo);
|
|
682
|
+
resultStats[clustNames[maskIdx]] = stats;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const resultStats = {} as ClusterTypeStats;
|
|
687
|
+
resultStats[CLUSTER_TYPE.ORIGINAL] = origClustStats;
|
|
688
|
+
resultStats[CLUSTER_TYPE.CUSTOM] = customClustStats;
|
|
628
689
|
return resultStats;
|
|
629
690
|
}
|
|
630
691
|
|
|
@@ -760,12 +821,13 @@ export class PeptidesModel {
|
|
|
760
821
|
return 0;
|
|
761
822
|
}).filter((v) => v != 'general');
|
|
762
823
|
|
|
763
|
-
this.webLogoBounds[col.name] =
|
|
764
|
-
|
|
824
|
+
this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats, sortedStatsOrder, this.df.rowCount,
|
|
825
|
+
this.cp, this.headerSelectedMonomers[col.name]);
|
|
765
826
|
gcArgs.preventDefault();
|
|
766
827
|
}
|
|
767
828
|
} catch (e) {
|
|
768
|
-
console.warn(`PeptidesHeaderLogoError: couldn't render WebLogo for column \`${col!.name}\`.
|
|
829
|
+
console.warn(`PeptidesHeaderLogoError: couldn't render WebLogo for column \`${col!.name}\`. ` +
|
|
830
|
+
`See original error below.`);
|
|
769
831
|
console.warn(e);
|
|
770
832
|
} finally {
|
|
771
833
|
ctx.restore();
|
|
@@ -787,7 +849,7 @@ export class PeptidesModel {
|
|
|
787
849
|
const tooltipElements: HTMLDivElement[] = [];
|
|
788
850
|
const monomerName = aar.toLowerCase();
|
|
789
851
|
|
|
790
|
-
const mw =
|
|
852
|
+
const mw = getMonomerWorksInstance();
|
|
791
853
|
const mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
|
|
792
854
|
|
|
793
855
|
if (mol) {
|
|
@@ -830,67 +892,13 @@ export class PeptidesModel {
|
|
|
830
892
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
831
893
|
const das = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
|
|
832
894
|
const resultMap: { [key: string]: any } = {...das.tableMap, ...colResults};
|
|
833
|
-
const distroStatsElem = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
|
|
895
|
+
const distroStatsElem = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap, true);
|
|
834
896
|
|
|
835
897
|
ui.tooltip.show(distroStatsElem, x, y);
|
|
836
898
|
|
|
837
899
|
return distroStatsElem;
|
|
838
900
|
}
|
|
839
901
|
|
|
840
|
-
showTooltipCluster(cluster: number, x: number, y: number, clusterName: string): HTMLDivElement | null {
|
|
841
|
-
const bs = this.df.filter;
|
|
842
|
-
const filteredDf = bs.anyFalse ? this.df.clone(bs) : this.df;
|
|
843
|
-
|
|
844
|
-
const activityCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
845
|
-
const activityColData = activityCol.getRawData();
|
|
846
|
-
//TODO: use bitset instead of splitCol
|
|
847
|
-
const clusterCol = filteredDf.getCol(this.settings.clustersColumnName!);
|
|
848
|
-
const clusterColData = clusterCol.getRawData();
|
|
849
|
-
let splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
850
|
-
const indexes: number[] = [];
|
|
851
|
-
splitCol.init((i) => {
|
|
852
|
-
const result = clusterColData[i] == cluster;
|
|
853
|
-
if (result)
|
|
854
|
-
indexes.push(i);
|
|
855
|
-
return result;
|
|
856
|
-
});
|
|
857
|
-
if (splitCol.max == 0)
|
|
858
|
-
splitCol = filteredDf.getCol(clusterName);
|
|
859
|
-
const distDf = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
860
|
-
|
|
861
|
-
let stats: Stats;
|
|
862
|
-
if (bs.anyFalse) {
|
|
863
|
-
const trueCount = splitCol.stats.sum;
|
|
864
|
-
const maskInfo = {
|
|
865
|
-
trueCount: trueCount,
|
|
866
|
-
falseCount: activityColData.length - trueCount,
|
|
867
|
-
mask: splitCol.toList() as boolean[],
|
|
868
|
-
};
|
|
869
|
-
stats = getStats(activityColData, maskInfo);
|
|
870
|
-
} else
|
|
871
|
-
stats = this.clusterStats[clusterName];
|
|
872
|
-
|
|
873
|
-
if (!stats.count)
|
|
874
|
-
return null;
|
|
875
|
-
|
|
876
|
-
const colResults: {[colName: string]: number} = {};
|
|
877
|
-
for (const [col, agg] of Object.entries(this.settings.columns || {})) {
|
|
878
|
-
const currentCol = filteredDf.getCol(col);
|
|
879
|
-
const currentColData = currentCol.getRawData();
|
|
880
|
-
const tempCol = DG.Column.float('', indexes.length);
|
|
881
|
-
tempCol.init((i) => currentColData[indexes[i]]);
|
|
882
|
-
colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
const das = getDistributionAndStats(distDf, stats, `Cluster: ${clusterName}`, 'Other', true, splitCol.name);
|
|
886
|
-
const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
|
|
887
|
-
const tooltip = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap, true);
|
|
888
|
-
|
|
889
|
-
ui.tooltip.show(tooltip, x, y);
|
|
890
|
-
|
|
891
|
-
return tooltip;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
902
|
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
895
903
|
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
896
904
|
const tempSelectionAt = tempSelection[position];
|
|
@@ -925,44 +933,48 @@ export class PeptidesModel {
|
|
|
925
933
|
const filter = this.df.filter;
|
|
926
934
|
const clusterCol = this.df.col(this.settings.clustersColumnName!);
|
|
927
935
|
|
|
928
|
-
const changeSelectionBitset = (currentBitset: DG.BitSet
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return;
|
|
936
|
-
|
|
937
|
-
currentBitset.init((i) => edfSelection.get(i) || false, false);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
936
|
+
const changeSelectionBitset = (currentBitset: DG.BitSet, posList: type.RawColumn[], clustColCat: string[],
|
|
937
|
+
clustColData: type.RawData, customClust: {[key: string]: boolean[]}): void => {
|
|
938
|
+
const getBitAt = (i: number): boolean => {
|
|
939
|
+
for (const posRawCol of posList) {
|
|
940
|
+
if (this.mutationCliffsSelection[posRawCol.name].includes(posRawCol.cat![posRawCol.rawData[i]]))
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
940
943
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
this.isChangingEdfBitset = false;
|
|
945
|
-
};
|
|
944
|
+
const currentOrigClust = clustColCat[clustColData[i]];
|
|
945
|
+
if (typeof currentOrigClust === undefined)
|
|
946
|
+
return false;
|
|
946
947
|
|
|
947
|
-
|
|
948
|
+
for (const clust of this.logoSummarySelection) {
|
|
949
|
+
if (clust === currentOrigClust)
|
|
950
|
+
return true;
|
|
948
951
|
|
|
949
|
-
|
|
950
|
-
const getBitAt = (i: number): boolean => {
|
|
951
|
-
for (const position of positionList) {
|
|
952
|
-
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
953
|
-
if (this.mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
952
|
+
if (Object.hasOwn(customClust, clust) && customClust[clust][i] === true)
|
|
954
953
|
return true;
|
|
955
954
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
this.logoSummarySelection.some((cluster) => this.df.columns.contains(cluster) && this.df.get(cluster, i));
|
|
955
|
+
|
|
956
|
+
return false;
|
|
959
957
|
};
|
|
960
958
|
currentBitset.init((i) => getBitAt(i), false);
|
|
961
|
-
|
|
962
|
-
updateEdfSelection();
|
|
963
959
|
};
|
|
964
960
|
|
|
965
|
-
selection.onChanged.subscribe(() =>
|
|
961
|
+
selection.onChanged.subscribe(() => {
|
|
962
|
+
if (this.isUserChangedSelection)
|
|
963
|
+
return;
|
|
964
|
+
|
|
965
|
+
const positionList: type.RawColumn[] = Object.keys(this.mutationCliffsSelection).map((pos) => {
|
|
966
|
+
const posCol = this.df.getCol(pos);
|
|
967
|
+
return {name: pos, cat: posCol.categories, rawData: posCol.getRawData()};
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
const clustColCat = clusterCol?.categories ?? [];
|
|
971
|
+
const clustColData = clusterCol?.getRawData() ?? new Int32Array(0);
|
|
972
|
+
const customClust: {[key: string]: boolean[]} = {};
|
|
973
|
+
for (const clust of this.customClusters)
|
|
974
|
+
customClust[clust.name] = clust.toList();
|
|
975
|
+
|
|
976
|
+
changeSelectionBitset(selection, positionList, clustColCat, clustColData, customClust);
|
|
977
|
+
});
|
|
966
978
|
|
|
967
979
|
filter.onChanged.subscribe(() => {
|
|
968
980
|
const positionList = Object.keys(this.invariantMapSelection);
|
|
@@ -988,8 +1000,8 @@ export class PeptidesModel {
|
|
|
988
1000
|
this.isBitsetChangedInitialized = true;
|
|
989
1001
|
}
|
|
990
1002
|
|
|
991
|
-
fireBitsetChanged(
|
|
992
|
-
this.
|
|
1003
|
+
fireBitsetChanged(fireFilterChanged: boolean = false): void {
|
|
1004
|
+
this.isUserChangedSelection = false;
|
|
993
1005
|
this.df.selection.fireChanged();
|
|
994
1006
|
if (fireFilterChanged)
|
|
995
1007
|
this.df.filter.fireChanged();
|
|
@@ -1002,8 +1014,7 @@ export class PeptidesModel {
|
|
|
1002
1014
|
for (const pane of acc.panes)
|
|
1003
1015
|
pane.expanded = true;
|
|
1004
1016
|
}
|
|
1005
|
-
|
|
1006
|
-
this.isPeptideSpaceChangingBitset = false;
|
|
1017
|
+
this.isUserChangedSelection = true;
|
|
1007
1018
|
}
|
|
1008
1019
|
|
|
1009
1020
|
postProcessGrids(): void {
|
|
@@ -1027,6 +1038,41 @@ export class PeptidesModel {
|
|
|
1027
1038
|
}
|
|
1028
1039
|
}
|
|
1029
1040
|
|
|
1041
|
+
closeViewer(viewerType: VIEWER_TYPE): void {
|
|
1042
|
+
const viewer = this.findViewer(viewerType);
|
|
1043
|
+
viewer?.detach();
|
|
1044
|
+
viewer?.close();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
findViewerNode(viewerType: VIEWER_TYPE): DG.DockNode | null {
|
|
1048
|
+
for (const node of this.analysisView.dockManager.rootNode.children) {
|
|
1049
|
+
if (node.container.containerElement.innerHTML.includes(viewerType))
|
|
1050
|
+
return node;
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
async addDendrogram(): Promise<void> {
|
|
1056
|
+
const pi = DG.TaskBarProgressIndicator.create('Calculating distance matrix...');
|
|
1057
|
+
try {
|
|
1058
|
+
const pepColValues: string[] = this.df.getCol(this.settings.sequenceColumnName!).toList();
|
|
1059
|
+
this._dm ??= new DistanceMatrix(await createDistanceMatrixWorker(pepColValues, StringMetricsNames.Levenshtein));
|
|
1060
|
+
const leafCol = this.df.col('~leaf-id') ?? this.df.columns.addNewString('~leaf-id').init((i) => i.toString());
|
|
1061
|
+
const treeNode = await this.treeHelper.hierarchicalClusteringByDistance(this._dm, 'ward');
|
|
1062
|
+
|
|
1063
|
+
this.df.setTag(treeTAGS.NEWICK, this.treeHelper.toNewick(treeNode));
|
|
1064
|
+
const leafOrdering = this.treeHelper.getLeafList(treeNode).map((leaf) => parseInt(leaf.name));
|
|
1065
|
+
this.analysisView.grid.setRowOrder(leafOrdering);
|
|
1066
|
+
const dendrogramViewer = await this.df.plot.fromType('Dendrogram', {nodeColumnName: leafCol.name}) as DG.JsViewer;
|
|
1067
|
+
|
|
1068
|
+
this.analysisView.dockManager.dock(dendrogramViewer, DG.DOCK_TYPE.LEFT, null, 'Dendrogram', 0.25);
|
|
1069
|
+
} catch (e) {
|
|
1070
|
+
_package.logger.error(e as string);
|
|
1071
|
+
} finally {
|
|
1072
|
+
pi.close();
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1030
1076
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
1031
1077
|
const currentAAR = this.df.get(position, index) as string;
|
|
1032
1078
|
return currentAAR === aar ? aarLabel : C.CATEGORIES.OTHER;
|
|
@@ -1051,30 +1097,50 @@ export class PeptidesModel {
|
|
|
1051
1097
|
const settingsButton = ui.iconFA('wrench', () => getSettingsDialog(this), 'Peptides analysis settings');
|
|
1052
1098
|
this.analysisView.setRibbonPanels([[settingsButton]], false);
|
|
1053
1099
|
this.isRibbonSet = true;
|
|
1100
|
+
grok.events.onResetFilterRequest.subscribe(() => {
|
|
1101
|
+
this.isInvariantMapTrigger = true;
|
|
1102
|
+
this.initInvariantMapSelection(true);
|
|
1103
|
+
this.isInvariantMapTrigger = false;
|
|
1104
|
+
});
|
|
1054
1105
|
}
|
|
1055
1106
|
|
|
1056
1107
|
this.updateGrid();
|
|
1057
|
-
this.fireBitsetChanged(
|
|
1108
|
+
this.fireBitsetChanged(true);
|
|
1058
1109
|
this.analysisView.grid.invalidate();
|
|
1059
1110
|
}
|
|
1060
1111
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const mutationCliffsViewer = await dfPlt.fromType('peptide-sar-viewer') as MonomerPosition;
|
|
1066
|
-
const mostPotentResiduesViewer = await dfPlt.fromType('peptide-sar-viewer-vertical') as MostPotentResiduesViewer;
|
|
1067
|
-
if (this.settings.clustersColumnName)
|
|
1068
|
-
await this.addLogoSummaryTableViewer();
|
|
1112
|
+
findViewer(viewerType: VIEWER_TYPE): DG.Viewer | null {
|
|
1113
|
+
return wu(this.analysisView.viewers).find((v) => v.type === viewerType) || null;
|
|
1114
|
+
}
|
|
1069
1115
|
|
|
1070
|
-
|
|
1116
|
+
async addLogoSummaryTable(): Promise<void> {
|
|
1117
|
+
this.closeViewer(VIEWER_TYPE.MONOMER_POSITION);
|
|
1118
|
+
this.closeViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
1119
|
+
const logoSummaryTable = await this.df.plot.fromType(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable;
|
|
1120
|
+
this.analysisView.dockManager.dock(logoSummaryTable, DG.DOCK_TYPE.RIGHT, null, VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
1121
|
+
if (this.settings.showMonomerPosition)
|
|
1122
|
+
await this.addMonomerPosition();
|
|
1123
|
+
if (this.settings.showMostPotentResidues)
|
|
1124
|
+
await this.addMostPotentResidues();
|
|
1125
|
+
}
|
|
1071
1126
|
|
|
1072
|
-
|
|
1127
|
+
async addMonomerPosition(): Promise<void> {
|
|
1128
|
+
const monomerPosition = await this.df.plot.fromType(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition;
|
|
1129
|
+
const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer | null;
|
|
1130
|
+
const dm = this.analysisView.dockManager;
|
|
1131
|
+
const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1132
|
+
[DG.DOCK_TYPE.LEFT, this.findViewerNode(VIEWER_TYPE.MOST_POTENT_RESIDUES), 0.7];
|
|
1133
|
+
dm.dock(monomerPosition, dockType, refNode, VIEWER_TYPE.MONOMER_POSITION, ratio);
|
|
1073
1134
|
}
|
|
1074
1135
|
|
|
1075
|
-
async
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1136
|
+
async addMostPotentResidues(): Promise<void> {
|
|
1137
|
+
const mostPotentResidues =
|
|
1138
|
+
await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer;
|
|
1139
|
+
const monomerPosition = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
1140
|
+
const dm = this.analysisView.dockManager;
|
|
1141
|
+
const [dockType, refNode, ratio] = monomerPosition === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1142
|
+
[DG.DOCK_TYPE.RIGHT, this.findViewerNode(VIEWER_TYPE.MONOMER_POSITION), 0.3];
|
|
1143
|
+
dm.dock(mostPotentResidues, dockType, refNode, VIEWER_TYPE.MOST_POTENT_RESIDUES, ratio);
|
|
1078
1144
|
}
|
|
1079
1145
|
|
|
1080
1146
|
addNewCluster(clusterName: string): void {
|
|
@@ -1084,7 +1150,7 @@ export class PeptidesModel {
|
|
|
1084
1150
|
this.analysisView.grid.col(newClusterCol.name)!.visible = false;
|
|
1085
1151
|
}
|
|
1086
1152
|
|
|
1087
|
-
|
|
1153
|
+
createNewView(): void {
|
|
1088
1154
|
const rowMask = this.getCompoundBitest();
|
|
1089
1155
|
if (!rowMask.anyTrue)
|
|
1090
1156
|
return grok.shell.warning('Cannot create a new view, there are no visible selected rows in your dataset');
|