@datagrok/peptides 1.3.8 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-test.js +815 -432
- package/dist/package.js +779 -394
- package/package.json +2 -2
- package/src/model.ts +154 -248
- package/src/package.ts +4 -4
- package/src/tests/algorithms.ts +51 -0
- package/src/tests/core.ts +10 -10
- package/src/utils/algorithms.ts +91 -0
- package/src/utils/cell-renderer.ts +48 -81
- package/src/utils/misc.ts +14 -22
- package/src/utils/statistics.ts +2 -3
- package/src/utils/types.ts +24 -8
- package/src/viewers/logo-summary.ts +117 -5
- package/src/viewers/sar-viewer.ts +1 -1
- package/src/widgets/manual-alignment.ts +2 -2
- package/src/widgets/peptides.ts +3 -2
- package/src/widgets/settings.ts +36 -6
- package/test-Peptides-62cc009524f3-d4fc804f.html +276 -0
package/src/model.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
|
-
import * as bio from '@datagrok-libraries/bio';
|
|
5
4
|
|
|
6
5
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
7
6
|
|
|
@@ -10,15 +9,17 @@ import * as rxjs from 'rxjs';
|
|
|
10
9
|
|
|
11
10
|
import * as C from './utils/constants';
|
|
12
11
|
import * as type from './utils/types';
|
|
13
|
-
import {
|
|
14
|
-
import {MutationCliffsViewer,
|
|
12
|
+
import {calculateSelected, isGridCellInvalid, scaleActivity} from './utils/misc';
|
|
13
|
+
import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
15
14
|
import * as CR from './utils/cell-renderer';
|
|
16
15
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
17
16
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
18
17
|
import {getStats, Stats} from './utils/statistics';
|
|
19
18
|
import {LogoSummary} from './viewers/logo-summary';
|
|
20
19
|
import {getSettingsDialog} from './widgets/settings';
|
|
21
|
-
import {
|
|
20
|
+
import {getMonomerWorks} from './package';
|
|
21
|
+
import * as bio from '@datagrok-libraries/bio';
|
|
22
|
+
import {findMutations} from './utils/algorithms';
|
|
22
23
|
|
|
23
24
|
export class PeptidesModel {
|
|
24
25
|
static modelName = 'peptidesModel';
|
|
@@ -26,6 +27,7 @@ export class PeptidesModel {
|
|
|
26
27
|
mutationCliffsGridSubject = new rxjs.Subject<DG.Grid>();
|
|
27
28
|
mostPotentResiduesGridSubject = new rxjs.Subject<DG.Grid>();
|
|
28
29
|
logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
|
|
30
|
+
settingsSubject = new rxjs.Subject();
|
|
29
31
|
|
|
30
32
|
_isUpdating: boolean = false;
|
|
31
33
|
isBitsetChangedInitialized = false;
|
|
@@ -33,7 +35,7 @@ export class PeptidesModel {
|
|
|
33
35
|
|
|
34
36
|
mutationCliffsGrid!: DG.Grid;
|
|
35
37
|
mostPotentResiduesGrid!: DG.Grid;
|
|
36
|
-
logoSummaryGrid!: DG.Grid;
|
|
38
|
+
// logoSummaryGrid!: DG.Grid;
|
|
37
39
|
sourceGrid!: DG.Grid;
|
|
38
40
|
df: DG.DataFrame;
|
|
39
41
|
splitCol!: DG.Column<boolean>;
|
|
@@ -51,15 +53,23 @@ export class PeptidesModel {
|
|
|
51
53
|
isChangingEdfBitset = false;
|
|
52
54
|
|
|
53
55
|
monomerMap: { [key: string]: { molfile: string, fullName: string } } = {};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
cachedBarchartTooltip: { bar: string, tooltip: null | HTMLDivElement } = {bar: '', tooltip: null};
|
|
56
|
+
monomerLib: bio.IMonomerLib | null = null; // To get monomers from lib(s)
|
|
57
|
+
monomerWorks: bio.MonomerWorks | null = null; // To get processed monomers
|
|
57
58
|
|
|
58
59
|
_settings!: type.PeptidesSettings;
|
|
59
60
|
isRibbonSet = false;
|
|
60
61
|
|
|
62
|
+
cp: bio.SeqPalette;
|
|
63
|
+
initBitset: DG.BitSet;
|
|
64
|
+
isInvariantMapTrigger: boolean = false;
|
|
65
|
+
headerSelectedMonomers: type.MonomerSelectionStats = {};
|
|
66
|
+
webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
|
|
67
|
+
cachedWebLogoTooltip: {bar: string; tooltip: HTMLDivElement | null;} = {bar: '', tooltip: null};
|
|
68
|
+
|
|
61
69
|
private constructor(dataFrame: DG.DataFrame) {
|
|
62
70
|
this.df = dataFrame;
|
|
71
|
+
this.initBitset = this.df.filter.clone();
|
|
72
|
+
this.cp = bio.pickUpPalette(this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE));
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
static async getInstance(dataFrame: DG.DataFrame): Promise<PeptidesModel> {
|
|
@@ -80,6 +90,10 @@ export class PeptidesModel {
|
|
|
80
90
|
return this.logoSummaryGridSubject.asObservable();
|
|
81
91
|
}
|
|
82
92
|
|
|
93
|
+
get onSettingsChanged(): rxjs.Observable<unknown> {
|
|
94
|
+
return this.settingsSubject.asObservable();
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
get mutationCliffsSelection(): type.PositionToAARList {
|
|
84
98
|
this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
85
99
|
return this._mutationCliffsSelection;
|
|
@@ -100,7 +114,9 @@ export class PeptidesModel {
|
|
|
100
114
|
set invariantMapSelection(selection: type.PositionToAARList) {
|
|
101
115
|
this._invariantMapSelection = selection;
|
|
102
116
|
this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
|
|
117
|
+
this.isInvariantMapTrigger = true;
|
|
103
118
|
this.df.filter.fireChanged();
|
|
119
|
+
this.isInvariantMapTrigger = false;
|
|
104
120
|
this.invalidateGrids();
|
|
105
121
|
}
|
|
106
122
|
|
|
@@ -166,6 +182,7 @@ export class PeptidesModel {
|
|
|
166
182
|
this.df.setTag('settings', JSON.stringify(this._settings));
|
|
167
183
|
//TODO: update only needed components
|
|
168
184
|
this.updateDefault();
|
|
185
|
+
this.settingsSubject.next();
|
|
169
186
|
}
|
|
170
187
|
|
|
171
188
|
createAccordion(): DG.Accordion {
|
|
@@ -180,14 +197,14 @@ export class PeptidesModel {
|
|
|
180
197
|
|
|
181
198
|
updateDefault(): void {
|
|
182
199
|
if ((this.sourceGrid && !this._isUpdating) || !this.isInitialized) {
|
|
183
|
-
this.isInitialized = true;
|
|
200
|
+
// this.isInitialized = true;
|
|
184
201
|
this._isUpdating = true;
|
|
185
202
|
this.initializeViewersComponents();
|
|
186
203
|
//FIXME: modify during the initializeViewersComponents stages
|
|
187
204
|
this.mutationCliffsGridSubject.next(this.mutationCliffsGrid);
|
|
188
205
|
this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
|
|
189
|
-
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
190
|
-
|
|
206
|
+
// if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
207
|
+
// this.logoSummaryGridSubject.next(this.logoSummaryGrid);
|
|
191
208
|
|
|
192
209
|
this.fireBitsetChanged();
|
|
193
210
|
this.invalidateGrids();
|
|
@@ -203,8 +220,8 @@ export class PeptidesModel {
|
|
|
203
220
|
const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
204
221
|
const alphabet = col.tags['alphabet'];
|
|
205
222
|
const splitSeqDf = splitAlignedSequences(col);
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
for (const col of splitSeqDf.columns)
|
|
224
|
+
col.name = `p${col.name}`;
|
|
208
225
|
|
|
209
226
|
const positionColumns = splitSeqDf.columns.names();
|
|
210
227
|
|
|
@@ -227,6 +244,8 @@ export class PeptidesModel {
|
|
|
227
244
|
|
|
228
245
|
// SAR matrix table
|
|
229
246
|
//pivot a table to make it matrix-like
|
|
247
|
+
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
248
|
+
matrixDf = matrixDf.clone(DG.BitSet.create(matrixDf.rowCount, (i) => monomerCol.get(i) ? true : false));
|
|
230
249
|
matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
231
250
|
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
232
251
|
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
@@ -234,123 +253,41 @@ export class PeptidesModel {
|
|
|
234
253
|
matrixDf.name = 'SAR';
|
|
235
254
|
|
|
236
255
|
// Setting category order
|
|
237
|
-
this.setCategoryOrder(matrixDf);
|
|
256
|
+
// this.setCategoryOrder(matrixDf);
|
|
238
257
|
|
|
239
258
|
// SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
|
|
240
259
|
const sequenceDf = this.createVerticalTable();
|
|
241
260
|
|
|
242
|
-
this.
|
|
261
|
+
const scaledActivityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
262
|
+
const monomerColumns = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
|
|
263
|
+
this.substitutionsInfo = findMutations(scaledActivityCol, monomerColumns, this.settings);
|
|
243
264
|
|
|
244
265
|
[this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
|
|
245
266
|
this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
|
|
246
267
|
|
|
247
268
|
if (this.df.getTag(C.TAGS.CLUSTERS)) {
|
|
248
269
|
this.clusterStatsDf = this.calculateClusterStatistics();
|
|
249
|
-
this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
270
|
+
// this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
250
271
|
}
|
|
251
272
|
|
|
252
273
|
// init invariant map & mutation cliffs selections
|
|
253
274
|
this.initSelections(positionColumns);
|
|
254
275
|
|
|
255
|
-
positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
276
|
+
// positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
256
277
|
|
|
257
|
-
this.
|
|
278
|
+
this.setWebLogoInteraction();
|
|
279
|
+
this.webLogoBounds = {};
|
|
258
280
|
|
|
259
|
-
this.setCellRenderers(positionColumns);
|
|
281
|
+
this.setCellRenderers([...positionColumns, C.COLUMNS_NAMES.MEAN_DIFFERENCE]);
|
|
260
282
|
|
|
261
283
|
// show all the statistics in a tooltip over cell
|
|
262
|
-
this.setTooltips(positionColumns);
|
|
284
|
+
this.setTooltips([...positionColumns, C.COLUMNS_NAMES.MEAN_DIFFERENCE]);
|
|
263
285
|
|
|
264
286
|
this.setInteractionCallback();
|
|
265
287
|
|
|
266
288
|
this.setBitsetCallback();
|
|
267
289
|
|
|
268
|
-
this.postProcessGrids();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
//TODO: move out
|
|
272
|
-
calcSubstitutions(): void {
|
|
273
|
-
const activityValues: DG.Column<number> = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
274
|
-
const columnList: DG.Column<string>[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
|
|
275
|
-
const nCols = columnList.length;
|
|
276
|
-
if (nCols == 0)
|
|
277
|
-
throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
|
|
278
|
-
|
|
279
|
-
this.substitutionsInfo = new Map();
|
|
280
|
-
const nRows = this.df.rowCount;
|
|
281
|
-
for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
|
|
282
|
-
for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
|
|
283
|
-
let substCounter = 0;
|
|
284
|
-
const activityValSeq1 = activityValues.get(seq1Idx)!;
|
|
285
|
-
const activityValSeq2 = activityValues.get(seq2Idx)!;
|
|
286
|
-
const delta = activityValSeq1 - activityValSeq2;
|
|
287
|
-
if (Math.abs(delta) < (this.settings.minActivityDelta ?? 0))
|
|
288
|
-
continue;
|
|
289
|
-
|
|
290
|
-
let substCounterFlag = false;
|
|
291
|
-
const tempData: { pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number }[] =
|
|
292
|
-
[];
|
|
293
|
-
for (const currentPosCol of columnList) {
|
|
294
|
-
const seq1monomer = currentPosCol.get(seq1Idx)!;
|
|
295
|
-
const seq2monomer = currentPosCol.get(seq2Idx)!;
|
|
296
|
-
if (seq1monomer == seq2monomer)
|
|
297
|
-
continue;
|
|
298
|
-
|
|
299
|
-
substCounter++;
|
|
300
|
-
substCounterFlag = substCounter > (this.settings.maxMutations ?? 1);
|
|
301
|
-
if (substCounterFlag)
|
|
302
|
-
break;
|
|
303
|
-
|
|
304
|
-
tempData.push({
|
|
305
|
-
pos: currentPosCol.name,
|
|
306
|
-
seq1monomer: seq1monomer,
|
|
307
|
-
seq2monomer: seq2monomer,
|
|
308
|
-
seq1Idx: seq1Idx,
|
|
309
|
-
seq2Idx: seq2Idx,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (substCounterFlag || substCounter == 0)
|
|
314
|
-
continue;
|
|
315
|
-
|
|
316
|
-
for (const tempDataElement of tempData) {
|
|
317
|
-
const position = tempDataElement.pos;
|
|
318
|
-
|
|
319
|
-
//Working with seq1monomer
|
|
320
|
-
const seq1monomer = tempDataElement.seq1monomer;
|
|
321
|
-
if (!this.substitutionsInfo.has(seq1monomer))
|
|
322
|
-
this.substitutionsInfo.set(seq1monomer, new Map());
|
|
323
|
-
|
|
324
|
-
let positionsMap = this.substitutionsInfo.get(seq1monomer)!;
|
|
325
|
-
if (!positionsMap.has(position))
|
|
326
|
-
positionsMap.set(position, new Map());
|
|
327
|
-
|
|
328
|
-
let indexes = positionsMap.get(position)!;
|
|
329
|
-
|
|
330
|
-
!indexes.has(seq1Idx) ? indexes.set(seq1Idx, [seq2Idx]) : (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
|
|
331
|
-
|
|
332
|
-
//Working with seq2monomer
|
|
333
|
-
const seq2monomer = tempDataElement.seq2monomer;
|
|
334
|
-
if (!this.substitutionsInfo.has(seq2monomer))
|
|
335
|
-
this.substitutionsInfo.set(seq2monomer, new Map());
|
|
336
|
-
|
|
337
|
-
positionsMap = this.substitutionsInfo.get(seq2monomer)!;
|
|
338
|
-
if (!positionsMap.has(position))
|
|
339
|
-
positionsMap.set(position, new Map());
|
|
340
|
-
|
|
341
|
-
indexes = positionsMap.get(position)!;
|
|
342
|
-
!indexes.has(seq2Idx) ? indexes.set(seq2Idx, [seq1Idx]) : (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const TypedArray = getTypedArrayConstructor(nRows);
|
|
348
|
-
for (const positionMap of this.substitutionsInfo.values()) {
|
|
349
|
-
for (const indexMap of positionMap.values()) {
|
|
350
|
-
for (const [index, indexArray] of indexMap.entries())
|
|
351
|
-
indexMap.set(index, new TypedArray(indexArray));
|
|
352
|
-
}
|
|
353
|
-
}
|
|
290
|
+
this.postProcessGrids(positionColumns);
|
|
354
291
|
}
|
|
355
292
|
|
|
356
293
|
initSelections(positionColumns: string[]): void {
|
|
@@ -362,7 +299,6 @@ export class PeptidesModel {
|
|
|
362
299
|
}
|
|
363
300
|
this.invariantMapSelection = tempInvariantMapSelection;
|
|
364
301
|
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
365
|
-
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
366
302
|
}
|
|
367
303
|
|
|
368
304
|
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
|
|
@@ -417,21 +353,23 @@ export class PeptidesModel {
|
|
|
417
353
|
calculateMonomerPositionStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
418
354
|
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
419
355
|
|
|
356
|
+
let monomerCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
357
|
+
// monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
358
|
+
|
|
420
359
|
//calculate p-values based on t-test
|
|
421
360
|
const matrixCols = matrixDf.columns;
|
|
422
361
|
const mdCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
423
362
|
const pValCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
|
|
424
363
|
const countCol = matrixCols.addNewInt(C.COLUMNS_NAMES.COUNT);
|
|
425
364
|
const ratioCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
const activityCol: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
|
|
365
|
+
const posCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
366
|
+
const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
429
367
|
const sourceDfLen = activityCol.length;
|
|
430
368
|
|
|
431
369
|
for (let i = 0; i < matrixDf.rowCount; i++) {
|
|
432
|
-
const position
|
|
433
|
-
const
|
|
434
|
-
const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) ===
|
|
370
|
+
const position = posCol.get(i)!;
|
|
371
|
+
const monomer = monomerCol.get(i)!;
|
|
372
|
+
const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) === monomer);
|
|
435
373
|
const stats = getStats(activityCol, mask);
|
|
436
374
|
|
|
437
375
|
mdCol.set(i, stats.meanDifference);
|
|
@@ -440,8 +378,6 @@ export class PeptidesModel {
|
|
|
440
378
|
ratioCol.set(i, stats.ratio);
|
|
441
379
|
}
|
|
442
380
|
|
|
443
|
-
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
444
|
-
matrixDf = matrixDf.clone(DG.BitSet.create(matrixDf.rowCount, (i) => monomerCol.get(i) ? true : false));
|
|
445
381
|
return matrixDf as DG.DataFrame;
|
|
446
382
|
}
|
|
447
383
|
|
|
@@ -469,27 +405,27 @@ export class PeptidesModel {
|
|
|
469
405
|
return statsDf;
|
|
470
406
|
}
|
|
471
407
|
|
|
472
|
-
setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
408
|
+
// setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
409
|
+
// let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
|
|
410
|
+
// if (this.settings.isBidirectional) {
|
|
411
|
+
// const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
|
|
412
|
+
// sortArgument = 'Absolute Mean difference';
|
|
413
|
+
// const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
|
|
414
|
+
// absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
415
|
+
// }
|
|
416
|
+
|
|
417
|
+
// const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
|
|
418
|
+
// .aggregate();
|
|
419
|
+
// const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
420
|
+
// const getWeight = (aar: string): number => aarWeightsDf
|
|
421
|
+
// .groupBy(['weight'])
|
|
422
|
+
// .where(`${C.COLUMNS_NAMES.MONOMER} = ${aar}`)
|
|
423
|
+
// .aggregate()
|
|
424
|
+
// .get('weight', 0) as number;
|
|
425
|
+
// aarList.sort((first, second) => getWeight(second) - getWeight(first));
|
|
426
|
+
|
|
427
|
+
// matrixDf.getCol(C.COLUMNS_NAMES.MONOMER).setCategoryOrder(aarList);
|
|
428
|
+
// }
|
|
493
429
|
|
|
494
430
|
createVerticalTable(): DG.DataFrame {
|
|
495
431
|
// TODO: aquire ALL of the positions
|
|
@@ -537,86 +473,6 @@ export class PeptidesModel {
|
|
|
537
473
|
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
538
474
|
}
|
|
539
475
|
|
|
540
|
-
createLogoSummaryGrid(): DG.Grid {
|
|
541
|
-
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
542
|
-
const summaryTableLength = summaryTable.rowCount;
|
|
543
|
-
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
544
|
-
const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
|
|
545
|
-
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
546
|
-
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
547
|
-
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
548
|
-
const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
549
|
-
|
|
550
|
-
for (let index = 0; index < summaryTableLength; ++index) {
|
|
551
|
-
const indexes: number[] = [];
|
|
552
|
-
for (let j = 0; j < originalClustersCol.length; ++j) {
|
|
553
|
-
if (originalClustersCol.get(j) === clustersCol.get(index))
|
|
554
|
-
indexes.push(j);
|
|
555
|
-
}
|
|
556
|
-
const tCol = DG.Column.string('peptides', indexes.length);
|
|
557
|
-
tCol.init((i) => peptideCol.get(indexes[i]));
|
|
558
|
-
|
|
559
|
-
for (const tag of peptideCol.tags)
|
|
560
|
-
tCol.setTag(tag[0], tag[1]);
|
|
561
|
-
|
|
562
|
-
const dfSlice = DG.DataFrame.fromColumns([tCol]);
|
|
563
|
-
tempDfList[index] = dfSlice;
|
|
564
|
-
webLogoCol.set(index, index.toString());
|
|
565
|
-
membersCol.set(index, dfSlice.rowCount);
|
|
566
|
-
}
|
|
567
|
-
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
568
|
-
|
|
569
|
-
const grid = summaryTable.plot.grid();
|
|
570
|
-
const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
|
|
571
|
-
gridClustersCol.name = 'Clusters';
|
|
572
|
-
gridClustersCol.visible = true;
|
|
573
|
-
grid.columns.rowHeader!.visible = false;
|
|
574
|
-
grid.props.rowHeight = 55;
|
|
575
|
-
grid.onCellPrepare((cell) => {
|
|
576
|
-
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
|
|
577
|
-
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
|
|
578
|
-
.then((viewer) => cell.element = viewer.root);
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
grid.root.addEventListener('click', (ev) => {
|
|
582
|
-
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
583
|
-
if (!cell || !cell.isTableCell)
|
|
584
|
-
return;
|
|
585
|
-
|
|
586
|
-
const cluster = clustersCol.get(cell.tableRowIndex!)!;
|
|
587
|
-
summaryTable.currentRowIdx = -1;
|
|
588
|
-
if (ev.shiftKey)
|
|
589
|
-
this.modifyClusterSelection(cluster);
|
|
590
|
-
else
|
|
591
|
-
this.initClusterSelection(cluster);
|
|
592
|
-
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
593
|
-
});
|
|
594
|
-
grid.onCellRender.subscribe((gridCellArgs) => {
|
|
595
|
-
const gc = gridCellArgs.cell;
|
|
596
|
-
if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
|
|
597
|
-
return;
|
|
598
|
-
const canvasContext = gridCellArgs.g;
|
|
599
|
-
const bound = gridCellArgs.bounds;
|
|
600
|
-
canvasContext.save();
|
|
601
|
-
canvasContext.beginPath();
|
|
602
|
-
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
603
|
-
canvasContext.clip();
|
|
604
|
-
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
|
|
605
|
-
gridCellArgs.preventDefault();
|
|
606
|
-
canvasContext.restore();
|
|
607
|
-
});
|
|
608
|
-
grid.onCellTooltip((cell, x, y) => {
|
|
609
|
-
if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
|
|
610
|
-
this.showTooltipCluster(cell.cell.value, x, y);
|
|
611
|
-
return true;
|
|
612
|
-
});
|
|
613
|
-
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
614
|
-
webLogoGridCol.cellType = 'html';
|
|
615
|
-
webLogoGridCol.width = 350;
|
|
616
|
-
|
|
617
|
-
return grid;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
476
|
modifyClusterSelection(cluster: number): void {
|
|
621
477
|
const tempSelection = this.logoSummarySelection;
|
|
622
478
|
const idx = tempSelection.indexOf(cluster);
|
|
@@ -632,7 +488,7 @@ export class PeptidesModel {
|
|
|
632
488
|
this.logoSummarySelection = [cluster];
|
|
633
489
|
}
|
|
634
490
|
|
|
635
|
-
|
|
491
|
+
setWebLogoInteraction(): void {
|
|
636
492
|
const eventAction = (ev: MouseEvent): void => {
|
|
637
493
|
const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
638
494
|
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
|
|
@@ -649,7 +505,7 @@ export class PeptidesModel {
|
|
|
649
505
|
}
|
|
650
506
|
|
|
651
507
|
findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
|
|
652
|
-
const barCoords = this.
|
|
508
|
+
const barCoords = this.webLogoBounds[cell.tableColumn!.name];
|
|
653
509
|
for (const [monomer, coords] of Object.entries(barCoords)) {
|
|
654
510
|
const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
|
|
655
511
|
const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
|
|
@@ -666,15 +522,17 @@ export class PeptidesModel {
|
|
|
666
522
|
const monomer = barPart.monomer;
|
|
667
523
|
const position = barPart.position;
|
|
668
524
|
if (ev.type === 'click') {
|
|
669
|
-
ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position,
|
|
670
|
-
this.initMonomerPositionSelection(monomer, position,
|
|
671
|
-
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
525
|
+
ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, false) :
|
|
526
|
+
this.initMonomerPositionSelection(monomer, position, false);
|
|
672
527
|
} else {
|
|
673
|
-
const bar = `${
|
|
674
|
-
if (this.
|
|
675
|
-
ui.tooltip.show(this.
|
|
528
|
+
const bar = `${position} = ${monomer}`;
|
|
529
|
+
if (this.cachedWebLogoTooltip.bar == bar)
|
|
530
|
+
ui.tooltip.show(this.cachedWebLogoTooltip.tooltip!, ev.clientX, ev.clientY);
|
|
676
531
|
else
|
|
677
|
-
this.
|
|
532
|
+
this.cachedWebLogoTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
|
|
533
|
+
|
|
534
|
+
//TODO: how to unghighlight?
|
|
535
|
+
// this.df.rows.match(bar).highlight();
|
|
678
536
|
}
|
|
679
537
|
}
|
|
680
538
|
|
|
@@ -731,22 +589,40 @@ export class PeptidesModel {
|
|
|
731
589
|
|
|
732
590
|
this.sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
733
591
|
this.sourceGrid.onCellRender.subscribe((gcArgs) => {
|
|
734
|
-
const
|
|
592
|
+
const ctx = gcArgs.g;
|
|
735
593
|
const bounds = gcArgs.bounds;
|
|
736
594
|
const col = gcArgs.cell.tableColumn;
|
|
737
595
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
596
|
+
ctx.save();
|
|
597
|
+
ctx.beginPath();
|
|
598
|
+
ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
599
|
+
ctx.clip();
|
|
742
600
|
|
|
743
601
|
if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
|
|
744
|
-
const
|
|
745
|
-
|
|
602
|
+
const monomerStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
603
|
+
const positionStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
604
|
+
const rowMask = DG.BitSet.create(this.monomerPositionStatsDf.rowCount, (i) => positionStatsCol.get(i) === col.name);
|
|
605
|
+
//TODO: precalc on stats creation
|
|
606
|
+
const sortedStatsOrder = this.monomerPositionStatsDf.getSortedOrder([C.COLUMNS_NAMES.COUNT], [false], rowMask)
|
|
607
|
+
.sort((a, b) => {
|
|
608
|
+
if (monomerStatsCol.get(a) === '-' || monomerStatsCol.get(a) === '')
|
|
609
|
+
return -1;
|
|
610
|
+
else if (monomerStatsCol.get(b) === '-' || monomerStatsCol.get(b) === '')
|
|
611
|
+
return +1;
|
|
612
|
+
return 0;
|
|
613
|
+
});
|
|
614
|
+
const statsInfo: type.StatsInfo = {
|
|
615
|
+
countCol: this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.COUNT),
|
|
616
|
+
monomerCol: monomerStatsCol,
|
|
617
|
+
orderedIndexes: sortedStatsOrder,
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
this.webLogoBounds[col.name] =
|
|
621
|
+
CR.drawLogoInBounds(ctx, bounds, statsInfo, this.df.rowCount, this.cp, this.headerSelectedMonomers[col.name]);
|
|
746
622
|
gcArgs.preventDefault();
|
|
747
623
|
}
|
|
748
624
|
|
|
749
|
-
|
|
625
|
+
ctx.restore();
|
|
750
626
|
});
|
|
751
627
|
}
|
|
752
628
|
|
|
@@ -787,7 +663,7 @@ export class PeptidesModel {
|
|
|
787
663
|
const tooltipElements: HTMLDivElement[] = [];
|
|
788
664
|
const monomerName = aar.toLowerCase();
|
|
789
665
|
|
|
790
|
-
let mw =
|
|
666
|
+
let mw = getMonomerWorks();
|
|
791
667
|
let mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
|
|
792
668
|
|
|
793
669
|
if (mol) {
|
|
@@ -856,7 +732,6 @@ export class PeptidesModel {
|
|
|
856
732
|
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
|
|
857
733
|
isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
|
|
858
734
|
this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
|
|
859
|
-
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
860
735
|
};
|
|
861
736
|
|
|
862
737
|
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
@@ -921,7 +796,7 @@ export class PeptidesModel {
|
|
|
921
796
|
invalidateGrids(): void {
|
|
922
797
|
this.mutationCliffsGrid.invalidate();
|
|
923
798
|
this.mostPotentResiduesGrid.invalidate();
|
|
924
|
-
this.logoSummaryGrid?.invalidate();
|
|
799
|
+
// this.logoSummaryGrid?.invalidate();
|
|
925
800
|
this.sourceGrid?.invalidate();
|
|
926
801
|
}
|
|
927
802
|
|
|
@@ -967,18 +842,26 @@ export class PeptidesModel {
|
|
|
967
842
|
};
|
|
968
843
|
|
|
969
844
|
selection.onChanged.subscribe(() => changeSelectionBitset(selection));
|
|
845
|
+
|
|
970
846
|
filter.onChanged.subscribe(() => {
|
|
971
847
|
const positionList = Object.keys(this.invariantMapSelection);
|
|
972
|
-
|
|
848
|
+
const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
|
|
973
849
|
let result = true;
|
|
974
850
|
for (const position of positionList) {
|
|
975
851
|
const aarList = this.invariantMapSelection[position];
|
|
976
852
|
result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
|
|
977
853
|
if (!result)
|
|
978
|
-
|
|
854
|
+
return result;
|
|
979
855
|
}
|
|
980
|
-
|
|
981
|
-
}
|
|
856
|
+
return result;
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
if (!this.isInvariantMapTrigger)
|
|
860
|
+
this.initBitset = filter.clone();
|
|
861
|
+
|
|
862
|
+
// filter.copyFrom(invariantMapBitset.and(this.initBitset), false);
|
|
863
|
+
const temp = invariantMapBitset.and(this.initBitset);
|
|
864
|
+
filter.init((i) => temp.get(i), false);
|
|
982
865
|
});
|
|
983
866
|
this.isBitsetChangedInitialized = true;
|
|
984
867
|
}
|
|
@@ -987,11 +870,12 @@ export class PeptidesModel {
|
|
|
987
870
|
this.isPeptideSpaceChangingBitset = isPeptideSpaceSource;
|
|
988
871
|
this.df.selection.fireChanged();
|
|
989
872
|
this.modifyOrCreateSplitCol();
|
|
873
|
+
this.headerSelectedMonomers = calculateSelected(this.df);
|
|
990
874
|
grok.shell.o = this.createAccordion().root;
|
|
991
875
|
this.isPeptideSpaceChangingBitset = false;
|
|
992
876
|
}
|
|
993
877
|
|
|
994
|
-
postProcessGrids(): void {
|
|
878
|
+
postProcessGrids(posCols: string[]): void {
|
|
995
879
|
const mdCol: DG.GridColumn = this.mostPotentResiduesGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
996
880
|
mdCol.name = 'Diff';
|
|
997
881
|
|
|
@@ -1019,8 +903,22 @@ export class PeptidesModel {
|
|
|
1019
903
|
|
|
1020
904
|
setViewerGridProps(this.mutationCliffsGrid);
|
|
1021
905
|
setViewerGridProps(this.mostPotentResiduesGrid);
|
|
1022
|
-
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
1023
|
-
|
|
906
|
+
// if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
907
|
+
// setViewerGridProps(this.logoSummaryGrid);
|
|
908
|
+
|
|
909
|
+
for (let gcIndex = 0; gcIndex < this.sourceGrid.columns.length; ++gcIndex) {
|
|
910
|
+
const col = this.sourceGrid.columns.byIndex(gcIndex)!;
|
|
911
|
+
if (!col.column)
|
|
912
|
+
continue;
|
|
913
|
+
|
|
914
|
+
if (posCols.includes(col.name))
|
|
915
|
+
col.name = col.name.substring(1);
|
|
916
|
+
|
|
917
|
+
col.visible =
|
|
918
|
+
col.column?.semType === C.SEM_TYPES.MONOMER ||
|
|
919
|
+
col.column.name === C.COLUMNS_NAMES.ACTIVITY_SCALED ||
|
|
920
|
+
Object.keys(this.settings.columns ?? {}).includes(col.column.name ?? '');
|
|
921
|
+
}
|
|
1024
922
|
}
|
|
1025
923
|
|
|
1026
924
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
@@ -1040,19 +938,27 @@ export class PeptidesModel {
|
|
|
1040
938
|
async init(): Promise<void> {
|
|
1041
939
|
if (this.isInitialized)
|
|
1042
940
|
return;
|
|
941
|
+
this.isInitialized = true;
|
|
942
|
+
|
|
943
|
+
// Don't find the dataset if the analysis started from button
|
|
944
|
+
if (this.df.getTag('newAnalysis') !== '1')
|
|
945
|
+
this.currentView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === '1')!;
|
|
946
|
+
|
|
947
|
+
this.currentView ??= grok.shell.addTableView(this.df);
|
|
1043
948
|
|
|
1044
|
-
this.
|
|
1045
|
-
grok.shell.addTableView(this.df);
|
|
949
|
+
this.df.setTag('newAnalysis', '');
|
|
1046
950
|
if (!this.isRibbonSet) {
|
|
1047
|
-
|
|
951
|
+
//TODO: don't pass model, pass parameters instead
|
|
952
|
+
const settingsButton = ui.bigButton('Settings', () => getSettingsDialog(this), 'Peptides analysis settings');
|
|
953
|
+
this.currentView.setRibbonPanels([[settingsButton]], false);
|
|
1048
954
|
this.isRibbonSet = true;
|
|
1049
955
|
}
|
|
1050
956
|
grok.shell.v = this.currentView;
|
|
1051
957
|
this.sourceGrid = this.currentView.grid;
|
|
1052
|
-
if (this.df.tags[C.PEPTIDES_ANALYSIS] === '
|
|
958
|
+
if (this.df.tags[C.PEPTIDES_ANALYSIS] === '1')
|
|
1053
959
|
return;
|
|
1054
960
|
|
|
1055
|
-
this.df.tags[C.PEPTIDES_ANALYSIS] = '
|
|
961
|
+
this.df.tags[C.PEPTIDES_ANALYSIS] = '1';
|
|
1056
962
|
const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
|
|
1057
963
|
scaledGridCol.name = scaledGridCol.column!.getTag('gridName');
|
|
1058
964
|
scaledGridCol.format = '#.000';
|