@datagrok/peptides 1.3.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 +424 -271
- package/dist/package.js +482 -290
- package/package.json +5 -1
- package/src/model.ts +206 -92
- package/src/package.ts +3 -2
- package/src/tests/core.ts +8 -7
- package/src/utils/cell-renderer.ts +12 -0
- package/src/utils/constants.ts +7 -3
- package/src/utils/misc.ts +12 -17
- package/src/utils/peptide-similarity-space.ts +2 -1
- package/src/viewers/logo-summary.ts +5 -5
- package/src/viewers/peptide-space-viewer.ts +6 -4
- package/src/viewers/sar-viewer.ts +39 -6
- package/src/widgets/distribution.ts +25 -10
- package/src/widgets/mutation-cliffs.ts +2 -1
- package/src/widgets/{analyze-peptides.ts → peptides.ts} +50 -31
- package/{test-Peptides-7770371320b2-8cac318e.html → test-Peptides-8408d9b6ee67-ca232121.html} +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Volodymyr Dyma",
|
|
6
6
|
"email": "vdyma@datagrok.ai"
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"@types/node-fetch": "^2.6.2",
|
|
44
44
|
"node-fetch": "^2.6.7"
|
|
45
45
|
},
|
|
46
|
+
"grokDependencies": {
|
|
47
|
+
"@datagrok/bio": "latest",
|
|
48
|
+
"@datagrok/helm": "latest"
|
|
49
|
+
},
|
|
46
50
|
"scripts": {
|
|
47
51
|
"link-api": "npm link datagrok-api",
|
|
48
52
|
"link-utils": "npm link @datagrok-libraries/utils",
|
package/src/model.ts
CHANGED
|
@@ -5,13 +5,12 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
6
6
|
|
|
7
7
|
import * as rxjs from 'rxjs';
|
|
8
|
-
import $ from 'cash-dom';
|
|
9
8
|
|
|
10
9
|
import * as C from './utils/constants';
|
|
11
10
|
import * as type from './utils/types';
|
|
12
11
|
import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
|
|
13
12
|
import {MutationCliffsViewer, SARViewerBase, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
14
|
-
import
|
|
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';
|
|
@@ -35,9 +34,11 @@ export class PeptidesModel {
|
|
|
35
34
|
df: DG.DataFrame;
|
|
36
35
|
splitCol!: DG.Column<boolean>;
|
|
37
36
|
edf: DG.DataFrame | null = null;
|
|
38
|
-
|
|
37
|
+
monomerPositionStatsDf!: DG.DataFrame;
|
|
38
|
+
clusterStatsDf!: DG.DataFrame;
|
|
39
39
|
_mutationCliffsSelection: type.PositionToAARList = {};
|
|
40
40
|
_invariantMapSelection: type.PositionToAARList = {};
|
|
41
|
+
_logoSummarySelection: number[] = [];
|
|
41
42
|
substitutionsInfo: type.SubstitutionsInfo = new Map();
|
|
42
43
|
isInitialized = false;
|
|
43
44
|
currentView!: DG.TableView;
|
|
@@ -86,7 +87,6 @@ export class PeptidesModel {
|
|
|
86
87
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
87
88
|
this.fireBitsetChanged();
|
|
88
89
|
this.invalidateGrids();
|
|
89
|
-
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
get invariantMapSelection(): type.PositionToAARList {
|
|
@@ -100,6 +100,17 @@ export class PeptidesModel {
|
|
|
100
100
|
this.invalidateGrids();
|
|
101
101
|
}
|
|
102
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();
|
|
112
|
+
}
|
|
113
|
+
|
|
103
114
|
get usedProperties(): {[propName: string]: string | number | boolean} {
|
|
104
115
|
this._usedProperties = JSON.parse(this.df.tags['sarProperties'] ?? '{}');
|
|
105
116
|
return this._usedProperties;
|
|
@@ -171,7 +182,8 @@ export class PeptidesModel {
|
|
|
171
182
|
}
|
|
172
183
|
|
|
173
184
|
updateDefault(): void {
|
|
174
|
-
const proprtyChanged =
|
|
185
|
+
const proprtyChanged =
|
|
186
|
+
this.isPropertyChanged(this.mutationCliffsViewer) || this.isPropertyChanged(this.mostPotentResiduesViewer);
|
|
175
187
|
if ((this.sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
176
188
|
this.isInitialized = true;
|
|
177
189
|
this._isUpdating = true;
|
|
@@ -193,7 +205,7 @@ export class PeptidesModel {
|
|
|
193
205
|
throw new Error(`Source grid is not initialized`);
|
|
194
206
|
|
|
195
207
|
//Split the aligned sequence into separate AARs
|
|
196
|
-
const col
|
|
208
|
+
const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
197
209
|
const alphabet = col.tags['alphabet'];
|
|
198
210
|
const splitSeqDf = splitAlignedSequences(col);
|
|
199
211
|
|
|
@@ -204,12 +216,7 @@ export class PeptidesModel {
|
|
|
204
216
|
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
205
217
|
splitSeqDf.columns.add(activityCol);
|
|
206
218
|
|
|
207
|
-
this.joinDataFrames(positionColumns, splitSeqDf);
|
|
208
|
-
|
|
209
|
-
for (const dfCol of this.df.columns) {
|
|
210
|
-
if (positionColumns.includes(dfCol.name))
|
|
211
|
-
setAARRenderer(dfCol, alphabet, this.sourceGrid);
|
|
212
|
-
}
|
|
219
|
+
this.joinDataFrames(positionColumns, splitSeqDf, alphabet);
|
|
213
220
|
|
|
214
221
|
this.sortSourceGrid();
|
|
215
222
|
|
|
@@ -223,11 +230,11 @@ export class PeptidesModel {
|
|
|
223
230
|
matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
|
|
224
231
|
|
|
225
232
|
//statistics for specific AAR at a specific position
|
|
226
|
-
this.
|
|
233
|
+
this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics(matrixDf);
|
|
227
234
|
|
|
228
235
|
// SAR matrix table
|
|
229
236
|
//pivot a table to make it matrix-like
|
|
230
|
-
matrixDf = this.
|
|
237
|
+
matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
231
238
|
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
232
239
|
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
233
240
|
.aggregate();
|
|
@@ -244,8 +251,10 @@ export class PeptidesModel {
|
|
|
244
251
|
[this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
|
|
245
252
|
this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
|
|
246
253
|
|
|
247
|
-
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
254
|
+
if (this.df.getTag(C.TAGS.CLUSTERS)) {
|
|
255
|
+
this.clusterStatsDf = this.calculateClusterStatistics();
|
|
248
256
|
this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
257
|
+
}
|
|
249
258
|
|
|
250
259
|
// init invariant map & mutation cliffs selections
|
|
251
260
|
this.initSelections(positionColumns);
|
|
@@ -360,15 +369,22 @@ export class PeptidesModel {
|
|
|
360
369
|
}
|
|
361
370
|
this.invariantMapSelection = tempInvariantMapSelection;
|
|
362
371
|
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
372
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
363
373
|
}
|
|
364
374
|
|
|
365
|
-
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame): void {
|
|
375
|
+
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
|
|
366
376
|
// append splitSeqDf columns to source table and make sure columns are not added more than once
|
|
367
377
|
const name = this.df.name;
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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);
|
|
372
388
|
}
|
|
373
389
|
this.df.name = name;
|
|
374
390
|
this.currentView.name = name;
|
|
@@ -393,8 +409,8 @@ export class PeptidesModel {
|
|
|
393
409
|
}
|
|
394
410
|
|
|
395
411
|
createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
|
|
396
|
-
const [scaledDf, newColName] =
|
|
397
|
-
scaleActivity(activityScaling, this.df
|
|
412
|
+
const [scaledDf, _, newColName] =
|
|
413
|
+
scaleActivity(activityScaling, this.df.getCol(C.COLUMNS_NAMES.ACTIVITY));
|
|
398
414
|
//TODO: make another func
|
|
399
415
|
const scaledCol = scaledDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
400
416
|
scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
|
|
@@ -410,7 +426,7 @@ export class PeptidesModel {
|
|
|
410
426
|
this.sourceGrid.columns.setOrder([newColName]);
|
|
411
427
|
}
|
|
412
428
|
|
|
413
|
-
|
|
429
|
+
calculateMonomerPositionStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
414
430
|
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
415
431
|
|
|
416
432
|
//calculate p-values based on t-test
|
|
@@ -427,7 +443,7 @@ export class PeptidesModel {
|
|
|
427
443
|
for (let i = 0; i < matrixDf.rowCount; i++) {
|
|
428
444
|
const position: string = posCol.get(i);
|
|
429
445
|
const aar: string = aarCol.get(i);
|
|
430
|
-
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);
|
|
431
447
|
const stats = getStats(activityCol, mask);
|
|
432
448
|
|
|
433
449
|
mdCol.set(i, stats.meanDifference);
|
|
@@ -436,23 +452,43 @@ export class PeptidesModel {
|
|
|
436
452
|
ratioCol.set(i, stats.ratio);
|
|
437
453
|
}
|
|
438
454
|
|
|
439
|
-
const countThreshold = 4;
|
|
440
|
-
matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} >= ${countThreshold}`).toDataFrame();
|
|
441
|
-
matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} <= ${sourceDfLen - countThreshold}`).toDataFrame();
|
|
442
|
-
|
|
443
455
|
return matrixDf as DG.DataFrame;
|
|
444
456
|
}
|
|
445
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
|
+
|
|
446
482
|
setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
447
483
|
let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
|
|
448
484
|
if (this.getViewer().bidirectionalAnalysis) {
|
|
449
|
-
const mdCol = this.
|
|
485
|
+
const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
|
|
450
486
|
sortArgument = 'Absolute Mean difference';
|
|
451
|
-
const absMDCol = this.
|
|
487
|
+
const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
|
|
452
488
|
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
453
489
|
}
|
|
454
490
|
|
|
455
|
-
const aarWeightsDf = this.
|
|
491
|
+
const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
|
|
456
492
|
.aggregate();
|
|
457
493
|
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
458
494
|
const getWeight = (aar: string): number => aarWeightsDf
|
|
@@ -469,7 +505,7 @@ export class PeptidesModel {
|
|
|
469
505
|
// TODO: aquire ALL of the positions
|
|
470
506
|
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
|
|
471
507
|
'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
|
|
472
|
-
let sequenceDf = this.
|
|
508
|
+
let sequenceDf = this.monomerPositionStatsDf.groupBy(columns)
|
|
473
509
|
.where('pValue <= 0.1')
|
|
474
510
|
.aggregate();
|
|
475
511
|
|
|
@@ -505,8 +541,8 @@ export class PeptidesModel {
|
|
|
505
541
|
pValGridCol.name = 'P-value';
|
|
506
542
|
|
|
507
543
|
// Setting Monomer column renderer
|
|
508
|
-
setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
509
|
-
setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
544
|
+
CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
545
|
+
CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
510
546
|
|
|
511
547
|
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
512
548
|
}
|
|
@@ -514,28 +550,75 @@ export class PeptidesModel {
|
|
|
514
550
|
createLogoSummaryGrid(): DG.Grid {
|
|
515
551
|
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
516
552
|
const summaryTableLength = summaryTable.rowCount;
|
|
517
|
-
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
518
553
|
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
519
|
-
|
|
554
|
+
const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
|
|
555
|
+
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
520
556
|
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
521
557
|
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
558
|
+
const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
522
559
|
|
|
523
560
|
for (let index = 0; index < summaryTableLength; ++index) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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;
|
|
529
574
|
webLogoCol.set(index, index.toString());
|
|
575
|
+
membersCol.set(index, dfSlice.rowCount);
|
|
530
576
|
}
|
|
531
577
|
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
532
578
|
|
|
533
579
|
const grid = summaryTable.plot.grid();
|
|
580
|
+
const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
|
|
581
|
+
gridClustersCol.name = 'Clusters';
|
|
582
|
+
gridClustersCol.visible = true;
|
|
534
583
|
grid.columns.rowHeader!.visible = false;
|
|
535
584
|
grid.props.rowHeight = 55;
|
|
536
585
|
grid.onCellPrepare((cell) => {
|
|
537
|
-
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo')
|
|
538
|
-
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo'
|
|
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;
|
|
539
622
|
});
|
|
540
623
|
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
541
624
|
webLogoGridCol.cellType = 'html';
|
|
@@ -544,6 +627,21 @@ export class PeptidesModel {
|
|
|
544
627
|
return grid;
|
|
545
628
|
}
|
|
546
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);
|
|
637
|
+
|
|
638
|
+
this.logoSummarySelection = tempSelection;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
initClusterSelection(cluster: number): void {
|
|
642
|
+
this.logoSummarySelection = [cluster];
|
|
643
|
+
}
|
|
644
|
+
|
|
547
645
|
setBarChartInteraction(): void {
|
|
548
646
|
const eventAction = (ev: MouseEvent): void => {
|
|
549
647
|
const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
@@ -578,8 +676,9 @@ export class PeptidesModel {
|
|
|
578
676
|
const monomer = barPart.monomer;
|
|
579
677
|
const position = barPart.position;
|
|
580
678
|
if (ev.type === 'click') {
|
|
581
|
-
ev.shiftKey ? this.
|
|
582
|
-
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);
|
|
583
682
|
} else {
|
|
584
683
|
const bar = `${monomer}:${position}`;
|
|
585
684
|
if (this.cachedBarchartTooltip.bar == bar)
|
|
@@ -590,7 +689,7 @@ export class PeptidesModel {
|
|
|
590
689
|
}
|
|
591
690
|
|
|
592
691
|
setCellRenderers(renderColNames: string[]): void {
|
|
593
|
-
const mdCol = this.
|
|
692
|
+
const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
594
693
|
//decompose into two different renering funcs
|
|
595
694
|
const renderCell = (args: DG.GridCellRenderArgs): void => {
|
|
596
695
|
const canvasContext = args.g;
|
|
@@ -622,14 +721,16 @@ export class PeptidesModel {
|
|
|
622
721
|
|
|
623
722
|
const viewer = this.getViewer();
|
|
624
723
|
if (this.isInvariantMap) {
|
|
625
|
-
const value: number = this.
|
|
724
|
+
const value: number = this.monomerPositionStatsDf
|
|
626
725
|
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
|
|
627
726
|
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
628
727
|
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
629
|
-
renderInvaraintMapCell(
|
|
728
|
+
CR.renderInvaraintMapCell(
|
|
729
|
+
canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
|
|
630
730
|
} else {
|
|
631
|
-
renderMutationCliffCell(
|
|
632
|
-
|
|
731
|
+
CR.renderMutationCliffCell(
|
|
732
|
+
canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf, viewer.bidirectionalAnalysis,
|
|
733
|
+
mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
|
|
633
734
|
}
|
|
634
735
|
}
|
|
635
736
|
args.preventDefault();
|
|
@@ -651,7 +752,7 @@ export class PeptidesModel {
|
|
|
651
752
|
context.clip();
|
|
652
753
|
|
|
653
754
|
if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
|
|
654
|
-
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);
|
|
655
756
|
this.barsBounds[col.name] = barBounds;
|
|
656
757
|
gcArgs.preventDefault();
|
|
657
758
|
}
|
|
@@ -708,8 +809,9 @@ export class PeptidesModel {
|
|
|
708
809
|
}
|
|
709
810
|
|
|
710
811
|
showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
|
|
711
|
-
const currentStatsDf = this.
|
|
812
|
+
const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
|
|
712
813
|
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
814
|
+
//TODO: use bitset instead of splitCol
|
|
713
815
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
714
816
|
const currentPosCol = this.df.getCol(position);
|
|
715
817
|
splitCol.init((i) => currentPosCol.get(i) == aar);
|
|
@@ -730,15 +832,40 @@ export class PeptidesModel {
|
|
|
730
832
|
return tooltip;
|
|
731
833
|
}
|
|
732
834
|
|
|
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);
|
|
855
|
+
|
|
856
|
+
return tooltip;
|
|
857
|
+
}
|
|
858
|
+
|
|
733
859
|
setInteractionCallback(): void {
|
|
734
860
|
const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
|
|
735
861
|
const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
|
|
736
|
-
// const invariantMapDf = this.invariantMapGrid.dataFrame;
|
|
737
862
|
|
|
738
863
|
const chooseAction =
|
|
739
|
-
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void =>
|
|
740
|
-
isShiftPressed ? this.
|
|
741
|
-
this.
|
|
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
|
+
};
|
|
742
869
|
|
|
743
870
|
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
744
871
|
const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
@@ -758,7 +885,7 @@ export class PeptidesModel {
|
|
|
758
885
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
759
886
|
const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
760
887
|
const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
761
|
-
chooseAction(aar, position, ev.shiftKey);
|
|
888
|
+
chooseAction(aar, position, ev.shiftKey, false);
|
|
762
889
|
});
|
|
763
890
|
|
|
764
891
|
const cellChanged = (table: DG.DataFrame): void => {
|
|
@@ -772,17 +899,14 @@ export class PeptidesModel {
|
|
|
772
899
|
this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
|
|
773
900
|
}
|
|
774
901
|
|
|
775
|
-
|
|
902
|
+
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
776
903
|
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
aarIndex
|
|
783
|
-
tempSelectionAt.length == 1 ? delete tempSelection[position] :
|
|
784
|
-
tempSelectionAt.splice(aarIndex, 1);
|
|
785
|
-
}
|
|
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);
|
|
786
910
|
|
|
787
911
|
if (isInvariantMapSelection)
|
|
788
912
|
this.invariantMapSelection = tempSelection;
|
|
@@ -790,8 +914,8 @@ export class PeptidesModel {
|
|
|
790
914
|
this.mutationCliffsSelection = tempSelection;
|
|
791
915
|
}
|
|
792
916
|
|
|
793
|
-
|
|
794
|
-
const tempSelection
|
|
917
|
+
initMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
918
|
+
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
795
919
|
tempSelection[position] = [aar];
|
|
796
920
|
|
|
797
921
|
if (isInvariantMapSelection)
|
|
@@ -803,8 +927,8 @@ export class PeptidesModel {
|
|
|
803
927
|
invalidateGrids(): void {
|
|
804
928
|
this.mutationCliffsGrid.invalidate();
|
|
805
929
|
this.mostPotentResiduesGrid.invalidate();
|
|
930
|
+
this.logoSummaryGrid?.invalidate();
|
|
806
931
|
this.sourceGrid?.invalidate();
|
|
807
|
-
//TODO: this.peptideSpaceGrid.invalidate();
|
|
808
932
|
}
|
|
809
933
|
|
|
810
934
|
setBitsetCallback(): void {
|
|
@@ -812,6 +936,7 @@ export class PeptidesModel {
|
|
|
812
936
|
return;
|
|
813
937
|
const selection = this.df.selection;
|
|
814
938
|
const filter = this.df.filter;
|
|
939
|
+
const clusterCol = this.df.col(C.COLUMNS_NAMES.CLUSTERS);
|
|
815
940
|
|
|
816
941
|
const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
|
|
817
942
|
const edfSelection = this.edf?.selection;
|
|
@@ -830,11 +955,6 @@ export class PeptidesModel {
|
|
|
830
955
|
};
|
|
831
956
|
|
|
832
957
|
const positionList = Object.keys(this.mutationCliffsSelection);
|
|
833
|
-
if (positionList.length == 0) {
|
|
834
|
-
currentBitset.init(() => false, false);
|
|
835
|
-
updateEdfSelection();
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
958
|
|
|
839
959
|
//TODO: move out
|
|
840
960
|
const getBitAt = (i: number): boolean => {
|
|
@@ -843,6 +963,8 @@ export class PeptidesModel {
|
|
|
843
963
|
if (this._mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
844
964
|
return true;
|
|
845
965
|
}
|
|
966
|
+
if (this._logoSummarySelection.includes(clusterCol?.get(i)!))
|
|
967
|
+
return true;
|
|
846
968
|
return false;
|
|
847
969
|
};
|
|
848
970
|
currentBitset.init(getBitAt, false);
|
|
@@ -885,10 +1007,9 @@ export class PeptidesModel {
|
|
|
885
1007
|
for (let i = 0; i < colNum; ++i) {
|
|
886
1008
|
const col = girdCols.byIndex(i)!;
|
|
887
1009
|
const colName = col.name;
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
col.width = gridProps.rowHeight + 10;
|
|
1010
|
+
col.width =
|
|
1011
|
+
grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER ? 50 :
|
|
1012
|
+
gridProps.rowHeight + 10;
|
|
892
1013
|
}
|
|
893
1014
|
}
|
|
894
1015
|
|
|
@@ -951,7 +1072,9 @@ export class PeptidesModel {
|
|
|
951
1072
|
return;
|
|
952
1073
|
|
|
953
1074
|
this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
954
|
-
this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)
|
|
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';
|
|
955
1078
|
this.sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
|
|
956
1079
|
this.sourceGrid.props.allowColSelection = false;
|
|
957
1080
|
|
|
@@ -966,10 +1089,9 @@ export class PeptidesModel {
|
|
|
966
1089
|
};
|
|
967
1090
|
|
|
968
1091
|
for (let i = 0; i < this.sourceGrid.columns.length; i++) {
|
|
969
|
-
const
|
|
970
|
-
if (
|
|
971
|
-
|
|
972
|
-
aarCol.visible = false;
|
|
1092
|
+
const currentCol = this.sourceGrid.columns.byIndex(i);
|
|
1093
|
+
if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
|
|
1094
|
+
currentCol.visible = false;
|
|
973
1095
|
}
|
|
974
1096
|
|
|
975
1097
|
const options = {scaling: this.df.tags['scaling']};
|
|
@@ -986,14 +1108,6 @@ export class PeptidesModel {
|
|
|
986
1108
|
dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
987
1109
|
}
|
|
988
1110
|
|
|
989
|
-
// TODO: completely remove this viewer?
|
|
990
|
-
// if (this.df.rowCount <= 10000) {
|
|
991
|
-
// const peptideSpaceViewerOptions = {method: 'UMAP', measure: 'Levenshtein', cyclesCount: 100};
|
|
992
|
-
// const peptideSpaceViewer =
|
|
993
|
-
// await this.df.plot.fromType('peptide-space-viewer', peptideSpaceViewerOptions) as PeptideSpaceViewer;
|
|
994
|
-
// dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.RIGHT, null, 'Peptide Space Viewer');
|
|
995
|
-
// }
|
|
996
|
-
|
|
997
1111
|
this.updateDefault();
|
|
998
1112
|
|
|
999
1113
|
const mcNode =
|
package/src/package.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import * as C from './utils/constants';
|
|
7
7
|
|
|
8
|
-
import {analyzePeptidesWidget} from './widgets/
|
|
8
|
+
import {analyzePeptidesWidget} from './widgets/peptides';
|
|
9
9
|
import {PeptideSimilaritySpaceWidget} from './utils/peptide-similarity-space';
|
|
10
10
|
import {manualAlignmentWidget} from './widgets/manual-alignment';
|
|
11
11
|
import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
@@ -155,7 +155,8 @@ export function getPeptidesStructure(col: DG.Column): DG.Widget {
|
|
|
155
155
|
|
|
156
156
|
function getOrDefine(dataframe?: DG.DataFrame, column?: DG.Column | null): [DG.DataFrame, DG.Column] {
|
|
157
157
|
dataframe ??= grok.shell.t;
|
|
158
|
-
column ??= dataframe.columns.bySemType(C.SEM_TYPES.MACROMOLECULE);
|
|
158
|
+
// column ??= dataframe.columns.bySemType(C.SEM_TYPES.MACROMOLECULE);
|
|
159
|
+
column ??= dataframe.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
159
160
|
if (column === null)
|
|
160
161
|
throw new Error('Table does not contain aligned sequence columns');
|
|
161
162
|
|