@datagrok/peptides 1.3.0 → 1.3.2
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 +2843 -3472
- package/dist/package.js +2643 -3294
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +1673 -1673
- package/package.json +7 -3
- package/src/model.ts +210 -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-916a90d7d48b-ea373bf5.html} +37 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Volodymyr Dyma",
|
|
6
6
|
"email": "vdyma@datagrok.ai"
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"directory": "packages/Peptides"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@datagrok-libraries/bio": "^
|
|
15
|
+
"@datagrok-libraries/bio": "^5.0.0",
|
|
16
16
|
"@datagrok-libraries/ml": "^2.0.1",
|
|
17
17
|
"@datagrok-libraries/statistics": "^0.1.6",
|
|
18
|
-
"@datagrok-libraries/utils": "^
|
|
18
|
+
"@datagrok-libraries/utils": "^1.10.1",
|
|
19
19
|
"cash-dom": "latest",
|
|
20
20
|
"datagrok-api": "^1.6.0",
|
|
21
21
|
"file-loader": "^6.2.0",
|
|
@@ -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,45 @@ export class PeptidesModel {
|
|
|
436
452
|
ratioCol.set(i, stats.ratio);
|
|
437
453
|
}
|
|
438
454
|
|
|
439
|
-
const
|
|
440
|
-
matrixDf = matrixDf.
|
|
441
|
-
matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} <= ${sourceDfLen - countThreshold}`).toDataFrame();
|
|
442
|
-
|
|
455
|
+
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
456
|
+
matrixDf = matrixDf.clone(DG.BitSet.create(matrixDf.rowCount, (i) => monomerCol.get(i) ? true : false));
|
|
443
457
|
return matrixDf as DG.DataFrame;
|
|
444
458
|
}
|
|
445
459
|
|
|
460
|
+
calculateClusterStatistics(): DG.DataFrame {
|
|
461
|
+
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
462
|
+
const statsDf = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
463
|
+
const clustersCol = statsDf.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
464
|
+
const statsDfCols = statsDf.columns;
|
|
465
|
+
const mdCol= statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
466
|
+
const pValCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
|
|
467
|
+
const countCol = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT);
|
|
468
|
+
const ratioCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
|
|
469
|
+
const activityList: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
|
|
470
|
+
|
|
471
|
+
for (let rowIdx = 0; rowIdx < clustersCol.length; ++rowIdx) {
|
|
472
|
+
const cluster = clustersCol.get(rowIdx);
|
|
473
|
+
const mask = DG.BitSet.create(activityList.length, (bitIdx) => originalClustersCol.get(bitIdx) === cluster);
|
|
474
|
+
const stats = getStats(activityList, mask);
|
|
475
|
+
|
|
476
|
+
mdCol.set(rowIdx, stats.meanDifference);
|
|
477
|
+
pValCol.set(rowIdx, stats.pValue);
|
|
478
|
+
countCol.set(rowIdx, stats.count);
|
|
479
|
+
ratioCol.set(rowIdx, stats.ratio);
|
|
480
|
+
}
|
|
481
|
+
return statsDf;
|
|
482
|
+
}
|
|
483
|
+
|
|
446
484
|
setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
447
485
|
let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
|
|
448
486
|
if (this.getViewer().bidirectionalAnalysis) {
|
|
449
|
-
const mdCol = this.
|
|
487
|
+
const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
|
|
450
488
|
sortArgument = 'Absolute Mean difference';
|
|
451
|
-
const absMDCol = this.
|
|
489
|
+
const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
|
|
452
490
|
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
453
491
|
}
|
|
454
492
|
|
|
455
|
-
const aarWeightsDf = this.
|
|
493
|
+
const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
|
|
456
494
|
.aggregate();
|
|
457
495
|
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
458
496
|
const getWeight = (aar: string): number => aarWeightsDf
|
|
@@ -469,7 +507,7 @@ export class PeptidesModel {
|
|
|
469
507
|
// TODO: aquire ALL of the positions
|
|
470
508
|
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
|
|
471
509
|
'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
|
|
472
|
-
let sequenceDf = this.
|
|
510
|
+
let sequenceDf = this.monomerPositionStatsDf.groupBy(columns)
|
|
473
511
|
.where('pValue <= 0.1')
|
|
474
512
|
.aggregate();
|
|
475
513
|
|
|
@@ -505,8 +543,8 @@ export class PeptidesModel {
|
|
|
505
543
|
pValGridCol.name = 'P-value';
|
|
506
544
|
|
|
507
545
|
// Setting Monomer column renderer
|
|
508
|
-
setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
509
|
-
setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
546
|
+
CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
547
|
+
CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
510
548
|
|
|
511
549
|
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
512
550
|
}
|
|
@@ -514,28 +552,75 @@ export class PeptidesModel {
|
|
|
514
552
|
createLogoSummaryGrid(): DG.Grid {
|
|
515
553
|
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
516
554
|
const summaryTableLength = summaryTable.rowCount;
|
|
517
|
-
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
518
555
|
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
519
|
-
|
|
556
|
+
const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
|
|
557
|
+
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
520
558
|
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
521
559
|
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
560
|
+
const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
522
561
|
|
|
523
562
|
for (let index = 0; index < summaryTableLength; ++index) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
563
|
+
const indexes: number[] = [];
|
|
564
|
+
for (let j = 0; j < originalClustersCol.length; ++j) {
|
|
565
|
+
if (originalClustersCol.get(j) === clustersCol.get(index))
|
|
566
|
+
indexes.push(j);
|
|
567
|
+
}
|
|
568
|
+
const tCol = DG.Column.string('peptides', indexes.length);
|
|
569
|
+
tCol.init((i) => peptideCol.get(indexes[i]));
|
|
570
|
+
|
|
571
|
+
for (const tag of peptideCol.tags)
|
|
572
|
+
tCol.setTag(tag[0], tag[1]);
|
|
573
|
+
|
|
574
|
+
const dfSlice = DG.DataFrame.fromColumns([tCol]);
|
|
575
|
+
tempDfList[index] = dfSlice;
|
|
529
576
|
webLogoCol.set(index, index.toString());
|
|
577
|
+
membersCol.set(index, dfSlice.rowCount);
|
|
530
578
|
}
|
|
531
579
|
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
532
580
|
|
|
533
581
|
const grid = summaryTable.plot.grid();
|
|
582
|
+
const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
|
|
583
|
+
gridClustersCol.name = 'Clusters';
|
|
584
|
+
gridClustersCol.visible = true;
|
|
534
585
|
grid.columns.rowHeader!.visible = false;
|
|
535
586
|
grid.props.rowHeight = 55;
|
|
536
587
|
grid.onCellPrepare((cell) => {
|
|
537
|
-
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo')
|
|
538
|
-
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo'
|
|
588
|
+
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
|
|
589
|
+
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
|
|
590
|
+
.then((viewer) => cell.element = viewer.root);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
grid.root.addEventListener('click', (ev) => {
|
|
594
|
+
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
595
|
+
if (!cell || !cell.isTableCell)
|
|
596
|
+
return;
|
|
597
|
+
|
|
598
|
+
const cluster = clustersCol.get(cell.tableRowIndex!)!;
|
|
599
|
+
summaryTable.currentRowIdx = -1;
|
|
600
|
+
if (ev.shiftKey)
|
|
601
|
+
this.modifyClusterSelection(cluster);
|
|
602
|
+
else
|
|
603
|
+
this.initClusterSelection(cluster);
|
|
604
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
605
|
+
});
|
|
606
|
+
grid.onCellRender.subscribe((gridCellArgs) => {
|
|
607
|
+
const gc = gridCellArgs.cell;
|
|
608
|
+
if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
|
|
609
|
+
return;
|
|
610
|
+
const canvasContext = gridCellArgs.g;
|
|
611
|
+
const bound = gridCellArgs.bounds;
|
|
612
|
+
canvasContext.save();
|
|
613
|
+
canvasContext.beginPath();
|
|
614
|
+
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
615
|
+
canvasContext.clip();
|
|
616
|
+
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
|
|
617
|
+
gridCellArgs.preventDefault();
|
|
618
|
+
canvasContext.restore();
|
|
619
|
+
});
|
|
620
|
+
grid.onCellTooltip((cell, x, y) => {
|
|
621
|
+
if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
|
|
622
|
+
this.showTooltipCluster(cell.cell.value, x, y);
|
|
623
|
+
return true;
|
|
539
624
|
});
|
|
540
625
|
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
541
626
|
webLogoGridCol.cellType = 'html';
|
|
@@ -544,6 +629,21 @@ export class PeptidesModel {
|
|
|
544
629
|
return grid;
|
|
545
630
|
}
|
|
546
631
|
|
|
632
|
+
modifyClusterSelection(cluster: number): void {
|
|
633
|
+
const tempSelection = this.logoSummarySelection;
|
|
634
|
+
const idx = tempSelection.indexOf(cluster);
|
|
635
|
+
if (idx !== -1)
|
|
636
|
+
tempSelection.splice(idx, 1);
|
|
637
|
+
else
|
|
638
|
+
tempSelection.push(cluster);
|
|
639
|
+
|
|
640
|
+
this.logoSummarySelection = tempSelection;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
initClusterSelection(cluster: number): void {
|
|
644
|
+
this.logoSummarySelection = [cluster];
|
|
645
|
+
}
|
|
646
|
+
|
|
547
647
|
setBarChartInteraction(): void {
|
|
548
648
|
const eventAction = (ev: MouseEvent): void => {
|
|
549
649
|
const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
@@ -578,8 +678,9 @@ export class PeptidesModel {
|
|
|
578
678
|
const monomer = barPart.monomer;
|
|
579
679
|
const position = barPart.position;
|
|
580
680
|
if (ev.type === 'click') {
|
|
581
|
-
ev.shiftKey ? this.
|
|
582
|
-
this.
|
|
681
|
+
ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
|
|
682
|
+
this.initMonomerPositionSelection(monomer, position, true);
|
|
683
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
583
684
|
} else {
|
|
584
685
|
const bar = `${monomer}:${position}`;
|
|
585
686
|
if (this.cachedBarchartTooltip.bar == bar)
|
|
@@ -590,7 +691,7 @@ export class PeptidesModel {
|
|
|
590
691
|
}
|
|
591
692
|
|
|
592
693
|
setCellRenderers(renderColNames: string[]): void {
|
|
593
|
-
const mdCol = this.
|
|
694
|
+
const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
594
695
|
//decompose into two different renering funcs
|
|
595
696
|
const renderCell = (args: DG.GridCellRenderArgs): void => {
|
|
596
697
|
const canvasContext = args.g;
|
|
@@ -622,14 +723,16 @@ export class PeptidesModel {
|
|
|
622
723
|
|
|
623
724
|
const viewer = this.getViewer();
|
|
624
725
|
if (this.isInvariantMap) {
|
|
625
|
-
const value: number = this.
|
|
726
|
+
const value: number = this.monomerPositionStatsDf
|
|
626
727
|
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
|
|
627
728
|
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
628
729
|
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
629
|
-
renderInvaraintMapCell(
|
|
730
|
+
CR.renderInvaraintMapCell(
|
|
731
|
+
canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
|
|
630
732
|
} else {
|
|
631
|
-
renderMutationCliffCell(
|
|
632
|
-
|
|
733
|
+
CR.renderMutationCliffCell(
|
|
734
|
+
canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf, viewer.bidirectionalAnalysis,
|
|
735
|
+
mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
|
|
633
736
|
}
|
|
634
737
|
}
|
|
635
738
|
args.preventDefault();
|
|
@@ -651,7 +754,7 @@ export class PeptidesModel {
|
|
|
651
754
|
context.clip();
|
|
652
755
|
|
|
653
756
|
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);
|
|
757
|
+
const barBounds = CR.renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
|
|
655
758
|
this.barsBounds[col.name] = barBounds;
|
|
656
759
|
gcArgs.preventDefault();
|
|
657
760
|
}
|
|
@@ -708,8 +811,9 @@ export class PeptidesModel {
|
|
|
708
811
|
}
|
|
709
812
|
|
|
710
813
|
showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
|
|
711
|
-
const currentStatsDf = this.
|
|
814
|
+
const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
|
|
712
815
|
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
816
|
+
//TODO: use bitset instead of splitCol
|
|
713
817
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
714
818
|
const currentPosCol = this.df.getCol(position);
|
|
715
819
|
splitCol.init((i) => currentPosCol.get(i) == aar);
|
|
@@ -730,15 +834,40 @@ export class PeptidesModel {
|
|
|
730
834
|
return tooltip;
|
|
731
835
|
}
|
|
732
836
|
|
|
837
|
+
showTooltipCluster(cluster: number, x: number, y: number): HTMLDivElement | null {
|
|
838
|
+
const currentStatsDf = this.clusterStatsDf.rows.match({clusters: cluster}).toDataFrame();
|
|
839
|
+
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
840
|
+
//TODO: use bitset instead of splitCol
|
|
841
|
+
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
842
|
+
const currentClusterCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
843
|
+
splitCol.init((i) => currentClusterCol.get(i) == cluster);
|
|
844
|
+
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
845
|
+
const stats: Stats = {
|
|
846
|
+
count: currentStatsDf.get(C.COLUMNS_NAMES.COUNT, 0),
|
|
847
|
+
ratio: currentStatsDf.get(C.COLUMNS_NAMES.RATIO, 0),
|
|
848
|
+
pValue: currentStatsDf.get(C.COLUMNS_NAMES.P_VALUE, 0),
|
|
849
|
+
meanDifference: currentStatsDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, 0),
|
|
850
|
+
};
|
|
851
|
+
if (!stats.count)
|
|
852
|
+
return null;
|
|
853
|
+
|
|
854
|
+
const tooltip = getDistributionAndStats(distributionTable, stats, `Cluster: ${cluster}`, 'Other', true);
|
|
855
|
+
|
|
856
|
+
ui.tooltip.show(tooltip, x, y);
|
|
857
|
+
|
|
858
|
+
return tooltip;
|
|
859
|
+
}
|
|
860
|
+
|
|
733
861
|
setInteractionCallback(): void {
|
|
734
862
|
const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
|
|
735
863
|
const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
|
|
736
|
-
// const invariantMapDf = this.invariantMapGrid.dataFrame;
|
|
737
864
|
|
|
738
865
|
const chooseAction =
|
|
739
|
-
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void =>
|
|
740
|
-
isShiftPressed ? this.
|
|
741
|
-
this.
|
|
866
|
+
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
|
|
867
|
+
isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
|
|
868
|
+
this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
|
|
869
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
870
|
+
};
|
|
742
871
|
|
|
743
872
|
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
744
873
|
const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
@@ -758,7 +887,7 @@ export class PeptidesModel {
|
|
|
758
887
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
759
888
|
const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
760
889
|
const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
761
|
-
chooseAction(aar, position, ev.shiftKey);
|
|
890
|
+
chooseAction(aar, position, ev.shiftKey, false);
|
|
762
891
|
});
|
|
763
892
|
|
|
764
893
|
const cellChanged = (table: DG.DataFrame): void => {
|
|
@@ -772,17 +901,14 @@ export class PeptidesModel {
|
|
|
772
901
|
this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
|
|
773
902
|
}
|
|
774
903
|
|
|
775
|
-
|
|
904
|
+
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
776
905
|
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
|
-
}
|
|
906
|
+
const tempSelectionAt = tempSelection[position];
|
|
907
|
+
const aarIndex = tempSelectionAt.indexOf(aar);
|
|
908
|
+
if (aarIndex === -1)
|
|
909
|
+
tempSelectionAt.push(aar);
|
|
910
|
+
else
|
|
911
|
+
tempSelectionAt.splice(aarIndex, 1);
|
|
786
912
|
|
|
787
913
|
if (isInvariantMapSelection)
|
|
788
914
|
this.invariantMapSelection = tempSelection;
|
|
@@ -790,8 +916,10 @@ export class PeptidesModel {
|
|
|
790
916
|
this.mutationCliffsSelection = tempSelection;
|
|
791
917
|
}
|
|
792
918
|
|
|
793
|
-
|
|
794
|
-
const tempSelection
|
|
919
|
+
initMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
920
|
+
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
921
|
+
for (const key of Object.keys(tempSelection))
|
|
922
|
+
tempSelection[key] = [];
|
|
795
923
|
tempSelection[position] = [aar];
|
|
796
924
|
|
|
797
925
|
if (isInvariantMapSelection)
|
|
@@ -803,8 +931,8 @@ export class PeptidesModel {
|
|
|
803
931
|
invalidateGrids(): void {
|
|
804
932
|
this.mutationCliffsGrid.invalidate();
|
|
805
933
|
this.mostPotentResiduesGrid.invalidate();
|
|
934
|
+
this.logoSummaryGrid?.invalidate();
|
|
806
935
|
this.sourceGrid?.invalidate();
|
|
807
|
-
//TODO: this.peptideSpaceGrid.invalidate();
|
|
808
936
|
}
|
|
809
937
|
|
|
810
938
|
setBitsetCallback(): void {
|
|
@@ -812,6 +940,7 @@ export class PeptidesModel {
|
|
|
812
940
|
return;
|
|
813
941
|
const selection = this.df.selection;
|
|
814
942
|
const filter = this.df.filter;
|
|
943
|
+
const clusterCol = this.df.col(C.COLUMNS_NAMES.CLUSTERS);
|
|
815
944
|
|
|
816
945
|
const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
|
|
817
946
|
const edfSelection = this.edf?.selection;
|
|
@@ -830,11 +959,6 @@ export class PeptidesModel {
|
|
|
830
959
|
};
|
|
831
960
|
|
|
832
961
|
const positionList = Object.keys(this.mutationCliffsSelection);
|
|
833
|
-
if (positionList.length == 0) {
|
|
834
|
-
currentBitset.init(() => false, false);
|
|
835
|
-
updateEdfSelection();
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
962
|
|
|
839
963
|
//TODO: move out
|
|
840
964
|
const getBitAt = (i: number): boolean => {
|
|
@@ -843,6 +967,8 @@ export class PeptidesModel {
|
|
|
843
967
|
if (this._mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
844
968
|
return true;
|
|
845
969
|
}
|
|
970
|
+
if (this._logoSummarySelection.includes(clusterCol?.get(i)!))
|
|
971
|
+
return true;
|
|
846
972
|
return false;
|
|
847
973
|
};
|
|
848
974
|
currentBitset.init(getBitAt, false);
|
|
@@ -885,10 +1011,9 @@ export class PeptidesModel {
|
|
|
885
1011
|
for (let i = 0; i < colNum; ++i) {
|
|
886
1012
|
const col = girdCols.byIndex(i)!;
|
|
887
1013
|
const colName = col.name;
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
col.width = gridProps.rowHeight + 10;
|
|
1014
|
+
col.width =
|
|
1015
|
+
grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER ? 50 :
|
|
1016
|
+
gridProps.rowHeight + 10;
|
|
892
1017
|
}
|
|
893
1018
|
}
|
|
894
1019
|
|
|
@@ -951,7 +1076,9 @@ export class PeptidesModel {
|
|
|
951
1076
|
return;
|
|
952
1077
|
|
|
953
1078
|
this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
954
|
-
this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)
|
|
1079
|
+
const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
|
|
1080
|
+
scaledGridCol.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
|
|
1081
|
+
scaledGridCol.format = '#.000';
|
|
955
1082
|
this.sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
|
|
956
1083
|
this.sourceGrid.props.allowColSelection = false;
|
|
957
1084
|
|
|
@@ -966,10 +1093,9 @@ export class PeptidesModel {
|
|
|
966
1093
|
};
|
|
967
1094
|
|
|
968
1095
|
for (let i = 0; i < this.sourceGrid.columns.length; i++) {
|
|
969
|
-
const
|
|
970
|
-
if (
|
|
971
|
-
|
|
972
|
-
aarCol.visible = false;
|
|
1096
|
+
const currentCol = this.sourceGrid.columns.byIndex(i);
|
|
1097
|
+
if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
|
|
1098
|
+
currentCol.visible = false;
|
|
973
1099
|
}
|
|
974
1100
|
|
|
975
1101
|
const options = {scaling: this.df.tags['scaling']};
|
|
@@ -986,14 +1112,6 @@ export class PeptidesModel {
|
|
|
986
1112
|
dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
987
1113
|
}
|
|
988
1114
|
|
|
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
1115
|
this.updateDefault();
|
|
998
1116
|
|
|
999
1117
|
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
|
|