@datagrok/peptides 1.9.2 → 1.11.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/CHANGELOG.md +81 -0
- package/dist/209.js +2 -0
- package/dist/521.js +2 -0
- package/dist/535.js +2 -0
- package/dist/729.js +2 -0
- package/dist/959.js +2 -0
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +13 -11
- package/setup-unlink-clean.sh +0 -0
- package/src/model.ts +302 -232
- package/src/package-test.ts +2 -1
- package/src/package.ts +14 -16
- package/src/tests/algorithms.ts +4 -1
- package/src/tests/core.ts +100 -14
- package/src/tests/model.ts +16 -14
- package/src/tests/peptide-space-test.ts +7 -7
- package/src/tests/table-view.ts +63 -54
- package/src/tests/utils.ts +1 -1
- package/src/tests/viewers.ts +40 -6
- package/src/tests/widgets.ts +100 -18
- package/src/utils/cell-renderer.ts +38 -20
- package/src/utils/constants.ts +3 -0
- package/src/utils/misc.ts +44 -12
- package/src/utils/peptide-similarity-space.ts +3 -4
- package/src/utils/statistics.ts +2 -2
- package/src/utils/types.ts +3 -1
- package/src/viewers/logo-summary.ts +132 -140
- package/src/viewers/peptide-space-viewer.ts +2 -1
- package/src/viewers/sar-viewer.ts +46 -39
- package/src/widgets/distribution.ts +17 -17
- package/src/widgets/mutation-cliffs.ts +91 -19
- package/src/widgets/peptides.ts +12 -13
- package/src/widgets/settings.ts +47 -25
- package/src/widgets/similarity.ts +39 -0
- package/dist/168.js +0 -2
- package/dist/478.js +0 -2
- package/dist/802.js +0 -2
- package/dist/951.js +0 -2
package/src/model.ts
CHANGED
|
@@ -12,15 +12,17 @@ import {DistanceMatrix} from '@datagrok-libraries/ml/src/distance-matrix';
|
|
|
12
12
|
import {StringMetricsNames} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
13
13
|
import {ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
|
|
14
14
|
import {TAGS as treeTAGS} from '@datagrok-libraries/bio/src/trees';
|
|
15
|
+
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
15
16
|
|
|
16
17
|
import wu from 'wu';
|
|
17
18
|
import * as rxjs from 'rxjs';
|
|
18
19
|
import * as uuid from 'uuid';
|
|
20
|
+
import $ from 'cash-dom';
|
|
19
21
|
|
|
20
22
|
import * as C from './utils/constants';
|
|
21
23
|
import * as type from './utils/types';
|
|
22
|
-
import {calculateSelected, extractColInfo, scaleActivity, getStatsSummary} from './utils/misc';
|
|
23
|
-
import {MONOMER_POSITION_PROPERTIES, MonomerPosition,
|
|
24
|
+
import {calculateSelected, extractColInfo, scaleActivity, getStatsSummary, prepareTableForHistogram, getTemplate} from './utils/misc';
|
|
25
|
+
import {MONOMER_POSITION_PROPERTIES, MonomerPosition, MostPotentResidues} from './viewers/sar-viewer';
|
|
24
26
|
import * as CR from './utils/cell-renderer';
|
|
25
27
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
26
28
|
import {getActivityDistribution, getDistributionLegend, getDistributionWidget, getStatsTableMap,
|
|
@@ -31,7 +33,8 @@ import {getSettingsDialog} from './widgets/settings';
|
|
|
31
33
|
import {_package, getMonomerWorksInstance, getTreeHelperInstance} from './package';
|
|
32
34
|
import {findMutations} from './utils/algorithms';
|
|
33
35
|
import {createDistanceMatrixWorker} from './utils/worker-creator';
|
|
34
|
-
import
|
|
36
|
+
import {calculateIdentity, calculateSimilarity} from './widgets/similarity';
|
|
37
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
35
38
|
|
|
36
39
|
export type SummaryStats = {
|
|
37
40
|
minCount: number, maxCount: number,
|
|
@@ -60,12 +63,6 @@ export const getAggregatedColName = (aggF: string, colName: string): string => `
|
|
|
60
63
|
export class PeptidesModel {
|
|
61
64
|
static modelName = 'peptidesModel';
|
|
62
65
|
|
|
63
|
-
_settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
|
|
64
|
-
_monomerPositionSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
65
|
-
_newClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
66
|
-
_removeClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
67
|
-
_filterChangedSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
68
|
-
|
|
69
66
|
_isUpdating: boolean = false;
|
|
70
67
|
isBitsetChangedInitialized = false;
|
|
71
68
|
isCellChanging = false;
|
|
@@ -75,8 +72,8 @@ export class PeptidesModel {
|
|
|
75
72
|
splitCol!: DG.Column<boolean>;
|
|
76
73
|
_monomerPositionStats?: MonomerPositionStats;
|
|
77
74
|
_clusterStats?: ClusterTypeStats;
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
_mutationCliffsSelection!: type.PositionToAARList;
|
|
76
|
+
_invariantMapSelection!: type.PositionToAARList;
|
|
80
77
|
_clusterSelection!: string[];
|
|
81
78
|
_mutationCliffs: type.MutationCliffs | null = null;
|
|
82
79
|
isInitialized = false;
|
|
@@ -90,8 +87,6 @@ export class PeptidesModel {
|
|
|
90
87
|
isRibbonSet = false;
|
|
91
88
|
|
|
92
89
|
_cp?: SeqPalette;
|
|
93
|
-
initBitset: DG.BitSet;
|
|
94
|
-
isInvariantMapTrigger: boolean = false;
|
|
95
90
|
headerSelectedMonomers: type.MonomerSelectionStats = {};
|
|
96
91
|
webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
|
|
97
92
|
cachedWebLogoTooltip: {bar: string, tooltip: HTMLDivElement | null} = {bar: '', tooltip: null};
|
|
@@ -101,12 +96,12 @@ export class PeptidesModel {
|
|
|
101
96
|
_matrixDf?: DG.DataFrame;
|
|
102
97
|
_splitSeqDf?: DG.DataFrame;
|
|
103
98
|
_distanceMatrix!: DistanceMatrix;
|
|
104
|
-
_treeHelper!: ITreeHelper;
|
|
105
99
|
_dm!: DistanceMatrix;
|
|
100
|
+
_layoutEventInitialized = false;
|
|
101
|
+
isToolboxSet: boolean = false;
|
|
106
102
|
|
|
107
103
|
private constructor(dataFrame: DG.DataFrame) {
|
|
108
104
|
this.df = dataFrame;
|
|
109
|
-
this.initBitset = this.df.filter.clone();
|
|
110
105
|
}
|
|
111
106
|
|
|
112
107
|
static getInstance(dataFrame: DG.DataFrame): PeptidesModel {
|
|
@@ -123,11 +118,6 @@ export class PeptidesModel {
|
|
|
123
118
|
return id;
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
get treeHelper(): ITreeHelper {
|
|
127
|
-
this._treeHelper ??= getTreeHelperInstance();
|
|
128
|
-
return this._treeHelper;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
121
|
get monomerPositionDf(): DG.DataFrame {
|
|
132
122
|
this._monomerPositionDf ??= this.createMonomerPositionDf();
|
|
133
123
|
return this._monomerPositionDf;
|
|
@@ -146,15 +136,6 @@ export class PeptidesModel {
|
|
|
146
136
|
this._monomerPositionStats = mps;
|
|
147
137
|
}
|
|
148
138
|
|
|
149
|
-
get matrixDf(): DG.DataFrame {
|
|
150
|
-
this._matrixDf ??= this.buildMatrixDf();
|
|
151
|
-
return this._matrixDf;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
set matrixDf(df: DG.DataFrame) {
|
|
155
|
-
this._matrixDf = df;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
139
|
get splitSeqDf(): DG.DataFrame {
|
|
159
140
|
this._splitSeqDf ??= this.buildSplitSeqDf();
|
|
160
141
|
return this._splitSeqDf;
|
|
@@ -179,10 +160,6 @@ export class PeptidesModel {
|
|
|
179
160
|
}
|
|
180
161
|
|
|
181
162
|
get mutationCliffs(): type.MutationCliffs | null {
|
|
182
|
-
// if (this._mutationCliffs)
|
|
183
|
-
// return this._mutationCliffs;
|
|
184
|
-
|
|
185
|
-
// this.updateMutationCliffs(false);
|
|
186
163
|
return this._mutationCliffs!;
|
|
187
164
|
}
|
|
188
165
|
|
|
@@ -209,59 +186,55 @@ export class PeptidesModel {
|
|
|
209
186
|
}
|
|
210
187
|
|
|
211
188
|
get analysisView(): DG.TableView {
|
|
212
|
-
this._analysisView
|
|
213
|
-
wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame
|
|
214
|
-
|
|
215
|
-
|
|
189
|
+
if (this._analysisView === undefined) {
|
|
190
|
+
this._analysisView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame?.getTag(C.TAGS.UUID) === this.id);
|
|
191
|
+
if (this._analysisView === undefined) {
|
|
192
|
+
this._analysisView = grok.shell.addTableView(this.df);
|
|
193
|
+
const posCols = this.splitSeqDf.columns.names();
|
|
194
|
+
|
|
195
|
+
for (let colIdx = 1; colIdx < this._analysisView.grid.columns.length; ++colIdx) {
|
|
196
|
+
const gridCol = this._analysisView.grid.columns.byIndex(colIdx)!;
|
|
197
|
+
gridCol.visible =
|
|
198
|
+
posCols.includes(gridCol.column!.name) || (gridCol.column!.name === C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized)
|
|
216
204
|
grok.shell.v = this._analysisView;
|
|
217
205
|
|
|
206
|
+
this._analysisView.grid.invalidate();
|
|
218
207
|
return this._analysisView;
|
|
219
208
|
}
|
|
220
209
|
|
|
221
|
-
get
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
get onSettingsChanged(): rxjs.Observable<type.PeptidesSettings> {
|
|
226
|
-
return this._settingsSubject.asObservable();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
get onNewCluster(): rxjs.Observable<undefined> {
|
|
230
|
-
return this._newClusterSubject.asObservable();
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
get onRemoveCluster(): rxjs.Observable<undefined> {
|
|
234
|
-
return this._removeClusterSubject.asObservable();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
get onFilterChanged(): rxjs.Observable<undefined> {
|
|
238
|
-
return this._filterChangedSubject.asObservable();
|
|
210
|
+
get mutationCliffsSelection(): type.PositionToAARList {
|
|
211
|
+
this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
212
|
+
return this._mutationCliffsSelection;
|
|
239
213
|
}
|
|
240
214
|
|
|
241
|
-
|
|
242
|
-
this.
|
|
243
|
-
return this._monomerPositionSelection;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
set monomerPositionSelection(selection: type.PositionToAARList) {
|
|
247
|
-
this._monomerPositionSelection = selection;
|
|
215
|
+
set mutationCliffsSelection(selection: type.PositionToAARList) {
|
|
216
|
+
this._mutationCliffsSelection = selection;
|
|
248
217
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
249
218
|
this.fireBitsetChanged();
|
|
250
|
-
|
|
219
|
+
|
|
220
|
+
const mpViewer = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
221
|
+
mpViewer?.viewerGrid.invalidate();
|
|
222
|
+
const mprViewer = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
|
|
223
|
+
mprViewer?.viewerGrid.invalidate();
|
|
224
|
+
|
|
251
225
|
this.analysisView.grid.invalidate();
|
|
252
226
|
}
|
|
253
227
|
|
|
254
|
-
get
|
|
255
|
-
this.
|
|
256
|
-
|
|
228
|
+
get invariantMapSelection(): type.PositionToAARList {
|
|
229
|
+
this._invariantMapSelection ??=
|
|
230
|
+
JSON.parse(this.df.tags[C.TAGS.INVARIANT_MAP_SELECTION] || this.df.tags[C.TAGS.FILTER] || '{}');
|
|
231
|
+
return this._invariantMapSelection;
|
|
257
232
|
}
|
|
258
233
|
|
|
259
|
-
set
|
|
260
|
-
this.
|
|
261
|
-
this.df.tags[C.TAGS.
|
|
262
|
-
this.
|
|
263
|
-
this.fireBitsetChanged(true);
|
|
264
|
-
this.isInvariantMapTrigger = false;
|
|
234
|
+
set invariantMapSelection(selection: type.PositionToAARList) {
|
|
235
|
+
this._invariantMapSelection = selection;
|
|
236
|
+
this.df.tags[C.TAGS.INVARIANT_MAP_SELECTION] = JSON.stringify(selection);
|
|
237
|
+
this.fireBitsetChanged();
|
|
265
238
|
this.analysisView.grid.invalidate();
|
|
266
239
|
}
|
|
267
240
|
|
|
@@ -298,7 +271,7 @@ export class PeptidesModel {
|
|
|
298
271
|
}
|
|
299
272
|
|
|
300
273
|
get isMonomerPositionSelectionEmpty(): boolean {
|
|
301
|
-
for (const aarList of Object.values(this.
|
|
274
|
+
for (const aarList of Object.values(this.mutationCliffsSelection)) {
|
|
302
275
|
if (aarList.length !== 0)
|
|
303
276
|
return false;
|
|
304
277
|
}
|
|
@@ -326,6 +299,7 @@ export class PeptidesModel {
|
|
|
326
299
|
for (const [key, value] of newSettingsEntries) {
|
|
327
300
|
this._settings[key as keyof type.PeptidesSettings] = value as any;
|
|
328
301
|
switch (key) {
|
|
302
|
+
case 'activityColumnName':
|
|
329
303
|
case 'scaling':
|
|
330
304
|
updateVars.add('activity');
|
|
331
305
|
updateVars.add('mutationCliffs');
|
|
@@ -363,12 +337,11 @@ export class PeptidesModel {
|
|
|
363
337
|
break;
|
|
364
338
|
case 'stats':
|
|
365
339
|
this.monomerPositionStats = this.calculateMonomerPositionStatistics();
|
|
366
|
-
this.monomerPositionDf = this.createMonomerPositionDf();
|
|
367
340
|
this.mostPotentResiduesDf = this.createMostPotentResiduesDf();
|
|
368
341
|
this.clusterStats = this.calculateClusterStatistics();
|
|
369
342
|
break;
|
|
370
343
|
case 'grid':
|
|
371
|
-
this.
|
|
344
|
+
this.setGridProperties();
|
|
372
345
|
break;
|
|
373
346
|
case 'dendrogram':
|
|
374
347
|
this.settings.showDendrogram ? this.addDendrogram() : this.closeViewer(VIEWER_TYPE.DENDROGRAM);
|
|
@@ -389,7 +362,23 @@ export class PeptidesModel {
|
|
|
389
362
|
}
|
|
390
363
|
|
|
391
364
|
//TODO: handle settings change
|
|
392
|
-
this.
|
|
365
|
+
const mpViewer = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
366
|
+
mpViewer?.createMonomerPositionGrid();
|
|
367
|
+
mpViewer?.render();
|
|
368
|
+
const mprViewer = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
|
|
369
|
+
mprViewer?.createMostPotentResiduesGrid();
|
|
370
|
+
mprViewer?.render();
|
|
371
|
+
const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
372
|
+
lstViewer?.createLogoSummaryTableGrid();
|
|
373
|
+
lstViewer?.render();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
get identityTemplate(): string {
|
|
377
|
+
return this.df.getTag(C.TAGS.IDENTITY_TEMPLATE) ?? '';
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
set identityTemplate(template: string) {
|
|
381
|
+
this.df.setTag(C.TAGS.IDENTITY_TEMPLATE, template);
|
|
393
382
|
}
|
|
394
383
|
|
|
395
384
|
updateMutationCliffs(notify: boolean = true): void {
|
|
@@ -409,34 +398,23 @@ export class PeptidesModel {
|
|
|
409
398
|
}
|
|
410
399
|
|
|
411
400
|
createMonomerPositionDf(): DG.DataFrame {
|
|
412
|
-
const
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
420
|
-
for (let i = 0; i < monomerCol.length; ++i) {
|
|
421
|
-
if (monomerCol.get(i) === '') {
|
|
422
|
-
matrixDf.rows.removeAt(i);
|
|
423
|
-
break;
|
|
401
|
+
const uniqueMonomers = new Set<string>();
|
|
402
|
+
const splitSeqCols = this.splitSeqDf.columns;
|
|
403
|
+
for (const col of splitSeqCols) {
|
|
404
|
+
const colCat = col.categories;
|
|
405
|
+
for (const cat of colCat) {
|
|
406
|
+
if (cat !== '')
|
|
407
|
+
uniqueMonomers.add(cat);
|
|
424
408
|
}
|
|
425
409
|
}
|
|
426
|
-
matrixDf.name = 'SAR';
|
|
427
410
|
|
|
428
|
-
|
|
429
|
-
|
|
411
|
+
const monomerCol = DG.Column.fromStrings(C.COLUMNS_NAMES.MONOMER, Array.from(uniqueMonomers));
|
|
412
|
+
const monomerPositionDf = DG.DataFrame.fromColumns([monomerCol]);
|
|
413
|
+
monomerPositionDf.name = 'SAR';
|
|
414
|
+
for (const col of splitSeqCols)
|
|
415
|
+
monomerPositionDf.columns.addNewBool(col.name);
|
|
430
416
|
|
|
431
|
-
|
|
432
|
-
const splitSeqDfColumns = this.splitSeqDf.columns;
|
|
433
|
-
const positionColumns = splitSeqDfColumns.names();
|
|
434
|
-
return this.splitSeqDf
|
|
435
|
-
.groupBy(positionColumns)
|
|
436
|
-
.aggregate()
|
|
437
|
-
.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER)
|
|
438
|
-
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER])
|
|
439
|
-
.aggregate();
|
|
417
|
+
return monomerPositionDf;
|
|
440
418
|
}
|
|
441
419
|
|
|
442
420
|
buildSplitSeqDf(): DG.DataFrame {
|
|
@@ -462,19 +440,35 @@ export class PeptidesModel {
|
|
|
462
440
|
acc.addTitle(ui.h1(`${filterAndSelectionBs.trueCount} selected rows${filteredTitlePart}`));
|
|
463
441
|
if (filterAndSelectionBs.anyTrue) {
|
|
464
442
|
acc.addPane('Actions', () => {
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
443
|
+
const newView = ui.label('New view');
|
|
444
|
+
$(newView).addClass('d4-link-action');
|
|
445
|
+
newView.onclick = () => trueModel.createNewView();
|
|
446
|
+
newView.onmouseover = (ev) => ui.tooltip.show('Creates a new view from current selection', ev.clientX + 5, ev.clientY + 5);
|
|
447
|
+
const newCluster = ui.label('New cluster');
|
|
448
|
+
$(newCluster).addClass('d4-link-action');
|
|
449
|
+
newCluster.onclick = () => {
|
|
450
|
+
const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
451
|
+
if (lstViewer === null)
|
|
452
|
+
throw new Error('Logo summary table viewer is not found');
|
|
453
|
+
lstViewer.clusterFromSelection();
|
|
454
|
+
};
|
|
455
|
+
newCluster.onmouseover = (ev) => ui.tooltip.show('Creates a new cluster from selection', ev.clientX + 5, ev.clientY + 5);
|
|
456
|
+
const removeCluster = ui.label('Remove cluster');
|
|
457
|
+
$(removeCluster).addClass('d4-link-action');
|
|
458
|
+
removeCluster.onclick = () => {
|
|
459
|
+
const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
460
|
+
if (lstViewer === null)
|
|
461
|
+
throw new Error('Logo summary table viewer is not found');
|
|
462
|
+
lstViewer.removeCluster();
|
|
463
|
+
};
|
|
464
|
+
removeCluster.onmouseover = (ev) => ui.tooltip.show('Removes currently selected custom cluster', ev.clientX + 5, ev.clientY + 5);
|
|
465
|
+
removeCluster.style.visibility = trueModel.clusterSelection.length === 0 ||
|
|
466
|
+
!wu(this.customClusters).some((c) => trueModel.clusterSelection.includes(c.name)) ? 'hidden' : 'visible';
|
|
467
|
+
return ui.divV([newView, newCluster, removeCluster]);
|
|
474
468
|
});
|
|
475
469
|
}
|
|
476
470
|
const table = trueModel.df.filter.anyFalse ? trueModel.df.clone(trueModel.df.filter, null, true) : trueModel.df;
|
|
477
|
-
acc.addPane('Mutation
|
|
471
|
+
acc.addPane('Mutation Cliffs pairs', () => mutationCliffsWidget(trueModel.df, trueModel).root);
|
|
478
472
|
acc.addPane('Distribution', () => getDistributionWidget(table, trueModel).root);
|
|
479
473
|
|
|
480
474
|
return acc;
|
|
@@ -483,12 +477,10 @@ export class PeptidesModel {
|
|
|
483
477
|
updateGrid(): void {
|
|
484
478
|
this.joinDataFrames();
|
|
485
479
|
|
|
486
|
-
this.sortSourceGrid();
|
|
487
|
-
|
|
488
480
|
this.createScaledCol();
|
|
489
481
|
|
|
490
|
-
this.
|
|
491
|
-
this.
|
|
482
|
+
this.initInvariantMapSelection({notify: false});
|
|
483
|
+
this.initMutationCliffsSelection({notify: false});
|
|
492
484
|
|
|
493
485
|
this.setWebLogoInteraction();
|
|
494
486
|
this.webLogoBounds = {};
|
|
@@ -499,14 +491,14 @@ export class PeptidesModel {
|
|
|
499
491
|
|
|
500
492
|
this.setBitsetCallback();
|
|
501
493
|
|
|
502
|
-
this.
|
|
494
|
+
this.setGridProperties();
|
|
503
495
|
}
|
|
504
496
|
|
|
505
|
-
|
|
497
|
+
initInvariantMapSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
|
|
506
498
|
options.cleanInit ??= false;
|
|
507
499
|
options.notify ??= true;
|
|
508
500
|
|
|
509
|
-
const tempFilter: type.PositionToAARList = this.
|
|
501
|
+
const tempFilter: type.PositionToAARList = this.invariantMapSelection;
|
|
510
502
|
const positionColumns = this.splitSeqDf.columns.names();
|
|
511
503
|
for (const pos of positionColumns) {
|
|
512
504
|
if (options.cleanInit || !tempFilter.hasOwnProperty(pos))
|
|
@@ -514,16 +506,16 @@ export class PeptidesModel {
|
|
|
514
506
|
}
|
|
515
507
|
|
|
516
508
|
if (options.notify)
|
|
517
|
-
this.
|
|
509
|
+
this.invariantMapSelection = tempFilter;
|
|
518
510
|
else
|
|
519
|
-
this.
|
|
511
|
+
this._invariantMapSelection = tempFilter;
|
|
520
512
|
}
|
|
521
513
|
|
|
522
|
-
|
|
514
|
+
initMutationCliffsSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
|
|
523
515
|
options.cleanInit ??= false;
|
|
524
516
|
options.notify ??= true;
|
|
525
517
|
|
|
526
|
-
const tempSelection: type.PositionToAARList = this.
|
|
518
|
+
const tempSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
527
519
|
const positionColumns = this.splitSeqDf.columns.names();
|
|
528
520
|
for (const pos of positionColumns) {
|
|
529
521
|
if (options.cleanInit || !tempSelection.hasOwnProperty(pos))
|
|
@@ -531,9 +523,9 @@ export class PeptidesModel {
|
|
|
531
523
|
}
|
|
532
524
|
|
|
533
525
|
if (options.notify)
|
|
534
|
-
this.
|
|
526
|
+
this.mutationCliffsSelection = tempSelection;
|
|
535
527
|
else
|
|
536
|
-
this.
|
|
528
|
+
this._mutationCliffsSelection = tempSelection;
|
|
537
529
|
}
|
|
538
530
|
|
|
539
531
|
joinDataFrames(): void {
|
|
@@ -542,39 +534,19 @@ export class PeptidesModel {
|
|
|
542
534
|
const cols = this.df.columns;
|
|
543
535
|
const positionColumns = this.splitSeqDf.columns.names();
|
|
544
536
|
for (const colName of positionColumns) {
|
|
545
|
-
|
|
537
|
+
let col = this.df.col(colName);
|
|
546
538
|
const newCol = this.splitSeqDf.getCol(colName);
|
|
547
|
-
if (col
|
|
548
|
-
cols.add(newCol);
|
|
549
|
-
else {
|
|
539
|
+
if (col !== null)
|
|
550
540
|
cols.remove(colName);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
541
|
+
|
|
542
|
+
const newColCat = newCol.categories;
|
|
543
|
+
const newColData = newCol.getRawData();
|
|
544
|
+
col = cols.addNew(newCol.name, newCol.type).init((i) => newColCat[newColData[i]]);
|
|
545
|
+
CR.setAARRenderer(col, this.alphabet);
|
|
554
546
|
}
|
|
555
547
|
this.df.name = name;
|
|
556
548
|
}
|
|
557
549
|
|
|
558
|
-
sortSourceGrid(): void {
|
|
559
|
-
const colNames: DG.GridColumn[] = [];
|
|
560
|
-
const sourceGridCols = this.analysisView.grid.columns;
|
|
561
|
-
const sourceGridColsCount = sourceGridCols.length;
|
|
562
|
-
for (let i = 1; i < sourceGridColsCount; i++)
|
|
563
|
-
colNames.push(sourceGridCols.byIndex(i)!);
|
|
564
|
-
|
|
565
|
-
colNames.sort((a, b) => {
|
|
566
|
-
if (a.column!.semType === C.SEM_TYPES.MONOMER) {
|
|
567
|
-
if (b.column!.semType === C.SEM_TYPES.MONOMER)
|
|
568
|
-
return 0;
|
|
569
|
-
return -1;
|
|
570
|
-
}
|
|
571
|
-
if (b.column!.semType === C.SEM_TYPES.MONOMER)
|
|
572
|
-
return 1;
|
|
573
|
-
return 0;
|
|
574
|
-
});
|
|
575
|
-
sourceGridCols.setOrder(colNames.map((v) => v.name));
|
|
576
|
-
}
|
|
577
|
-
|
|
578
550
|
createScaledCol(): void {
|
|
579
551
|
const sourceGrid = this.analysisView.grid;
|
|
580
552
|
const scaledCol = scaleActivity(this.df.getCol(this.settings.activityColumnName!), this.settings.scaling);
|
|
@@ -586,9 +558,9 @@ export class PeptidesModel {
|
|
|
586
558
|
|
|
587
559
|
calculateMonomerPositionStatistics(): MonomerPositionStats {
|
|
588
560
|
const positionColumns = this.splitSeqDf.columns.toList();
|
|
561
|
+
const sourceDfLen = this.df.rowCount;
|
|
589
562
|
const monomerPositionObject = {general: {}} as MonomerPositionStats & { general: SummaryStats };
|
|
590
563
|
const activityColData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
591
|
-
const sourceDfLen = activityColData.length;
|
|
592
564
|
|
|
593
565
|
for (const posCol of positionColumns) {
|
|
594
566
|
const posColData = posCol.getRawData();
|
|
@@ -600,7 +572,12 @@ export class PeptidesModel {
|
|
|
600
572
|
if (monomer === '')
|
|
601
573
|
continue;
|
|
602
574
|
|
|
603
|
-
const
|
|
575
|
+
const boolArray: boolean[] = new Array(sourceDfLen).fill(false);
|
|
576
|
+
for (let i = 0; i < sourceDfLen; ++i) {
|
|
577
|
+
if (posColData[i] === categoryIndex)
|
|
578
|
+
boolArray[i] = true;
|
|
579
|
+
}
|
|
580
|
+
const bitArray = BitArray.fromValues(boolArray);
|
|
604
581
|
const stats = getStats(activityColData, bitArray);
|
|
605
582
|
currentPositionObject[monomer] = stats;
|
|
606
583
|
this.getSummaryStats(currentPositionObject.general, stats);
|
|
@@ -658,12 +635,11 @@ export class PeptidesModel {
|
|
|
658
635
|
|
|
659
636
|
calculateClusterStatistics(): ClusterTypeStats {
|
|
660
637
|
const rowCount = this.df.rowCount;
|
|
661
|
-
|
|
662
638
|
const origClustCol = this.df.getCol(this.settings.clustersColumnName!);
|
|
663
639
|
const origClustColData = origClustCol.getRawData();
|
|
664
640
|
const origClustColCat = origClustCol.categories;
|
|
665
641
|
const origClustMasks: BitArray[] = Array.from({length: origClustColCat.length},
|
|
666
|
-
() => BitArray
|
|
642
|
+
() => new BitArray(rowCount, false));
|
|
667
643
|
for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx)
|
|
668
644
|
origClustMasks[origClustColData[rowIdx]].setTrue(rowIdx);
|
|
669
645
|
|
|
@@ -793,9 +769,9 @@ export class PeptidesModel {
|
|
|
793
769
|
const position = barPart.position;
|
|
794
770
|
if (ev.type === 'click') {
|
|
795
771
|
if (!ev.shiftKey)
|
|
796
|
-
this.
|
|
772
|
+
this.initInvariantMapSelection({cleanInit: true, notify: false});
|
|
797
773
|
|
|
798
|
-
this.modifyMonomerPositionSelection(monomer, position,
|
|
774
|
+
this.modifyMonomerPositionSelection(monomer, position, true);
|
|
799
775
|
} else {
|
|
800
776
|
const bar = `${position} = ${monomer}`;
|
|
801
777
|
if (this.cachedWebLogoTooltip.bar === bar)
|
|
@@ -811,7 +787,7 @@ export class PeptidesModel {
|
|
|
811
787
|
setCellRenderers(): void {
|
|
812
788
|
const sourceGrid = this.analysisView.grid;
|
|
813
789
|
sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
814
|
-
|
|
790
|
+
const headerRenderer = (gcArgs: DG.GridCellRenderArgs): void => {
|
|
815
791
|
const ctx = gcArgs.g;
|
|
816
792
|
const bounds = gcArgs.bounds;
|
|
817
793
|
const col = gcArgs.cell.tableColumn;
|
|
@@ -834,8 +810,8 @@ export class PeptidesModel {
|
|
|
834
810
|
return 0;
|
|
835
811
|
}).filter((v) => v !== 'general');
|
|
836
812
|
|
|
837
|
-
this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats,
|
|
838
|
-
this.cp, this.headerSelectedMonomers[col.name]);
|
|
813
|
+
this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats, col.name, sortedStatsOrder,
|
|
814
|
+
this.df.rowCount, this.cp, this.headerSelectedMonomers[col.name]);
|
|
839
815
|
gcArgs.preventDefault();
|
|
840
816
|
}
|
|
841
817
|
} catch (e) {
|
|
@@ -845,15 +821,28 @@ export class PeptidesModel {
|
|
|
845
821
|
} finally {
|
|
846
822
|
ctx.restore();
|
|
847
823
|
}
|
|
848
|
-
}
|
|
824
|
+
};
|
|
825
|
+
sourceGrid.onCellRender.subscribe((gcArgs) => headerRenderer(gcArgs));
|
|
826
|
+
|
|
827
|
+
if (!this._layoutEventInitialized) {
|
|
828
|
+
grok.events.onViewLayoutApplied.subscribe((layout) => {
|
|
829
|
+
if (layout.view.id === this.analysisView.id) {
|
|
830
|
+
// this.analysisView.grid.onCellRender.subscribe((gcArgs) => headerRenderer(gcArgs));
|
|
831
|
+
this.updateGrid();
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
this._layoutEventInitialized = true;
|
|
835
|
+
}
|
|
849
836
|
}
|
|
850
837
|
|
|
851
838
|
setTooltips(): void {
|
|
852
839
|
this.analysisView.grid.onCellTooltip((cell, x, y) => {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (
|
|
856
|
-
|
|
840
|
+
if (cell.isColHeader && cell.tableColumn!.semType === C.SEM_TYPES.MONOMER)
|
|
841
|
+
return true;
|
|
842
|
+
if (!(cell.isTableCell && cell.tableColumn!.semType === C.SEM_TYPES.MONOMER))
|
|
843
|
+
return false;
|
|
844
|
+
|
|
845
|
+
this.showMonomerTooltip(cell.cell.value, x, y);
|
|
857
846
|
return true;
|
|
858
847
|
});
|
|
859
848
|
}
|
|
@@ -863,18 +852,21 @@ export class PeptidesModel {
|
|
|
863
852
|
const monomerName = aar.toLowerCase();
|
|
864
853
|
|
|
865
854
|
const mw = getMonomerWorksInstance();
|
|
866
|
-
const mol = mw
|
|
855
|
+
const mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
|
|
867
856
|
|
|
868
857
|
if (mol) {
|
|
869
858
|
tooltipElements.push(ui.div(monomerName));
|
|
870
859
|
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
871
860
|
tooltipElements.push(grok.chem.svgMol(mol, undefined, undefined, options));
|
|
872
|
-
} else
|
|
861
|
+
} else if (aar !== '')
|
|
873
862
|
tooltipElements.push(ui.div(aar));
|
|
863
|
+
else
|
|
864
|
+
return true;
|
|
865
|
+
|
|
874
866
|
|
|
875
867
|
ui.tooltip.show(ui.divV(tooltipElements), x, y);
|
|
876
868
|
|
|
877
|
-
return
|
|
869
|
+
return true;
|
|
878
870
|
}
|
|
879
871
|
|
|
880
872
|
//TODO: move out to viewer code
|
|
@@ -893,12 +885,12 @@ export class PeptidesModel {
|
|
|
893
885
|
const distributionTable = DG.DataFrame.fromColumns(
|
|
894
886
|
[activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
|
|
895
887
|
const labels = getDistributionLegend(`${position} : ${aar}`, 'Other');
|
|
896
|
-
const hist = getActivityDistribution(distributionTable, true);
|
|
897
|
-
const tableMap = getStatsTableMap(stats
|
|
898
|
-
const aggregatedColMap = this.getAggregatedColumnValues({mask: mask
|
|
888
|
+
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable), true);
|
|
889
|
+
const tableMap = getStatsTableMap(stats);
|
|
890
|
+
const aggregatedColMap = this.getAggregatedColumnValues({mask: mask});
|
|
899
891
|
|
|
900
892
|
const resultMap = {...tableMap, ...aggregatedColMap};
|
|
901
|
-
const distroStatsElem = getStatsSummary(labels, hist, resultMap
|
|
893
|
+
const distroStatsElem = getStatsSummary(labels, hist, resultMap);
|
|
902
894
|
|
|
903
895
|
ui.tooltip.show(distroStatsElem, x, y);
|
|
904
896
|
|
|
@@ -908,6 +900,7 @@ export class PeptidesModel {
|
|
|
908
900
|
getAggregatedColumnValues(options: {filterDf?: boolean, mask?: DG.BitSet, fractionDigits?: number} = {},
|
|
909
901
|
): StringDictionary {
|
|
910
902
|
options.filterDf ??= false;
|
|
903
|
+
options.fractionDigits ??= 3;
|
|
911
904
|
|
|
912
905
|
const filteredDf = options.filterDf && this.df.filter.anyFalse ? this.df.clone(this.df.filter) : this.df;
|
|
913
906
|
|
|
@@ -920,8 +913,11 @@ export class PeptidesModel {
|
|
|
920
913
|
return colResults;
|
|
921
914
|
}
|
|
922
915
|
|
|
923
|
-
modifyMonomerPositionSelection(aar: string, position: string,
|
|
924
|
-
|
|
916
|
+
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMap: boolean): void {
|
|
917
|
+
if (this.df.getCol(position).categories.indexOf(aar) === -1)
|
|
918
|
+
return;
|
|
919
|
+
|
|
920
|
+
const tempSelection = isInvariantMap ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
925
921
|
const tempSelectionAt = tempSelection[position];
|
|
926
922
|
const aarIndex = tempSelectionAt.indexOf(aar);
|
|
927
923
|
if (aarIndex === -1)
|
|
@@ -929,10 +925,10 @@ export class PeptidesModel {
|
|
|
929
925
|
else
|
|
930
926
|
tempSelectionAt.splice(aarIndex, 1);
|
|
931
927
|
|
|
932
|
-
if (
|
|
933
|
-
this.
|
|
928
|
+
if (isInvariantMap)
|
|
929
|
+
this.invariantMapSelection = tempSelection;
|
|
934
930
|
else
|
|
935
|
-
this.
|
|
931
|
+
this.mutationCliffsSelection = tempSelection;
|
|
936
932
|
}
|
|
937
933
|
|
|
938
934
|
setBitsetCallback(): void {
|
|
@@ -942,14 +938,45 @@ export class PeptidesModel {
|
|
|
942
938
|
const filter = this.df.filter;
|
|
943
939
|
const clusterCol = this.df.col(this.settings.clustersColumnName!);
|
|
944
940
|
|
|
945
|
-
const changeSelectionBitset = (currentBitset: DG.BitSet,
|
|
941
|
+
const changeSelectionBitset = (currentBitset: DG.BitSet, clustColCat: string[],
|
|
946
942
|
clustColData: type.RawData, customClust: {[key: string]: BitArray}): void => {
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
943
|
+
const indexes = new Set<number>();
|
|
944
|
+
for (const [position, monomerList] of Object.entries(this.mutationCliffsSelection)) {
|
|
945
|
+
for (const monomer of monomerList) {
|
|
946
|
+
const substitutions = this.mutationCliffs?.get(monomer)?.get(position) ?? null;
|
|
947
|
+
if (substitutions === null)
|
|
948
|
+
continue;
|
|
949
|
+
for (const [key, value] of substitutions.entries()) {
|
|
950
|
+
indexes.add(key);
|
|
951
|
+
for (const v of value)
|
|
952
|
+
indexes.add(v);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
const positionList = Object.keys(this.invariantMapSelection);
|
|
958
|
+
const rowCount = this.df.rowCount;
|
|
959
|
+
for (const position of positionList) {
|
|
960
|
+
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
961
|
+
const positionColData = positionCol.getRawData();
|
|
962
|
+
const positionColCat = positionCol.categories;
|
|
963
|
+
const aarList = this.invariantMapSelection[position];
|
|
964
|
+
for (const aar of aarList) {
|
|
965
|
+
const aarIndex = positionColCat.indexOf(aar);
|
|
966
|
+
if (aarIndex === -1)
|
|
967
|
+
continue;
|
|
968
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
969
|
+
if (positionColData[i] === aarIndex)
|
|
970
|
+
indexes.add(i);
|
|
971
|
+
}
|
|
951
972
|
}
|
|
973
|
+
}
|
|
952
974
|
|
|
975
|
+
const getBitAt = (i: number): boolean => {
|
|
976
|
+
if (indexes.has(i))
|
|
977
|
+
return true;
|
|
978
|
+
|
|
979
|
+
//TODO: preprocess
|
|
953
980
|
const currentOrigClust = clustColCat[clustColData[i]];
|
|
954
981
|
if (typeof currentOrigClust === undefined)
|
|
955
982
|
return false;
|
|
@@ -971,11 +998,6 @@ export class PeptidesModel {
|
|
|
971
998
|
if (this.isUserChangedSelection)
|
|
972
999
|
return;
|
|
973
1000
|
|
|
974
|
-
const positionList: type.RawColumn[] = Object.keys(this.monomerPositionSelection).map((pos) => {
|
|
975
|
-
const posCol = this.df.getCol(pos);
|
|
976
|
-
return {name: pos, cat: posCol.categories, rawData: posCol.getRawData()};
|
|
977
|
-
});
|
|
978
|
-
|
|
979
1001
|
const clustColCat = clusterCol?.categories ?? [];
|
|
980
1002
|
const clustColData = clusterCol?.getRawData() ?? new Int32Array(0);
|
|
981
1003
|
const customClust: {[key: string]: BitArray} = {};
|
|
@@ -983,29 +1005,15 @@ export class PeptidesModel {
|
|
|
983
1005
|
for (const clust of this.customClusters)
|
|
984
1006
|
customClust[clust.name] = BitArray.fromUint32Array(rowCount, clust.getRawData() as Uint32Array);
|
|
985
1007
|
|
|
986
|
-
changeSelectionBitset(selection,
|
|
1008
|
+
changeSelectionBitset(selection, clustColCat, clustColData, customClust);
|
|
987
1009
|
});
|
|
988
1010
|
|
|
989
1011
|
filter.onChanged.subscribe(() => {
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
|
|
996
|
-
if (!result)
|
|
997
|
-
return result;
|
|
998
|
-
}
|
|
999
|
-
return result;
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
if (!this.isInvariantMapTrigger)
|
|
1003
|
-
this.initBitset = filter.clone();
|
|
1004
|
-
|
|
1005
|
-
const temp = invariantMapBitset.and(this.initBitset);
|
|
1006
|
-
filter.init((i) => temp.get(i), false);
|
|
1007
|
-
|
|
1008
|
-
this._filterChangedSubject.next();
|
|
1012
|
+
const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
1013
|
+
if (lstViewer !== null && typeof lstViewer.model !== 'undefined') {
|
|
1014
|
+
lstViewer.createLogoSummaryTableGrid();
|
|
1015
|
+
lstViewer.render();
|
|
1016
|
+
}
|
|
1009
1017
|
});
|
|
1010
1018
|
this.isBitsetChangedInitialized = true;
|
|
1011
1019
|
}
|
|
@@ -1027,23 +1035,13 @@ export class PeptidesModel {
|
|
|
1027
1035
|
this.isUserChangedSelection = true;
|
|
1028
1036
|
}
|
|
1029
1037
|
|
|
1030
|
-
|
|
1031
|
-
const posCols = this.splitSeqDf.columns.names();
|
|
1038
|
+
setGridProperties(props?: DG.IGridLookSettings): void {
|
|
1032
1039
|
const sourceGrid = this.analysisView.grid;
|
|
1033
|
-
const sourceGridCols = sourceGrid.columns;
|
|
1034
|
-
const sourceGridColsLen = sourceGridCols.length;
|
|
1035
|
-
const visibleColumns = Object.keys(this.settings.columns || {});
|
|
1036
1040
|
const sourceGridProps = sourceGrid.props;
|
|
1037
|
-
sourceGridProps.allowColSelection = false;
|
|
1038
|
-
sourceGridProps.allowEdit = false;
|
|
1039
|
-
sourceGridProps.showCurrentRowIndicator = false;
|
|
1041
|
+
sourceGridProps.allowColSelection = props?.allowColSelection ?? false;
|
|
1042
|
+
sourceGridProps.allowEdit = props?.allowEdit ?? false;
|
|
1043
|
+
sourceGridProps.showCurrentRowIndicator = props?.showCurrentRowIndicator ?? false;
|
|
1040
1044
|
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
1041
|
-
for (let colIdx = 1; colIdx < sourceGridColsLen; ++colIdx) {
|
|
1042
|
-
const gridCol = sourceGridCols.byIndex(colIdx)!;
|
|
1043
|
-
const tableColName = gridCol.column!.name;
|
|
1044
|
-
gridCol.visible = posCols.includes(tableColName) || (tableColName === C.COLUMNS_NAMES.ACTIVITY_SCALED) ||
|
|
1045
|
-
visibleColumns.includes(tableColName);
|
|
1046
|
-
}
|
|
1047
1045
|
}
|
|
1048
1046
|
|
|
1049
1047
|
closeViewer(viewerType: VIEWER_TYPE): void {
|
|
@@ -1066,10 +1064,11 @@ export class PeptidesModel {
|
|
|
1066
1064
|
const pepColValues: string[] = this.df.getCol(this.settings.sequenceColumnName!).toList();
|
|
1067
1065
|
this._dm ??= new DistanceMatrix(await createDistanceMatrixWorker(pepColValues, StringMetricsNames.Levenshtein));
|
|
1068
1066
|
const leafCol = this.df.col('~leaf-id') ?? this.df.columns.addNewString('~leaf-id').init((i) => i.toString());
|
|
1069
|
-
const
|
|
1067
|
+
const treeHelper: ITreeHelper = getTreeHelperInstance()!;
|
|
1068
|
+
const treeNode = await treeHelper.hierarchicalClusteringByDistance(this._dm, 'ward');
|
|
1070
1069
|
|
|
1071
|
-
this.df.setTag(treeTAGS.NEWICK,
|
|
1072
|
-
const leafOrdering =
|
|
1070
|
+
this.df.setTag(treeTAGS.NEWICK, treeHelper.toNewick(treeNode));
|
|
1071
|
+
const leafOrdering = treeHelper.getLeafList(treeNode).map((leaf) => parseInt(leaf.name));
|
|
1073
1072
|
this.analysisView.grid.setRowOrder(leafOrdering);
|
|
1074
1073
|
const dendrogramViewer = await this.df.plot.fromType('Dendrogram', {nodeColumnName: leafCol.name}) as DG.JsViewer;
|
|
1075
1074
|
|
|
@@ -1105,18 +1104,88 @@ export class PeptidesModel {
|
|
|
1105
1104
|
const settingsButton = ui.iconFA('wrench', () => getSettingsDialog(this), 'Peptides analysis settings');
|
|
1106
1105
|
this.analysisView.setRibbonPanels([[settingsButton]], false);
|
|
1107
1106
|
this.isRibbonSet = true;
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1107
|
+
this.updateGrid();
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
if (!this.isToolboxSet && this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1') {
|
|
1111
|
+
let template: ISeqSplitted;
|
|
1112
|
+
const sequencesCol = this.df.getCol(this.settings.sequenceColumnName!);
|
|
1113
|
+
const minTemplateLength = this.splitSeqDf.columns.toList()
|
|
1114
|
+
.filter((col) => col.stats.missingValueCount === 0).length;
|
|
1115
|
+
const calculateIdentityBtn = ui.button('Identity', async () => {
|
|
1116
|
+
let identityScoresCol = calculateIdentity(template, this.splitSeqDf);
|
|
1117
|
+
identityScoresCol.name = this.df.columns.getUnusedName(identityScoresCol.name);
|
|
1118
|
+
identityScoresCol = this.df.columns.add(identityScoresCol);
|
|
1119
|
+
identityScoresCol.setTag(C.TAGS.IDENTITY_TEMPLATE, new Array(template).join(' '));
|
|
1120
|
+
}, 'Calculate identity');
|
|
1121
|
+
const calculateSimilarityBtn = ui.button('Similarity', async () => {
|
|
1122
|
+
let similarityScoresCol = await calculateSimilarity(template, this.splitSeqDf);
|
|
1123
|
+
similarityScoresCol.name = this.df.columns.getUnusedName(similarityScoresCol.name);
|
|
1124
|
+
similarityScoresCol = this.df.columns.add(similarityScoresCol);
|
|
1125
|
+
similarityScoresCol.setTag(C.TAGS.SIMILARITY_TEMPLATE, new Array(template).join(' '));
|
|
1126
|
+
}, 'Calculate similarity');
|
|
1127
|
+
const templateInput = ui.stringInput('Template', this.identityTemplate, async () => {
|
|
1128
|
+
this.identityTemplate = templateInput.value;
|
|
1129
|
+
if (isNaN(parseInt(templateInput.value))) {
|
|
1130
|
+
if (templateInput.value.length === 0) {
|
|
1131
|
+
calculateIdentityBtn.disabled = true;
|
|
1132
|
+
calculateSimilarityBtn.disabled = true;
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
try {
|
|
1136
|
+
template ??= await getTemplate(this.identityTemplate, sequencesCol);
|
|
1137
|
+
if (template.length < minTemplateLength) {
|
|
1138
|
+
grok.shell.warning(`Template length should be at least ${minTemplateLength} amino acids.`);
|
|
1139
|
+
calculateIdentityBtn.disabled = true;
|
|
1140
|
+
calculateSimilarityBtn.disabled = true;
|
|
1141
|
+
return;
|
|
1142
|
+
} else if (new Array(template).includes('') || new Array(template).includes('-')) {
|
|
1143
|
+
grok.shell.warning('Template shouldn\'t contain gaps or empty cells.');
|
|
1144
|
+
calculateIdentityBtn.disabled = true;
|
|
1145
|
+
calculateSimilarityBtn.disabled = true;
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
} catch (e) {
|
|
1149
|
+
grok.shell.warning(`Only ${sequencesCol.getTag(DG.TAGS.UNITS)} sequence format is supported.`);
|
|
1150
|
+
grok.log.warning(e as string);
|
|
1151
|
+
calculateIdentityBtn.disabled = true;
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
} else {
|
|
1155
|
+
const rowIndex = parseInt(templateInput.value) - 1;
|
|
1156
|
+
const selectedIndexes = this.df.filter.getSelectedIndexes();
|
|
1157
|
+
if (rowIndex < 0 || rowIndex >= selectedIndexes.length) {
|
|
1158
|
+
grok.shell.warning('Invalid row index');
|
|
1159
|
+
calculateIdentityBtn.disabled = true;
|
|
1160
|
+
calculateSimilarityBtn.disabled = true;
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
this.identityTemplate = sequencesCol.get(selectedIndexes[rowIndex]);
|
|
1164
|
+
}
|
|
1165
|
+
try {
|
|
1166
|
+
template = await getTemplate(this.identityTemplate, sequencesCol);
|
|
1167
|
+
} catch (e) {
|
|
1168
|
+
grok.shell.warning('Couldn\'t recognize sequence format.');
|
|
1169
|
+
grok.log.warning(e as string);
|
|
1170
|
+
calculateIdentityBtn.disabled = true;
|
|
1171
|
+
calculateSimilarityBtn.disabled = true;
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
calculateIdentityBtn.disabled = false;
|
|
1175
|
+
calculateSimilarityBtn.disabled = false;
|
|
1176
|
+
}, {placeholder: 'Sequence or row index...'});
|
|
1177
|
+
templateInput.setTooltip('Template sequence. Can be row index, peptide ID or sequence.');
|
|
1178
|
+
templateInput.fireChanged();
|
|
1179
|
+
const acc = this.analysisView.toolboxPage.accordion;
|
|
1180
|
+
acc.addPane('Sequence Identity and Similarity',
|
|
1181
|
+
() => ui.divV([ui.form([templateInput]), calculateIdentityBtn, calculateSimilarityBtn]), true, acc.panes[0]);
|
|
1182
|
+
this.isToolboxSet = true;
|
|
1113
1183
|
}
|
|
1114
1184
|
|
|
1115
|
-
this.updateGrid();
|
|
1116
1185
|
this.fireBitsetChanged(true);
|
|
1117
|
-
this.analysisView.grid.invalidate();
|
|
1118
1186
|
if (typeof this.settings.targetColumnName === 'undefined')
|
|
1119
1187
|
this.updateMutationCliffs();
|
|
1188
|
+
this.analysisView.grid.invalidate();
|
|
1120
1189
|
}
|
|
1121
1190
|
|
|
1122
1191
|
findViewer(viewerType: VIEWER_TYPE): DG.Viewer | null {
|
|
@@ -1132,11 +1201,12 @@ export class PeptidesModel {
|
|
|
1132
1201
|
await this.addMonomerPosition();
|
|
1133
1202
|
if (this.settings.showMostPotentResidues)
|
|
1134
1203
|
await this.addMostPotentResidues();
|
|
1204
|
+
logoSummaryTable.viewerGrid.invalidate();
|
|
1135
1205
|
}
|
|
1136
1206
|
|
|
1137
1207
|
async addMonomerPosition(): Promise<void> {
|
|
1138
1208
|
const monomerPosition = await this.df.plot.fromType(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition;
|
|
1139
|
-
const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as
|
|
1209
|
+
const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
|
|
1140
1210
|
const dm = this.analysisView.dockManager;
|
|
1141
1211
|
const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1142
1212
|
[DG.DOCK_TYPE.LEFT, this.findViewerNode(VIEWER_TYPE.MOST_POTENT_RESIDUES), 0.7];
|
|
@@ -1151,7 +1221,7 @@ export class PeptidesModel {
|
|
|
1151
1221
|
|
|
1152
1222
|
async addMostPotentResidues(): Promise<void> {
|
|
1153
1223
|
const mostPotentResidues =
|
|
1154
|
-
await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as
|
|
1224
|
+
await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues;
|
|
1155
1225
|
const monomerPosition = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
1156
1226
|
const dm = this.analysisView.dockManager;
|
|
1157
1227
|
const [dockType, refNode, ratio] = monomerPosition === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|