@datagrok/peptides 1.2.0 → 1.3.1
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/dist/package-test.js +682 -613
- package/dist/package.js +779 -617
- package/package.json +5 -1
- package/src/model.ts +423 -222
- package/src/package.ts +16 -15
- package/src/styles.css +1 -0
- package/src/tests/core.ts +10 -9
- package/src/tests/peptide-space-test.ts +32 -32
- package/src/utils/cell-renderer.ts +39 -59
- package/src/utils/constants.ts +9 -2
- package/src/utils/misc.ts +12 -17
- package/src/utils/peptide-similarity-space.ts +2 -1
- package/src/utils/types.ts +1 -1
- package/src/viewers/logo-summary.ts +42 -0
- package/src/viewers/peptide-space-viewer.ts +6 -4
- package/src/viewers/sar-viewer.ts +64 -13
- package/src/widgets/distribution.ts +26 -11
- package/src/widgets/mutation-cliffs.ts +3 -2
- package/src/widgets/{analyze-peptides.ts → peptides.ts} +56 -28
- package/test-Peptides-8408d9b6ee67-ca232121.html +246 -0
- package/src/utils/invariant-map.ts +0 -163
package/src/model.ts
CHANGED
|
@@ -9,31 +9,36 @@ import * as rxjs from 'rxjs';
|
|
|
9
9
|
import * as C from './utils/constants';
|
|
10
10
|
import * as type from './utils/types';
|
|
11
11
|
import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {renderBarchart, renderSARCell, setAARRenderer} from './utils/cell-renderer';
|
|
12
|
+
import {MutationCliffsViewer, SARViewerBase, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
13
|
+
import * as CR from './utils/cell-renderer';
|
|
15
14
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
16
15
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
17
16
|
import {getStats, Stats} from './utils/statistics';
|
|
17
|
+
import {LogoSummary} from './viewers/logo-summary';
|
|
18
18
|
|
|
19
19
|
export class PeptidesModel {
|
|
20
20
|
static modelName = 'peptidesModel';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
mutationCliffsGridSubject = new rxjs.Subject<DG.Grid>();
|
|
23
|
+
mostPotentResiduesGridSubject = new rxjs.Subject<DG.Grid>();
|
|
24
|
+
logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
|
|
24
25
|
|
|
25
26
|
_isUpdating: boolean = false;
|
|
26
27
|
isBitsetChangedInitialized = false;
|
|
27
28
|
isCellChanging = false;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
mutationCliffsGrid!: DG.Grid;
|
|
31
|
+
mostPotentResiduesGrid!: DG.Grid;
|
|
32
|
+
logoSummaryGrid!: DG.Grid;
|
|
33
|
+
sourceGrid!: DG.Grid;
|
|
32
34
|
df: DG.DataFrame;
|
|
33
35
|
splitCol!: DG.Column<boolean>;
|
|
34
36
|
edf: DG.DataFrame | null = null;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
monomerPositionStatsDf!: DG.DataFrame;
|
|
38
|
+
clusterStatsDf!: DG.DataFrame;
|
|
39
|
+
_mutationCliffsSelection: type.PositionToAARList = {};
|
|
40
|
+
_invariantMapSelection: type.PositionToAARList = {};
|
|
41
|
+
_logoSummarySelection: number[] = [];
|
|
37
42
|
substitutionsInfo: type.SubstitutionsInfo = new Map();
|
|
38
43
|
isInitialized = false;
|
|
39
44
|
currentView!: DG.TableView;
|
|
@@ -41,8 +46,8 @@ export class PeptidesModel {
|
|
|
41
46
|
isPeptideSpaceChangingBitset = false;
|
|
42
47
|
isChangingEdfBitset = false;
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
mutationCliffsViewer!: MutationCliffsViewer;
|
|
50
|
+
mostPotentResiduesViewer!: MostPotentResiduesViewer;
|
|
46
51
|
|
|
47
52
|
_usedProperties: {[propName: string]: string | number | boolean} = {};
|
|
48
53
|
monomerMap: {[key: string]: {molfile: string, fullName: string}} = {};
|
|
@@ -61,19 +66,49 @@ export class PeptidesModel {
|
|
|
61
66
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
get
|
|
69
|
+
get onMutationCliffsGridChanged(): rxjs.Observable<DG.Grid> {
|
|
70
|
+
return this.mutationCliffsGridSubject.asObservable();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get onMostPotentResiduesGridChanged(): rxjs.Observable<DG.Grid> {
|
|
74
|
+
return this.mostPotentResiduesGridSubject.asObservable();
|
|
75
|
+
}
|
|
65
76
|
|
|
66
|
-
get
|
|
77
|
+
get onLogoSummaryGridChanged(): rxjs.Observable<DG.Grid> {
|
|
78
|
+
return this.logoSummaryGridSubject.asObservable();
|
|
79
|
+
}
|
|
67
80
|
|
|
68
|
-
get
|
|
69
|
-
this.
|
|
70
|
-
return this.
|
|
81
|
+
get mutationCliffsSelection(): type.PositionToAARList {
|
|
82
|
+
this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
83
|
+
return this._mutationCliffsSelection;
|
|
71
84
|
}
|
|
72
|
-
set
|
|
73
|
-
this.
|
|
85
|
+
set mutationCliffsSelection(selection: type.PositionToAARList) {
|
|
86
|
+
this._mutationCliffsSelection = selection;
|
|
74
87
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
88
|
+
this.fireBitsetChanged();
|
|
89
|
+
this.invalidateGrids();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get invariantMapSelection(): type.PositionToAARList {
|
|
93
|
+
this._invariantMapSelection ??= JSON.parse(this.df.tags[C.TAGS.FILTER] || '{}');
|
|
94
|
+
return this._invariantMapSelection;
|
|
95
|
+
}
|
|
96
|
+
set invariantMapSelection(selection: type.PositionToAARList) {
|
|
97
|
+
this._invariantMapSelection = selection;
|
|
98
|
+
this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
|
|
99
|
+
this.df.filter.fireChanged();
|
|
100
|
+
this.invalidateGrids();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get logoSummarySelection(): number[] {
|
|
104
|
+
this._logoSummarySelection ??= JSON.parse(this.df.tags[C.TAGS.CLUSTER_SELECTION] || '[]');
|
|
105
|
+
return this._logoSummarySelection;
|
|
106
|
+
}
|
|
107
|
+
set logoSummarySelection(selection: number[]) {
|
|
108
|
+
this._logoSummarySelection = selection;
|
|
109
|
+
this.df.tags[C.TAGS.CLUSTER_SELECTION] = JSON.stringify(selection);
|
|
110
|
+
this.fireBitsetChanged();
|
|
111
|
+
this.invalidateGrids();
|
|
77
112
|
}
|
|
78
113
|
|
|
79
114
|
get usedProperties(): {[propName: string]: string | number | boolean} {
|
|
@@ -103,23 +138,25 @@ export class PeptidesModel {
|
|
|
103
138
|
this.df.tags['distributionSplit'] = `${splitByAARFlag}${flag ? 1 : 0}`;
|
|
104
139
|
}
|
|
105
140
|
|
|
106
|
-
|
|
107
|
-
this.
|
|
108
|
-
|
|
141
|
+
get isInvariantMap(): boolean {
|
|
142
|
+
return this.df.getTag('isInvariantMap') === '1';
|
|
143
|
+
}
|
|
144
|
+
set isInvariantMap(x: boolean) {
|
|
145
|
+
this.df.setTag('isInvariantMap', x ? '1' : '0');
|
|
109
146
|
}
|
|
110
147
|
|
|
111
148
|
createAccordion(): DG.Accordion {
|
|
112
149
|
const acc = ui.accordion();
|
|
113
150
|
acc.root.style.width = '100%';
|
|
114
151
|
acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
|
|
115
|
-
acc.addPane('
|
|
116
|
-
acc.addPane('
|
|
152
|
+
acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root, true);
|
|
153
|
+
acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root, true);
|
|
117
154
|
|
|
118
155
|
return acc;
|
|
119
156
|
}
|
|
120
157
|
|
|
121
158
|
getViewer(): SARViewerBase {
|
|
122
|
-
const viewer = this.
|
|
159
|
+
const viewer = this.mutationCliffsViewer ?? this.mostPotentResiduesViewer;
|
|
123
160
|
if (!viewer)
|
|
124
161
|
throw new Error('ViewerError: none of the SAR viewers is initialized');
|
|
125
162
|
return viewer;
|
|
@@ -145,28 +182,30 @@ export class PeptidesModel {
|
|
|
145
182
|
}
|
|
146
183
|
|
|
147
184
|
updateDefault(): void {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
if ((this.
|
|
185
|
+
const proprtyChanged =
|
|
186
|
+
this.isPropertyChanged(this.mutationCliffsViewer) || this.isPropertyChanged(this.mostPotentResiduesViewer);
|
|
187
|
+
if ((this.sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
151
188
|
this.isInitialized = true;
|
|
152
189
|
this._isUpdating = true;
|
|
153
|
-
|
|
190
|
+
this.initializeViewersComponents();
|
|
154
191
|
//FIXME: modify during the initializeViewersComponents stages
|
|
155
|
-
this.
|
|
156
|
-
this.
|
|
192
|
+
this.mutationCliffsGridSubject.next(this.mutationCliffsGrid);
|
|
193
|
+
this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
|
|
194
|
+
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
195
|
+
this.logoSummaryGridSubject.next(this.logoSummaryGrid);
|
|
157
196
|
|
|
158
|
-
this.
|
|
197
|
+
this.fireBitsetChanged();
|
|
198
|
+
this.invalidateGrids();
|
|
159
199
|
this._isUpdating = false;
|
|
160
200
|
}
|
|
161
201
|
}
|
|
162
202
|
|
|
163
|
-
initializeViewersComponents():
|
|
164
|
-
if (this.
|
|
203
|
+
initializeViewersComponents(): void {
|
|
204
|
+
if (this.sourceGrid === null)
|
|
165
205
|
throw new Error(`Source grid is not initialized`);
|
|
166
206
|
|
|
167
207
|
//Split the aligned sequence into separate AARs
|
|
168
|
-
const col
|
|
169
|
-
// const alphabet = col.tags[DG.TAGS.UNITS].split(':')[2];
|
|
208
|
+
const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
170
209
|
const alphabet = col.tags['alphabet'];
|
|
171
210
|
const splitSeqDf = splitAlignedSequences(col);
|
|
172
211
|
|
|
@@ -177,12 +216,7 @@ export class PeptidesModel {
|
|
|
177
216
|
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
178
217
|
splitSeqDf.columns.add(activityCol);
|
|
179
218
|
|
|
180
|
-
this.joinDataFrames(positionColumns, splitSeqDf);
|
|
181
|
-
|
|
182
|
-
for (const dfCol of this.df.columns) {
|
|
183
|
-
if (positionColumns.includes(dfCol.name))
|
|
184
|
-
setAARRenderer(dfCol, alphabet, this._sourceGrid);
|
|
185
|
-
}
|
|
219
|
+
this.joinDataFrames(positionColumns, splitSeqDf, alphabet);
|
|
186
220
|
|
|
187
221
|
this.sortSourceGrid();
|
|
188
222
|
|
|
@@ -196,11 +230,11 @@ export class PeptidesModel {
|
|
|
196
230
|
matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
|
|
197
231
|
|
|
198
232
|
//statistics for specific AAR at a specific position
|
|
199
|
-
this.
|
|
233
|
+
this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics(matrixDf);
|
|
200
234
|
|
|
201
235
|
// SAR matrix table
|
|
202
236
|
//pivot a table to make it matrix-like
|
|
203
|
-
matrixDf = this.
|
|
237
|
+
matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
204
238
|
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
205
239
|
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
206
240
|
.aggregate();
|
|
@@ -214,28 +248,31 @@ export class PeptidesModel {
|
|
|
214
248
|
|
|
215
249
|
this.calcSubstitutions();
|
|
216
250
|
|
|
217
|
-
|
|
251
|
+
[this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
|
|
252
|
+
this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
|
|
218
253
|
|
|
219
|
-
this.
|
|
220
|
-
|
|
254
|
+
if (this.df.getTag(C.TAGS.CLUSTERS)) {
|
|
255
|
+
this.clusterStatsDf = this.calculateClusterStatistics();
|
|
256
|
+
this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// init invariant map & mutation cliffs selections
|
|
260
|
+
this.initSelections(positionColumns);
|
|
221
261
|
|
|
222
262
|
positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
223
263
|
|
|
224
264
|
this.setBarChartInteraction();
|
|
225
265
|
|
|
226
|
-
this.setCellRenderers(positionColumns
|
|
266
|
+
this.setCellRenderers(positionColumns);
|
|
227
267
|
|
|
228
268
|
// show all the statistics in a tooltip over cell
|
|
229
|
-
this.setTooltips(positionColumns
|
|
269
|
+
this.setTooltips(positionColumns);
|
|
230
270
|
|
|
231
271
|
this.setInteractionCallback();
|
|
232
272
|
|
|
233
273
|
this.setBitsetCallback();
|
|
234
274
|
|
|
235
|
-
this.postProcessGrids(
|
|
236
|
-
|
|
237
|
-
//TODO: return class instead
|
|
238
|
-
return [sarGrid, sarVGrid];
|
|
275
|
+
this.postProcessGrids();
|
|
239
276
|
}
|
|
240
277
|
|
|
241
278
|
calcSubstitutions(): void {
|
|
@@ -323,13 +360,31 @@ export class PeptidesModel {
|
|
|
323
360
|
}
|
|
324
361
|
}
|
|
325
362
|
|
|
326
|
-
|
|
363
|
+
initSelections(positionColumns: string[]): void {
|
|
364
|
+
const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
|
|
365
|
+
const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
366
|
+
for (const pos of positionColumns) {
|
|
367
|
+
tempInvariantMapSelection[pos] ??= [];
|
|
368
|
+
mutationCliffsSelection[pos] ??= [];
|
|
369
|
+
}
|
|
370
|
+
this.invariantMapSelection = tempInvariantMapSelection;
|
|
371
|
+
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
372
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
|
|
327
376
|
// append splitSeqDf columns to source table and make sure columns are not added more than once
|
|
328
377
|
const name = this.df.name;
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
378
|
+
for (const colName of positionColumns) {
|
|
379
|
+
const col = this.df.col(colName);
|
|
380
|
+
const newCol = splitSeqDf.getCol(colName);
|
|
381
|
+
if (col === null)
|
|
382
|
+
this.df.columns.add(newCol);
|
|
383
|
+
else {
|
|
384
|
+
this.df.columns.remove(colName);
|
|
385
|
+
this.df.columns.add(newCol);
|
|
386
|
+
}
|
|
387
|
+
CR.setAARRenderer(newCol, alphabet, this.sourceGrid);
|
|
333
388
|
}
|
|
334
389
|
this.df.name = name;
|
|
335
390
|
this.currentView.name = name;
|
|
@@ -337,8 +392,8 @@ export class PeptidesModel {
|
|
|
337
392
|
|
|
338
393
|
sortSourceGrid(): void {
|
|
339
394
|
const colNames: DG.GridColumn[] = [];
|
|
340
|
-
for (let i = 1; i < this.
|
|
341
|
-
colNames.push(this.
|
|
395
|
+
for (let i = 1; i < this.sourceGrid.columns.length; i++)
|
|
396
|
+
colNames.push(this.sourceGrid.columns.byIndex(i)!);
|
|
342
397
|
|
|
343
398
|
colNames.sort((a, b)=>{
|
|
344
399
|
if (a.column!.semType == C.SEM_TYPES.MONOMER) {
|
|
@@ -350,28 +405,28 @@ export class PeptidesModel {
|
|
|
350
405
|
return 1;
|
|
351
406
|
return 0;
|
|
352
407
|
});
|
|
353
|
-
this.
|
|
408
|
+
this.sourceGrid.columns.setOrder(colNames.map((v) => v.name));
|
|
354
409
|
}
|
|
355
410
|
|
|
356
411
|
createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
|
|
357
|
-
const [scaledDf, newColName] =
|
|
358
|
-
scaleActivity(activityScaling, this.df
|
|
412
|
+
const [scaledDf, _, newColName] =
|
|
413
|
+
scaleActivity(activityScaling, this.df.getCol(C.COLUMNS_NAMES.ACTIVITY));
|
|
359
414
|
//TODO: make another func
|
|
360
415
|
const scaledCol = scaledDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
361
416
|
scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
|
|
362
417
|
splitSeqDf.columns.add(scaledCol);
|
|
363
418
|
const oldScaledCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
364
419
|
this.df.columns.replace(oldScaledCol, scaledCol);
|
|
365
|
-
const gridCol = this.
|
|
420
|
+
const gridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
366
421
|
if (gridCol !== null) {
|
|
367
422
|
gridCol.name = newColName;
|
|
368
423
|
this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
|
|
369
424
|
}
|
|
370
425
|
|
|
371
|
-
this.
|
|
426
|
+
this.sourceGrid.columns.setOrder([newColName]);
|
|
372
427
|
}
|
|
373
428
|
|
|
374
|
-
|
|
429
|
+
calculateMonomerPositionStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
375
430
|
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
376
431
|
|
|
377
432
|
//calculate p-values based on t-test
|
|
@@ -388,7 +443,7 @@ export class PeptidesModel {
|
|
|
388
443
|
for (let i = 0; i < matrixDf.rowCount; i++) {
|
|
389
444
|
const position: string = posCol.get(i);
|
|
390
445
|
const aar: string = aarCol.get(i);
|
|
391
|
-
const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j)
|
|
446
|
+
const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) === aar);
|
|
392
447
|
const stats = getStats(activityCol, mask);
|
|
393
448
|
|
|
394
449
|
mdCol.set(i, stats.meanDifference);
|
|
@@ -397,23 +452,43 @@ export class PeptidesModel {
|
|
|
397
452
|
ratioCol.set(i, stats.ratio);
|
|
398
453
|
}
|
|
399
454
|
|
|
400
|
-
const countThreshold = 4;
|
|
401
|
-
matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} >= ${countThreshold}`).toDataFrame();
|
|
402
|
-
matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} <= ${sourceDfLen - countThreshold}`).toDataFrame();
|
|
403
|
-
|
|
404
455
|
return matrixDf as DG.DataFrame;
|
|
405
456
|
}
|
|
406
457
|
|
|
458
|
+
calculateClusterStatistics(): DG.DataFrame {
|
|
459
|
+
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
460
|
+
const statsDf = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
461
|
+
const clustersCol = statsDf.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
462
|
+
const statsDfCols = statsDf.columns;
|
|
463
|
+
const mdCol= statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
464
|
+
const pValCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
|
|
465
|
+
const countCol = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT);
|
|
466
|
+
const ratioCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
|
|
467
|
+
const activityList: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
|
|
468
|
+
|
|
469
|
+
for (let rowIdx = 0; rowIdx < clustersCol.length; ++rowIdx) {
|
|
470
|
+
const cluster = clustersCol.get(rowIdx);
|
|
471
|
+
const mask = DG.BitSet.create(activityList.length, (bitIdx) => originalClustersCol.get(bitIdx) === cluster);
|
|
472
|
+
const stats = getStats(activityList, mask);
|
|
473
|
+
|
|
474
|
+
mdCol.set(rowIdx, stats.meanDifference);
|
|
475
|
+
pValCol.set(rowIdx, stats.pValue);
|
|
476
|
+
countCol.set(rowIdx, stats.count);
|
|
477
|
+
ratioCol.set(rowIdx, stats.ratio);
|
|
478
|
+
}
|
|
479
|
+
return statsDf;
|
|
480
|
+
}
|
|
481
|
+
|
|
407
482
|
setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
408
483
|
let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
|
|
409
484
|
if (this.getViewer().bidirectionalAnalysis) {
|
|
410
|
-
const mdCol = this.
|
|
485
|
+
const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
|
|
411
486
|
sortArgument = 'Absolute Mean difference';
|
|
412
|
-
const absMDCol = this.
|
|
487
|
+
const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
|
|
413
488
|
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
414
489
|
}
|
|
415
490
|
|
|
416
|
-
const aarWeightsDf = this.
|
|
491
|
+
const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
|
|
417
492
|
.aggregate();
|
|
418
493
|
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
419
494
|
const getWeight = (aar: string): number => aarWeightsDf
|
|
@@ -430,7 +505,7 @@ export class PeptidesModel {
|
|
|
430
505
|
// TODO: aquire ALL of the positions
|
|
431
506
|
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
|
|
432
507
|
'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
|
|
433
|
-
let sequenceDf = this.
|
|
508
|
+
let sequenceDf = this.monomerPositionStatsDf.groupBy(columns)
|
|
434
509
|
.where('pValue <= 0.1')
|
|
435
510
|
.aggregate();
|
|
436
511
|
|
|
@@ -451,32 +526,125 @@ export class PeptidesModel {
|
|
|
451
526
|
return sequenceDf;
|
|
452
527
|
}
|
|
453
528
|
|
|
454
|
-
createGrids(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const
|
|
529
|
+
createGrids(mutationCliffsDf: DG.DataFrame, mostPotentResiduesDf: DG.DataFrame, positionColumns: string[],
|
|
530
|
+
alphabet: string): [DG.Grid, DG.Grid] {
|
|
531
|
+
// Creating Mutation Cliffs grid and sorting columns
|
|
532
|
+
const mutationCliffsGrid = mutationCliffsDf.plot.grid();
|
|
533
|
+
mutationCliffsGrid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
534
|
+
mutationCliffsGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
|
|
535
|
+
|
|
536
|
+
// Creating Monomer-Position grid, sorting and setting column format
|
|
537
|
+
const mostPotentResiduesGrid = mostPotentResiduesDf.plot.grid();
|
|
538
|
+
mostPotentResiduesGrid.sort([C.COLUMNS_NAMES.POSITION]);
|
|
539
|
+
const pValGridCol = mostPotentResiduesGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
|
|
463
540
|
pValGridCol.format = '#.000';
|
|
464
541
|
pValGridCol.name = 'P-value';
|
|
465
542
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
543
|
+
// Setting Monomer column renderer
|
|
544
|
+
CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
545
|
+
CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
546
|
+
|
|
547
|
+
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
createLogoSummaryGrid(): DG.Grid {
|
|
551
|
+
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
552
|
+
const summaryTableLength = summaryTable.rowCount;
|
|
553
|
+
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
554
|
+
const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
|
|
555
|
+
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
556
|
+
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
557
|
+
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
558
|
+
const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
559
|
+
|
|
560
|
+
for (let index = 0; index < summaryTableLength; ++index) {
|
|
561
|
+
const indexes: number[] = [];
|
|
562
|
+
for (let j = 0; j < originalClustersCol.length; ++j) {
|
|
563
|
+
if (originalClustersCol.get(j) === clustersCol.get(index))
|
|
564
|
+
indexes.push(j);
|
|
565
|
+
}
|
|
566
|
+
const tCol = DG.Column.string('peptides', indexes.length);
|
|
567
|
+
tCol.init((i) => peptideCol.get(indexes[i]));
|
|
568
|
+
|
|
569
|
+
for (const tag of peptideCol.tags)
|
|
570
|
+
tCol.setTag(tag[0], tag[1]);
|
|
571
|
+
|
|
572
|
+
const dfSlice = DG.DataFrame.fromColumns([tCol]);
|
|
573
|
+
tempDfList[index] = dfSlice;
|
|
574
|
+
webLogoCol.set(index, index.toString());
|
|
575
|
+
membersCol.set(index, dfSlice.rowCount);
|
|
576
|
+
}
|
|
577
|
+
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
578
|
+
|
|
579
|
+
const grid = summaryTable.plot.grid();
|
|
580
|
+
const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
|
|
581
|
+
gridClustersCol.name = 'Clusters';
|
|
582
|
+
gridClustersCol.visible = true;
|
|
583
|
+
grid.columns.rowHeader!.visible = false;
|
|
584
|
+
grid.props.rowHeight = 55;
|
|
585
|
+
grid.onCellPrepare((cell) => {
|
|
586
|
+
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
|
|
587
|
+
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
|
|
588
|
+
.then((viewer) => cell.element = viewer.root);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
grid.root.addEventListener('click', (ev) => {
|
|
592
|
+
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
593
|
+
if (!cell || !cell.isTableCell)
|
|
594
|
+
return;
|
|
595
|
+
|
|
596
|
+
const cluster = clustersCol.get(cell.tableRowIndex!)!;
|
|
597
|
+
summaryTable.currentRowIdx = -1;
|
|
598
|
+
if (ev.shiftKey)
|
|
599
|
+
this.modifyClusterSelection(cluster);
|
|
600
|
+
else
|
|
601
|
+
this.initClusterSelection(cluster);
|
|
602
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
603
|
+
});
|
|
604
|
+
grid.onCellRender.subscribe((gridCellArgs) => {
|
|
605
|
+
const gc = gridCellArgs.cell;
|
|
606
|
+
if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
|
|
607
|
+
return;
|
|
608
|
+
const canvasContext = gridCellArgs.g;
|
|
609
|
+
const bound = gridCellArgs.bounds;
|
|
610
|
+
canvasContext.save();
|
|
611
|
+
canvasContext.beginPath();
|
|
612
|
+
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
613
|
+
canvasContext.clip();
|
|
614
|
+
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
|
|
615
|
+
gridCellArgs.preventDefault();
|
|
616
|
+
canvasContext.restore();
|
|
617
|
+
});
|
|
618
|
+
grid.onCellTooltip((cell, x, y) => {
|
|
619
|
+
if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
|
|
620
|
+
this.showTooltipCluster(cell.cell.value, x, y);
|
|
621
|
+
return true;
|
|
622
|
+
});
|
|
623
|
+
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
624
|
+
webLogoGridCol.cellType = 'html';
|
|
625
|
+
webLogoGridCol.width = 350;
|
|
626
|
+
|
|
627
|
+
return grid;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
modifyClusterSelection(cluster: number): void {
|
|
631
|
+
const tempSelection = this.logoSummarySelection;
|
|
632
|
+
const idx = tempSelection.indexOf(cluster);
|
|
633
|
+
if (idx !== -1)
|
|
634
|
+
tempSelection.splice(idx, 1);
|
|
635
|
+
else
|
|
636
|
+
tempSelection.push(cluster);
|
|
469
637
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
setAARRenderer(tempCol, alphabet, sarGrid);
|
|
638
|
+
this.logoSummarySelection = tempSelection;
|
|
639
|
+
}
|
|
473
640
|
|
|
474
|
-
|
|
641
|
+
initClusterSelection(cluster: number): void {
|
|
642
|
+
this.logoSummarySelection = [cluster];
|
|
475
643
|
}
|
|
476
644
|
|
|
477
645
|
setBarChartInteraction(): void {
|
|
478
646
|
const eventAction = (ev: MouseEvent): void => {
|
|
479
|
-
const cell = this.
|
|
647
|
+
const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
480
648
|
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
|
|
481
649
|
const newBarPart = this.findAARandPosition(cell, ev);
|
|
482
650
|
this.requestBarchartAction(ev, newBarPart);
|
|
@@ -484,9 +652,9 @@ export class PeptidesModel {
|
|
|
484
652
|
};
|
|
485
653
|
|
|
486
654
|
// The following events makes the barchart interactive
|
|
487
|
-
rxjs.fromEvent<MouseEvent>(this.
|
|
655
|
+
rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
|
|
488
656
|
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
489
|
-
rxjs.fromEvent<MouseEvent>(this.
|
|
657
|
+
rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
|
|
490
658
|
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
491
659
|
}
|
|
492
660
|
|
|
@@ -508,8 +676,9 @@ export class PeptidesModel {
|
|
|
508
676
|
const monomer = barPart.monomer;
|
|
509
677
|
const position = barPart.position;
|
|
510
678
|
if (ev.type === 'click') {
|
|
511
|
-
ev.shiftKey ? this.
|
|
512
|
-
this.
|
|
679
|
+
ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
|
|
680
|
+
this.initMonomerPositionSelection(monomer, position, true);
|
|
681
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
513
682
|
} else {
|
|
514
683
|
const bar = `${monomer}:${position}`;
|
|
515
684
|
if (this.cachedBarchartTooltip.bar == bar)
|
|
@@ -519,8 +688,8 @@ export class PeptidesModel {
|
|
|
519
688
|
}
|
|
520
689
|
}
|
|
521
690
|
|
|
522
|
-
setCellRenderers(renderColNames: string[]
|
|
523
|
-
const mdCol = this.
|
|
691
|
+
setCellRenderers(renderColNames: string[]): void {
|
|
692
|
+
const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
524
693
|
//decompose into two different renering funcs
|
|
525
694
|
const renderCell = (args: DG.GridCellRenderArgs): void => {
|
|
526
695
|
const canvasContext = args.g;
|
|
@@ -551,18 +720,28 @@ export class PeptidesModel {
|
|
|
551
720
|
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
552
721
|
|
|
553
722
|
const viewer = this.getViewer();
|
|
554
|
-
|
|
555
|
-
|
|
723
|
+
if (this.isInvariantMap) {
|
|
724
|
+
const value: number = this.monomerPositionStatsDf
|
|
725
|
+
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
|
|
726
|
+
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
727
|
+
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
728
|
+
CR.renderInvaraintMapCell(
|
|
729
|
+
canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
|
|
730
|
+
} else {
|
|
731
|
+
CR.renderMutationCliffCell(
|
|
732
|
+
canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf, viewer.bidirectionalAnalysis,
|
|
733
|
+
mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
|
|
734
|
+
}
|
|
556
735
|
}
|
|
557
736
|
args.preventDefault();
|
|
558
737
|
}
|
|
559
738
|
canvasContext.restore();
|
|
560
739
|
};
|
|
561
|
-
|
|
562
|
-
|
|
740
|
+
this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
|
|
741
|
+
this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
|
|
563
742
|
|
|
564
|
-
this.
|
|
565
|
-
this.
|
|
743
|
+
this.sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
744
|
+
this.sourceGrid.onCellRender.subscribe((gcArgs) => {
|
|
566
745
|
const context = gcArgs.g;
|
|
567
746
|
const bounds = gcArgs.bounds;
|
|
568
747
|
const col = gcArgs.cell.tableColumn;
|
|
@@ -573,7 +752,7 @@ export class PeptidesModel {
|
|
|
573
752
|
context.clip();
|
|
574
753
|
|
|
575
754
|
if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
|
|
576
|
-
const barBounds = renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
|
|
755
|
+
const barBounds = CR.renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
|
|
577
756
|
this.barsBounds[col.name] = barBounds;
|
|
578
757
|
gcArgs.preventDefault();
|
|
579
758
|
}
|
|
@@ -582,7 +761,7 @@ export class PeptidesModel {
|
|
|
582
761
|
});
|
|
583
762
|
}
|
|
584
763
|
|
|
585
|
-
setTooltips(renderColNames: string[]
|
|
764
|
+
setTooltips(renderColNames: string[]): void {
|
|
586
765
|
const showTooltip = (cell: DG.GridCell, x: number, y: number): boolean => {
|
|
587
766
|
const tableCol = cell.tableColumn;
|
|
588
767
|
const tableColName = tableCol?.name;
|
|
@@ -604,9 +783,9 @@ export class PeptidesModel {
|
|
|
604
783
|
return true;
|
|
605
784
|
};
|
|
606
785
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.
|
|
786
|
+
this.mutationCliffsGrid.onCellTooltip(showTooltip);
|
|
787
|
+
this.mostPotentResiduesGrid.onCellTooltip(showTooltip);
|
|
788
|
+
this.sourceGrid.onCellTooltip((cell, x, y) => {
|
|
610
789
|
const col = cell.tableColumn;
|
|
611
790
|
const cellValue = cell.cell.value;
|
|
612
791
|
if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
|
|
@@ -617,8 +796,6 @@ export class PeptidesModel {
|
|
|
617
796
|
|
|
618
797
|
showMonomerTooltip(aar: string, x: number, y: number): void {
|
|
619
798
|
const tooltipElements: HTMLDivElement[] = [];
|
|
620
|
-
//@ts-ignore: no types for org
|
|
621
|
-
// const monomer: type.HELMMonomer = org.helm.webeditor.monomers.getMonomer('HELM_AA', aar);
|
|
622
799
|
const monomer: type.HELMMonomer = this.monomerLib[aar.toLowerCase()];
|
|
623
800
|
|
|
624
801
|
if (monomer) {
|
|
@@ -632,8 +809,9 @@ export class PeptidesModel {
|
|
|
632
809
|
}
|
|
633
810
|
|
|
634
811
|
showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
|
|
635
|
-
const currentStatsDf = this.
|
|
812
|
+
const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
|
|
636
813
|
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
814
|
+
//TODO: use bitset instead of splitCol
|
|
637
815
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
638
816
|
const currentPosCol = this.df.getCol(position);
|
|
639
817
|
splitCol.init((i) => currentPosCol.get(i) == aar);
|
|
@@ -654,32 +832,60 @@ export class PeptidesModel {
|
|
|
654
832
|
return tooltip;
|
|
655
833
|
}
|
|
656
834
|
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
const
|
|
835
|
+
showTooltipCluster(cluster: number, x: number, y: number): HTMLDivElement | null {
|
|
836
|
+
const currentStatsDf = this.clusterStatsDf.rows.match({clusters: cluster}).toDataFrame();
|
|
837
|
+
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
838
|
+
//TODO: use bitset instead of splitCol
|
|
839
|
+
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
840
|
+
const currentClusterCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
841
|
+
splitCol.init((i) => currentClusterCol.get(i) == cluster);
|
|
842
|
+
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
843
|
+
const stats: Stats = {
|
|
844
|
+
count: currentStatsDf.get(C.COLUMNS_NAMES.COUNT, 0),
|
|
845
|
+
ratio: currentStatsDf.get(C.COLUMNS_NAMES.RATIO, 0),
|
|
846
|
+
pValue: currentStatsDf.get(C.COLUMNS_NAMES.P_VALUE, 0),
|
|
847
|
+
meanDifference: currentStatsDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, 0),
|
|
848
|
+
};
|
|
849
|
+
if (!stats.count)
|
|
850
|
+
return null;
|
|
851
|
+
|
|
852
|
+
const tooltip = getDistributionAndStats(distributionTable, stats, `Cluster: ${cluster}`, 'Other', true);
|
|
853
|
+
|
|
854
|
+
ui.tooltip.show(tooltip, x, y);
|
|
660
855
|
|
|
661
|
-
|
|
662
|
-
|
|
856
|
+
return tooltip;
|
|
857
|
+
}
|
|
663
858
|
|
|
664
|
-
|
|
665
|
-
|
|
859
|
+
setInteractionCallback(): void {
|
|
860
|
+
const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
|
|
861
|
+
const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
|
|
862
|
+
|
|
863
|
+
const chooseAction =
|
|
864
|
+
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
|
|
865
|
+
isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
|
|
866
|
+
this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
|
|
867
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
871
|
+
const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
666
872
|
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
|
|
667
873
|
return;
|
|
668
874
|
|
|
669
875
|
const position = gridCell!.tableColumn!.name;
|
|
670
|
-
const aar =
|
|
671
|
-
chooseAction(aar, position, ev.shiftKey);
|
|
876
|
+
const aar = mutationCliffsDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
|
|
877
|
+
chooseAction(aar, position, ev.shiftKey, this.isInvariantMap);
|
|
672
878
|
});
|
|
673
879
|
|
|
674
|
-
this.
|
|
675
|
-
const gridCell = this.
|
|
880
|
+
this.mostPotentResiduesGrid.root.addEventListener('click', (ev) => {
|
|
881
|
+
const gridCell = this.mostPotentResiduesGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
676
882
|
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
677
883
|
return;
|
|
678
884
|
|
|
679
885
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
680
|
-
const position =
|
|
681
|
-
const aar =
|
|
682
|
-
chooseAction(aar, position, ev.shiftKey);
|
|
886
|
+
const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
887
|
+
const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
888
|
+
chooseAction(aar, position, ev.shiftKey, false);
|
|
683
889
|
});
|
|
684
890
|
|
|
685
891
|
const cellChanged = (table: DG.DataFrame): void => {
|
|
@@ -689,45 +895,50 @@ export class PeptidesModel {
|
|
|
689
895
|
table.currentRowIdx = -1;
|
|
690
896
|
this.isCellChanging = false;
|
|
691
897
|
};
|
|
692
|
-
this.
|
|
693
|
-
this.
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
modifyCurrentSelection(aar: string, position: string): void {
|
|
697
|
-
const tempSelection = this.currentSelection;
|
|
698
|
-
if (!tempSelection.hasOwnProperty(position))
|
|
699
|
-
tempSelection[position] = [aar];
|
|
700
|
-
else {
|
|
701
|
-
const tempSelectionAt = tempSelection[position];
|
|
702
|
-
const aarIndex = tempSelectionAt.indexOf(aar);
|
|
703
|
-
aarIndex == -1 ? tempSelectionAt.push(aar) :
|
|
704
|
-
tempSelectionAt.length == 1 ? delete tempSelection[position] :
|
|
705
|
-
tempSelectionAt.splice(aarIndex, 1);
|
|
706
|
-
}
|
|
898
|
+
this.mutationCliffsGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mutationCliffsDf));
|
|
899
|
+
this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
|
|
900
|
+
}
|
|
707
901
|
|
|
708
|
-
|
|
902
|
+
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
903
|
+
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
904
|
+
const tempSelectionAt = tempSelection[position];
|
|
905
|
+
const aarIndex = tempSelectionAt.indexOf(aar);
|
|
906
|
+
if (aarIndex === -1)
|
|
907
|
+
tempSelectionAt.push(aar);
|
|
908
|
+
else
|
|
909
|
+
tempSelectionAt.splice(aarIndex, 1);
|
|
910
|
+
|
|
911
|
+
if (isInvariantMapSelection)
|
|
912
|
+
this.invariantMapSelection = tempSelection;
|
|
913
|
+
else
|
|
914
|
+
this.mutationCliffsSelection = tempSelection;
|
|
709
915
|
}
|
|
710
916
|
|
|
711
|
-
|
|
712
|
-
const tempSelection
|
|
917
|
+
initMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
918
|
+
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
713
919
|
tempSelection[position] = [aar];
|
|
714
|
-
|
|
920
|
+
|
|
921
|
+
if (isInvariantMapSelection)
|
|
922
|
+
this.invariantMapSelection = tempSelection;
|
|
923
|
+
else
|
|
924
|
+
this.mutationCliffsSelection = tempSelection;
|
|
715
925
|
}
|
|
716
926
|
|
|
717
927
|
invalidateGrids(): void {
|
|
718
|
-
|
|
719
|
-
this.
|
|
720
|
-
this.
|
|
721
|
-
this.
|
|
722
|
-
//TODO: this.peptideSpaceGrid.invalidate();
|
|
928
|
+
this.mutationCliffsGrid.invalidate();
|
|
929
|
+
this.mostPotentResiduesGrid.invalidate();
|
|
930
|
+
this.logoSummaryGrid?.invalidate();
|
|
931
|
+
this.sourceGrid?.invalidate();
|
|
723
932
|
}
|
|
724
933
|
|
|
725
934
|
setBitsetCallback(): void {
|
|
726
935
|
if (this.isBitsetChangedInitialized)
|
|
727
936
|
return;
|
|
728
937
|
const selection = this.df.selection;
|
|
938
|
+
const filter = this.df.filter;
|
|
939
|
+
const clusterCol = this.df.col(C.COLUMNS_NAMES.CLUSTERS);
|
|
729
940
|
|
|
730
|
-
const
|
|
941
|
+
const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
|
|
731
942
|
const edfSelection = this.edf?.selection;
|
|
732
943
|
if (this.isPeptideSpaceChangingBitset) {
|
|
733
944
|
if (edfSelection == null)
|
|
@@ -743,20 +954,17 @@ export class PeptidesModel {
|
|
|
743
954
|
this.isChangingEdfBitset = false;
|
|
744
955
|
};
|
|
745
956
|
|
|
746
|
-
const positionList = Object.keys(this.
|
|
747
|
-
if (positionList.length == 0) {
|
|
748
|
-
currentBitset.init(() => false, false);
|
|
749
|
-
updateEdfSelection();
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
957
|
+
const positionList = Object.keys(this.mutationCliffsSelection);
|
|
752
958
|
|
|
753
959
|
//TODO: move out
|
|
754
960
|
const getBitAt = (i: number): boolean => {
|
|
755
961
|
for (const position of positionList) {
|
|
756
962
|
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
757
|
-
if (this.
|
|
963
|
+
if (this._mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
758
964
|
return true;
|
|
759
965
|
}
|
|
966
|
+
if (this._logoSummarySelection.includes(clusterCol?.get(i)!))
|
|
967
|
+
return true;
|
|
760
968
|
return false;
|
|
761
969
|
};
|
|
762
970
|
currentBitset.init(getBitAt, false);
|
|
@@ -764,7 +972,18 @@ export class PeptidesModel {
|
|
|
764
972
|
updateEdfSelection();
|
|
765
973
|
};
|
|
766
974
|
|
|
767
|
-
selection.onChanged.subscribe(() =>
|
|
975
|
+
selection.onChanged.subscribe(() => changeSelectionBitset(selection));
|
|
976
|
+
filter.onChanged.subscribe(() => {
|
|
977
|
+
const positionList = Object.keys(this.invariantMapSelection);
|
|
978
|
+
filter.init((i) => {
|
|
979
|
+
let result = true;
|
|
980
|
+
for (const position of positionList) {
|
|
981
|
+
const aarList = this._invariantMapSelection[position];
|
|
982
|
+
result &&= aarList.length == 0 || aarList.includes(this.df.get(position, i));
|
|
983
|
+
}
|
|
984
|
+
return result;
|
|
985
|
+
}, false);
|
|
986
|
+
});
|
|
768
987
|
this.isBitsetChangedInitialized = true;
|
|
769
988
|
}
|
|
770
989
|
|
|
@@ -776,11 +995,11 @@ export class PeptidesModel {
|
|
|
776
995
|
this.isPeptideSpaceChangingBitset = false;
|
|
777
996
|
}
|
|
778
997
|
|
|
779
|
-
postProcessGrids(
|
|
780
|
-
const mdCol: DG.GridColumn =
|
|
998
|
+
postProcessGrids(): void {
|
|
999
|
+
const mdCol: DG.GridColumn = this.mostPotentResiduesGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
781
1000
|
mdCol.name = 'Diff';
|
|
782
1001
|
|
|
783
|
-
for (const grid of [
|
|
1002
|
+
for (const grid of [this.mutationCliffsGrid, this.mostPotentResiduesGrid]) {
|
|
784
1003
|
const gridProps = grid.props;
|
|
785
1004
|
gridProps.rowHeight = 20;
|
|
786
1005
|
const girdCols = grid.columns;
|
|
@@ -788,21 +1007,24 @@ export class PeptidesModel {
|
|
|
788
1007
|
for (let i = 0; i < colNum; ++i) {
|
|
789
1008
|
const col = girdCols.byIndex(i)!;
|
|
790
1009
|
const colName = col.name;
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
col.width = gridProps.rowHeight + 10;
|
|
1010
|
+
col.width =
|
|
1011
|
+
grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER ? 50 :
|
|
1012
|
+
gridProps.rowHeight + 10;
|
|
795
1013
|
}
|
|
796
1014
|
}
|
|
797
1015
|
|
|
798
1016
|
const setViewerGridProps = (grid: DG.Grid): void => {
|
|
799
|
-
grid.props
|
|
800
|
-
|
|
801
|
-
|
|
1017
|
+
const gridProps = grid.props;
|
|
1018
|
+
gridProps.allowEdit = false;
|
|
1019
|
+
gridProps.allowRowSelection = false;
|
|
1020
|
+
gridProps.allowBlockSelection = false;
|
|
1021
|
+
gridProps.allowColSelection = false;
|
|
802
1022
|
};
|
|
803
1023
|
|
|
804
|
-
setViewerGridProps(
|
|
805
|
-
setViewerGridProps(
|
|
1024
|
+
setViewerGridProps(this.mutationCliffsGrid);
|
|
1025
|
+
setViewerGridProps(this.mostPotentResiduesGrid);
|
|
1026
|
+
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
1027
|
+
setViewerGridProps(this.logoSummaryGrid);
|
|
806
1028
|
}
|
|
807
1029
|
|
|
808
1030
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
@@ -819,9 +1041,9 @@ export class PeptidesModel {
|
|
|
819
1041
|
}
|
|
820
1042
|
|
|
821
1043
|
syncProperties(isSourceSAR = true): void {
|
|
822
|
-
if (this.
|
|
823
|
-
const [sourceViewer, targetViewer] = isSourceSAR ? [this.
|
|
824
|
-
[this.
|
|
1044
|
+
if (this.mutationCliffsViewer && this.mostPotentResiduesViewer) {
|
|
1045
|
+
const [sourceViewer, targetViewer] = isSourceSAR ? [this.mutationCliffsViewer, this.mostPotentResiduesViewer] :
|
|
1046
|
+
[this.mostPotentResiduesViewer, this.mutationCliffsViewer];
|
|
825
1047
|
const properties = sourceViewer.props.getProperties();
|
|
826
1048
|
const newProps: {[propName: string]: string | number | boolean} = {};
|
|
827
1049
|
for (const property of properties) {
|
|
@@ -845,14 +1067,16 @@ export class PeptidesModel {
|
|
|
845
1067
|
|
|
846
1068
|
this.currentView = this.df.tags[C.PEPTIDES_ANALYSIS] == 'true' ? grok.shell.v as DG.TableView :
|
|
847
1069
|
grok.shell.addTableView(this.df);
|
|
848
|
-
this.
|
|
1070
|
+
this.sourceGrid = this.currentView.grid;
|
|
849
1071
|
if (this.df.tags[C.PEPTIDES_ANALYSIS] == 'true')
|
|
850
1072
|
return;
|
|
851
1073
|
|
|
852
1074
|
this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
853
|
-
this.
|
|
854
|
-
|
|
855
|
-
|
|
1075
|
+
const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
|
|
1076
|
+
scaledGridCol.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
|
|
1077
|
+
scaledGridCol.format = '#.000';
|
|
1078
|
+
this.sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
|
|
1079
|
+
this.sourceGrid.props.allowColSelection = false;
|
|
856
1080
|
|
|
857
1081
|
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
858
1082
|
const adjustCellSize = (grid: DG.Grid): void => {
|
|
@@ -864,63 +1088,40 @@ export class PeptidesModel {
|
|
|
864
1088
|
grid.props.rowHeight = 20;
|
|
865
1089
|
};
|
|
866
1090
|
|
|
867
|
-
for (let i = 0; i < this.
|
|
868
|
-
const
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
aarCol.visible = false;
|
|
1091
|
+
for (let i = 0; i < this.sourceGrid.columns.length; i++) {
|
|
1092
|
+
const currentCol = this.sourceGrid.columns.byIndex(i);
|
|
1093
|
+
if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
|
|
1094
|
+
currentCol.visible = false;
|
|
872
1095
|
}
|
|
873
1096
|
|
|
874
1097
|
const options = {scaling: this.df.tags['scaling']};
|
|
875
1098
|
|
|
876
1099
|
const dockManager = this.currentView.dockManager;
|
|
877
1100
|
|
|
878
|
-
this.
|
|
879
|
-
|
|
880
|
-
this.sarViewerVertical =
|
|
881
|
-
await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as SARViewerVertical;
|
|
1101
|
+
this.mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
|
|
882
1102
|
|
|
883
|
-
|
|
1103
|
+
this.mostPotentResiduesViewer =
|
|
1104
|
+
await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
|
|
884
1105
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
// await this.df.plot.fromType('peptide-space-viewer', peptideSpaceViewerOptions) as PeptideSpaceViewer;
|
|
890
|
-
// dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.RIGHT, null, 'Peptide Space Viewer');
|
|
891
|
-
// }
|
|
1106
|
+
if (this.df.getTag(C.TAGS.CLUSTERS)) {
|
|
1107
|
+
const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
|
|
1108
|
+
dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
1109
|
+
}
|
|
892
1110
|
|
|
893
1111
|
this.updateDefault();
|
|
894
1112
|
|
|
895
|
-
|
|
1113
|
+
const mcNode =
|
|
1114
|
+
dockManager.dock(this.mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, this.mutationCliffsViewer.name);
|
|
1115
|
+
|
|
1116
|
+
dockManager.dock(
|
|
1117
|
+
this.mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, this.mostPotentResiduesViewer.name, 0.3);
|
|
896
1118
|
|
|
897
|
-
dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
|
|
898
1119
|
|
|
899
|
-
this.
|
|
900
|
-
adjustCellSize(this.
|
|
1120
|
+
this.sourceGrid.props.allowEdit = false;
|
|
1121
|
+
adjustCellSize(this.sourceGrid);
|
|
901
1122
|
|
|
902
1123
|
this.invalidateGrids();
|
|
903
1124
|
}
|
|
904
1125
|
|
|
905
|
-
invalidateSourceGrid(): void {this.
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
type viewerTypes = SARViewer | SARViewerVertical;
|
|
909
|
-
|
|
910
|
-
function dockViewers(
|
|
911
|
-
viewerList: viewerTypes[], attachDirection: DG.DockType, dockManager: DG.DockManager,
|
|
912
|
-
initialAttachDirection?: DG.DockType): DG.DockNode[] | null {
|
|
913
|
-
const viewerListLength = viewerList.length;
|
|
914
|
-
if (viewerListLength === 0)
|
|
915
|
-
return null;
|
|
916
|
-
|
|
917
|
-
let currentViewer = viewerList[0];
|
|
918
|
-
const nodeList = [dockManager.dock(currentViewer, initialAttachDirection, null, currentViewer.name ?? '')];
|
|
919
|
-
const ratio = 1 / viewerListLength;
|
|
920
|
-
|
|
921
|
-
for (let i = 1; i < viewerListLength; i++) {
|
|
922
|
-
currentViewer = viewerList[i];
|
|
923
|
-
nodeList.push(dockManager.dock(currentViewer, attachDirection, nodeList[i - 1], currentViewer.name ?? '', ratio));
|
|
924
|
-
}
|
|
925
|
-
return nodeList;
|
|
1126
|
+
invalidateSourceGrid(): void {this.sourceGrid.invalidate();}
|
|
926
1127
|
}
|