@datagrok/peptides 1.8.2 → 1.9.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/478.js +2 -2
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +3 -3
- package/src/demo/fasta.ts +1 -1
- package/src/model.ts +39 -21
- package/src/package.ts +1 -0
- package/src/tests/algorithms.ts +20 -12
- package/src/tests/core.ts +6 -6
- package/src/tests/viewers.ts +1 -1
- package/src/utils/algorithms.ts +14 -3
- package/src/utils/cell-renderer.ts +2 -2
- package/src/utils/misc.ts +6 -5
- package/src/utils/types.ts +1 -0
- package/src/viewers/sar-viewer.ts +25 -8
- package/src/widgets/mutation-cliffs.ts +20 -11
- package/src/widgets/peptides.ts +22 -14
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
3
|
"friendlyName": "Peptides",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Volodymyr Dyma",
|
|
7
7
|
"email": "vdyma@datagrok.ai"
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@datagrok-libraries/bio": "^5.29.3",
|
|
17
17
|
"@datagrok-libraries/ml": "^6.3.3",
|
|
18
18
|
"@datagrok-libraries/statistics": "^1.1.7",
|
|
19
|
-
"@datagrok-libraries/utils": "^4.0.
|
|
20
|
-
"@datagrok-libraries/tutorials": "^1.3.
|
|
19
|
+
"@datagrok-libraries/utils": "^4.0.7",
|
|
20
|
+
"@datagrok-libraries/tutorials": "^1.3.2",
|
|
21
21
|
"cash-dom": "^8.1.2",
|
|
22
22
|
"datagrok-api": "^1.6.0",
|
|
23
23
|
"file-loader": "^6.2.0",
|
package/src/demo/fasta.ts
CHANGED
|
@@ -20,7 +20,7 @@ export async function macromoleculeSarFastaDemoUI(): Promise<void> {
|
|
|
20
20
|
simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
|
|
21
21
|
simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
22
22
|
simpleAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
23
|
-
const simpleScaledCol = scaleActivity(simpleActivityCol,
|
|
23
|
+
const simpleScaledCol = scaleActivity(simpleActivityCol, C.SCALING_METHODS.MINUS_LG);
|
|
24
24
|
|
|
25
25
|
demo.step('Load data', async () => {grok.shell.addTableView(simpleTable);},
|
|
26
26
|
{description: 'Load the dataset containing macromolecule sequences. Notice how Datagrok detects macromolecules ' +
|
package/src/model.ts
CHANGED
|
@@ -19,8 +19,8 @@ import * as uuid from 'uuid';
|
|
|
19
19
|
|
|
20
20
|
import * as C from './utils/constants';
|
|
21
21
|
import * as type from './utils/types';
|
|
22
|
-
import {calculateSelected,
|
|
23
|
-
import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
22
|
+
import {calculateSelected, extractColInfo, scaleActivity, getStatsSummary} from './utils/misc';
|
|
23
|
+
import {MONOMER_POSITION_PROPERTIES, MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
24
24
|
import * as CR from './utils/cell-renderer';
|
|
25
25
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
26
26
|
import {getActivityDistribution, getDistributionLegend, getDistributionWidget, getStatsTableMap,
|
|
@@ -39,8 +39,8 @@ export type SummaryStats = {
|
|
|
39
39
|
minPValue: number, maxPValue: number,
|
|
40
40
|
minRatio: number, maxRatio: number,
|
|
41
41
|
};
|
|
42
|
-
export type PositionStats = {
|
|
43
|
-
export type MonomerPositionStats = {
|
|
42
|
+
export type PositionStats = {[monomer: string]: Stats} & {general: SummaryStats};
|
|
43
|
+
export type MonomerPositionStats = {[position: string]: PositionStats} & {general: SummaryStats};
|
|
44
44
|
export type ClusterStats = {[cluster: string]: Stats};
|
|
45
45
|
export enum CLUSTER_TYPE {
|
|
46
46
|
ORIGINAL = 'original',
|
|
@@ -78,11 +78,11 @@ export class PeptidesModel {
|
|
|
78
78
|
_monomerPositionSelection!: type.PositionToAARList;
|
|
79
79
|
_monomerPositionFilter!: type.PositionToAARList;
|
|
80
80
|
_clusterSelection!: string[];
|
|
81
|
-
_mutationCliffs
|
|
81
|
+
_mutationCliffs: type.MutationCliffs | null = null;
|
|
82
82
|
isInitialized = false;
|
|
83
83
|
_analysisView?: DG.TableView;
|
|
84
84
|
|
|
85
|
-
monomerMap: {
|
|
85
|
+
monomerMap: {[key: string]: {molfile: string, fullName: string}} = {};
|
|
86
86
|
monomerLib: IMonomerLib | null = null; // To get monomers from lib(s)
|
|
87
87
|
monomerWorks: MonomerWorks | null = null; // To get processed monomers
|
|
88
88
|
|
|
@@ -93,8 +93,8 @@ export class PeptidesModel {
|
|
|
93
93
|
initBitset: DG.BitSet;
|
|
94
94
|
isInvariantMapTrigger: boolean = false;
|
|
95
95
|
headerSelectedMonomers: type.MonomerSelectionStats = {};
|
|
96
|
-
webLogoBounds: {
|
|
97
|
-
cachedWebLogoTooltip: {
|
|
96
|
+
webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
|
|
97
|
+
cachedWebLogoTooltip: {bar: string, tooltip: HTMLDivElement | null} = {bar: '', tooltip: null};
|
|
98
98
|
_monomerPositionDf?: DG.DataFrame;
|
|
99
99
|
_alphabet?: string;
|
|
100
100
|
_mostPotentResiduesDf?: DG.DataFrame;
|
|
@@ -170,18 +170,15 @@ export class PeptidesModel {
|
|
|
170
170
|
return col.getTag(bioTAGS.alphabet);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
get mutationCliffs(): type.MutationCliffs {
|
|
174
|
-
if (this._mutationCliffs)
|
|
175
|
-
|
|
173
|
+
get mutationCliffs(): type.MutationCliffs | null {
|
|
174
|
+
// if (this._mutationCliffs)
|
|
175
|
+
// return this._mutationCliffs;
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
180
|
-
this._mutationCliffs = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
|
|
181
|
-
return this._mutationCliffs;
|
|
177
|
+
// this.updateMutationCliffs(false);
|
|
178
|
+
return this._mutationCliffs!;
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
set mutationCliffs(si: type.MutationCliffs) {
|
|
181
|
+
set mutationCliffs(si: type.MutationCliffs | null) {
|
|
185
182
|
this._mutationCliffs = si;
|
|
186
183
|
}
|
|
187
184
|
|
|
@@ -354,10 +351,7 @@ export class PeptidesModel {
|
|
|
354
351
|
this.createScaledCol();
|
|
355
352
|
break;
|
|
356
353
|
case 'mutationCliffs':
|
|
357
|
-
|
|
358
|
-
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
359
|
-
const monomerCols: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
360
|
-
this.mutationCliffs = findMutations(scaledActivityCol.getRawData(), monomerCols, this.settings);
|
|
354
|
+
this.updateMutationCliffs();
|
|
361
355
|
break;
|
|
362
356
|
case 'stats':
|
|
363
357
|
this.monomerPositionStats = this.calculateMonomerPositionStatistics();
|
|
@@ -390,6 +384,22 @@ export class PeptidesModel {
|
|
|
390
384
|
this._settingsSubject.next(this.settings);
|
|
391
385
|
}
|
|
392
386
|
|
|
387
|
+
updateMutationCliffs(notify: boolean = true): void {
|
|
388
|
+
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
389
|
+
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
390
|
+
const monomerCols: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractColInfo);
|
|
391
|
+
const targetCol = typeof this.settings.targetColumnName !== 'undefined' ?
|
|
392
|
+
extractColInfo(this.df.getCol(this.settings.targetColumnName)) : null;
|
|
393
|
+
const mpViewer = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
394
|
+
const currentTarget = mpViewer?.getProperty(MONOMER_POSITION_PROPERTIES.TARGET)?.get(mpViewer);
|
|
395
|
+
const targetOptions = {targetCol: targetCol, currentTarget: currentTarget};
|
|
396
|
+
const mutationCliffs = findMutations(scaledActivityCol.getRawData(), monomerCols, this.settings, targetOptions);
|
|
397
|
+
if (notify)
|
|
398
|
+
this.mutationCliffs = mutationCliffs;
|
|
399
|
+
else
|
|
400
|
+
this._mutationCliffs = mutationCliffs;
|
|
401
|
+
}
|
|
402
|
+
|
|
393
403
|
createMonomerPositionDf(): DG.DataFrame {
|
|
394
404
|
const positions = this.splitSeqDf.columns.names();
|
|
395
405
|
const matrixDf = this.matrixDf
|
|
@@ -1097,6 +1107,8 @@ export class PeptidesModel {
|
|
|
1097
1107
|
this.updateGrid();
|
|
1098
1108
|
this.fireBitsetChanged(true);
|
|
1099
1109
|
this.analysisView.grid.invalidate();
|
|
1110
|
+
if (typeof this.settings.targetColumnName === 'undefined')
|
|
1111
|
+
this.updateMutationCliffs();
|
|
1100
1112
|
}
|
|
1101
1113
|
|
|
1102
1114
|
findViewer(viewerType: VIEWER_TYPE): DG.Viewer | null {
|
|
@@ -1121,6 +1133,12 @@ export class PeptidesModel {
|
|
|
1121
1133
|
const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1122
1134
|
[DG.DOCK_TYPE.LEFT, this.findViewerNode(VIEWER_TYPE.MOST_POTENT_RESIDUES), 0.7];
|
|
1123
1135
|
dm.dock(monomerPosition, dockType, refNode, VIEWER_TYPE.MONOMER_POSITION, ratio);
|
|
1136
|
+
if (typeof this.settings.targetColumnName !== 'undefined') {
|
|
1137
|
+
const target = monomerPosition.getProperty(MONOMER_POSITION_PROPERTIES.TARGET)!;
|
|
1138
|
+
const choices = this.df.getCol(this.settings.targetColumnName!).categories;
|
|
1139
|
+
target.choices = choices;
|
|
1140
|
+
target.set(monomerPosition, choices[0]);
|
|
1141
|
+
}
|
|
1124
1142
|
}
|
|
1125
1143
|
|
|
1126
1144
|
async addMostPotentResidues(): Promise<void> {
|
package/src/package.ts
CHANGED
|
@@ -176,6 +176,7 @@ export async function peptideSpacePanel(col: DG.Column): Promise<DG.Widget> {
|
|
|
176
176
|
//name: Macromolecule SAR Analysis
|
|
177
177
|
//description: Macromolecule SAR Analysis demo on peptide sequences in FASTA format
|
|
178
178
|
//meta.demoPath: Bioinformatics | Macromolecule SAR Analysis
|
|
179
|
+
//meta.isDemoScript: True
|
|
179
180
|
export async function macromoleculeSarFastaDemo(): Promise<void> {
|
|
180
181
|
return macromoleculeSarFastaDemoUI();
|
|
181
182
|
}
|
package/src/tests/algorithms.ts
CHANGED
|
@@ -5,11 +5,13 @@ import {category, test, expect, before} from '@datagrok-libraries/utils/src/test
|
|
|
5
5
|
import {_package} from '../package-test';
|
|
6
6
|
import {findMutations} from '../utils/algorithms';
|
|
7
7
|
import * as type from '../utils/types';
|
|
8
|
+
import {extractColInfo} from '../utils/misc';
|
|
8
9
|
|
|
9
10
|
category('Algorithms', () => {
|
|
10
11
|
let activityCol: type.RawData;
|
|
11
12
|
let monomerColumns: type.RawColumn[];
|
|
12
13
|
let settings: type.PeptidesSettings;
|
|
14
|
+
let targetCol: type.RawColumn;
|
|
13
15
|
|
|
14
16
|
before(async () => {
|
|
15
17
|
activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]).getRawData();
|
|
@@ -22,29 +24,35 @@ category('Algorithms', () => {
|
|
|
22
24
|
rawData: col.getRawData(),
|
|
23
25
|
cat: col.categories,
|
|
24
26
|
}));
|
|
27
|
+
targetCol = extractColInfo(DG.Column.fromList('string', 'target', ['1', '2', '2']));
|
|
25
28
|
settings = {maxMutations: 1, minActivityDelta: 2};
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
test('MutationCliffs', async () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
32
|
+
// Check every pair
|
|
33
|
+
let mutationCliffsInfo: type.MutationCliffs = findMutations(activityCol, monomerColumns, settings);
|
|
34
|
+
expect(mutationCliffsInfo.has('C'), true, `MutationCliffsInfo should have key 'C'`);
|
|
35
|
+
expect(mutationCliffsInfo.has('D'), true, `MutationCliffsInfo should have key 'D'`);
|
|
36
|
+
expect(mutationCliffsInfo.has('A'), false, `MutationCliffsInfo should not have key 'A'`);
|
|
33
37
|
|
|
34
|
-
const c =
|
|
35
|
-
const d =
|
|
36
|
-
expect(c.has('3'), true);
|
|
37
|
-
expect(d.has('3'), true);
|
|
38
|
+
const c = mutationCliffsInfo.get('C')!;
|
|
39
|
+
const d = mutationCliffsInfo.get('D')!;
|
|
40
|
+
expect(c.has('3'), true, `MutationCliffsInfo['C'] should have key '3'`);
|
|
41
|
+
expect(d.has('3'), true, `MutationCliffsInfo['D'] should have key '3'`);
|
|
38
42
|
|
|
39
43
|
const c3 = c.get('3')!;
|
|
40
44
|
const d3 = d.get('3')!;
|
|
41
|
-
expect(c3.has(1), true);
|
|
42
|
-
expect(d3.has(2), true);
|
|
45
|
+
expect(c3.has(1), true, `MutationCliffsInfo['C']['3'] should have key 1`);
|
|
46
|
+
expect(d3.has(2), true, `MutationCliffsInfo['D']['3'] should have key 2`);
|
|
43
47
|
|
|
44
48
|
const c31 = c3.get(1)!;
|
|
45
49
|
const d32 = d3.get(2)!;
|
|
46
|
-
expect(c31[0], 2);
|
|
47
|
-
expect(d32[0], 1);
|
|
50
|
+
expect(c31[0], 2, `MutationCliffsInfo['C']['3'][1] should have value 2`);
|
|
51
|
+
expect(d32[0], 1, `MutationCliffsInfo['D']['3'][2] should have value 1`);
|
|
52
|
+
|
|
53
|
+
// Check with target
|
|
54
|
+
mutationCliffsInfo = findMutations(activityCol, monomerColumns, settings, {targetCol, currentTarget: '1'});
|
|
55
|
+
expect(mutationCliffsInfo.size, 0, `MutationCliffsInfo should be empty for target '1'`);
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
test('MutationCliffs - Benchmark 5k', async () => {
|
package/src/tests/core.ts
CHANGED
|
@@ -33,7 +33,7 @@ category('Core', () => {
|
|
|
33
33
|
simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
|
|
34
34
|
simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
35
35
|
simpleAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
36
|
-
simpleScaledCol = scaleActivity(simpleActivityCol,
|
|
36
|
+
simpleScaledCol = scaleActivity(simpleActivityCol, C.SCALING_METHODS.MINUS_LG);
|
|
37
37
|
|
|
38
38
|
model = await startAnalysis(
|
|
39
39
|
simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, C.SCALING_METHODS.MINUS_LG);
|
|
@@ -53,7 +53,7 @@ category('Core', () => {
|
|
|
53
53
|
complexAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.SEPARATOR);
|
|
54
54
|
complexAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
55
55
|
complexAlignedSeqCol.setTag(C.TAGS.SEPARATOR, '/');
|
|
56
|
-
complexScaledCol = scaleActivity(complexActivityCol,
|
|
56
|
+
complexScaledCol = scaleActivity(complexActivityCol, C.SCALING_METHODS.MINUS_LG);
|
|
57
57
|
|
|
58
58
|
model = await startAnalysis(
|
|
59
59
|
complexActivityCol, complexAlignedSeqCol, null, complexTable, complexScaledCol, C.SCALING_METHODS.MINUS_LG);
|
|
@@ -72,7 +72,7 @@ category('Core', () => {
|
|
|
72
72
|
simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
|
|
73
73
|
simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
74
74
|
simpleAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
75
|
-
simpleScaledCol = scaleActivity(simpleActivityCol,
|
|
75
|
+
simpleScaledCol = scaleActivity(simpleActivityCol, C.SCALING_METHODS.MINUS_LG);
|
|
76
76
|
|
|
77
77
|
model = await startAnalysis(
|
|
78
78
|
simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, C.SCALING_METHODS.MINUS_LG);
|
|
@@ -104,7 +104,7 @@ category('Core', () => {
|
|
|
104
104
|
test('Cluster stats - Benchmark HELM 5k', async () => {
|
|
105
105
|
const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k_2.d42'))[0];
|
|
106
106
|
const activityCol = df.getCol('Activity');
|
|
107
|
-
const scaledActivityCol = scaleActivity(activityCol,
|
|
107
|
+
const scaledActivityCol = scaleActivity(activityCol, C.SCALING_METHODS.NONE);
|
|
108
108
|
const clustersCol = df.getCol('Cluster');
|
|
109
109
|
const sequenceCol = df.getCol('HELM');
|
|
110
110
|
sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
@@ -119,7 +119,7 @@ category('Core', () => {
|
|
|
119
119
|
test('Monomer Position stats - Benchmark HELM 5k', async () => {
|
|
120
120
|
const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k.d42'))[0];
|
|
121
121
|
const activityCol = df.getCol('Activity');
|
|
122
|
-
const scaledActivityCol = scaleActivity(activityCol,
|
|
122
|
+
const scaledActivityCol = scaleActivity(activityCol, C.SCALING_METHODS.NONE);
|
|
123
123
|
const clustersCol = df.getCol('Cluster');
|
|
124
124
|
const sequenceCol = df.getCol('HELM');
|
|
125
125
|
sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
@@ -134,7 +134,7 @@ category('Core', () => {
|
|
|
134
134
|
test('Analysis start - Benchmark HELM 5k', async () => {
|
|
135
135
|
const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k.d42'))[0];
|
|
136
136
|
const activityCol = df.getCol('Activity');
|
|
137
|
-
const scaledActivityCol = scaleActivity(activityCol,
|
|
137
|
+
const scaledActivityCol = scaleActivity(activityCol, C.SCALING_METHODS.NONE);
|
|
138
138
|
const clustersCol = df.getCol('Cluster');
|
|
139
139
|
const sequenceCol = df.getCol('HELM');
|
|
140
140
|
sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
package/src/tests/viewers.ts
CHANGED
|
@@ -18,7 +18,7 @@ category('Viewers: Basic', () => {
|
|
|
18
18
|
const viewers = DG.Func.find({package: 'Peptides', tags: ['viewer']}).map((f) => f.friendlyName);
|
|
19
19
|
for (const v of viewers) {
|
|
20
20
|
test(v, async () => {
|
|
21
|
-
await testViewer(v, df.clone(), true);
|
|
21
|
+
await testViewer(v, df.clone(), {detectSemanticTypes: true});
|
|
22
22
|
}, {skipReason: 'GROK-11534'});
|
|
23
23
|
}
|
|
24
24
|
});
|
package/src/utils/algorithms.ts
CHANGED
|
@@ -5,20 +5,31 @@ import {getTypedArrayConstructor} from './misc';
|
|
|
5
5
|
type MutationCliffInfo = {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number};
|
|
6
6
|
|
|
7
7
|
export function findMutations(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
|
|
8
|
-
settings: type.PeptidesSettings = {}
|
|
8
|
+
settings: type.PeptidesSettings = {},
|
|
9
|
+
targetOptions: {targetCol?: type.RawColumn | null, currentTarget?: string | null} = {}): type.MutationCliffs {
|
|
9
10
|
const nCols = monomerInfoArray.length;
|
|
10
11
|
if (nCols == 0)
|
|
11
12
|
throw new Error(`PepAlgorithmError: Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
|
|
12
13
|
|
|
14
|
+
settings.minActivityDelta ??= 0;
|
|
15
|
+
settings.maxMutations ??= 1;
|
|
16
|
+
const currentTargetIdx = targetOptions.targetCol?.cat!.indexOf(targetOptions.currentTarget!) ?? -1;
|
|
17
|
+
|
|
13
18
|
const substitutionsInfo: type.MutationCliffs = new Map();
|
|
14
19
|
const nRows = activityArray.length;
|
|
15
20
|
for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
|
|
21
|
+
if (currentTargetIdx !== -1 && targetOptions.targetCol?.rawData[seq1Idx] !== currentTargetIdx)
|
|
22
|
+
continue;
|
|
23
|
+
|
|
16
24
|
for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
|
|
25
|
+
if (currentTargetIdx !== -1 && targetOptions.targetCol?.rawData[seq2Idx] !== currentTargetIdx)
|
|
26
|
+
continue;
|
|
27
|
+
|
|
17
28
|
let substCounter = 0;
|
|
18
29
|
const activityValSeq1 = activityArray[seq1Idx];
|
|
19
30
|
const activityValSeq2 = activityArray[seq2Idx];
|
|
20
31
|
const delta = activityValSeq1 - activityValSeq2;
|
|
21
|
-
if (Math.abs(delta) <
|
|
32
|
+
if (Math.abs(delta) < settings.minActivityDelta)
|
|
22
33
|
continue;
|
|
23
34
|
|
|
24
35
|
let substCounterFlag = false;
|
|
@@ -30,7 +41,7 @@ export function findMutations(activityArray: type.RawData, monomerInfoArray: typ
|
|
|
30
41
|
continue;
|
|
31
42
|
|
|
32
43
|
substCounter++;
|
|
33
|
-
substCounterFlag = substCounter >
|
|
44
|
+
substCounterFlag = substCounter > settings.maxMutations;
|
|
34
45
|
if (substCounterFlag)
|
|
35
46
|
break;
|
|
36
47
|
|
|
@@ -21,7 +21,7 @@ export function setAARRenderer(col: DG.Column, alphabet: string): void {
|
|
|
21
21
|
|
|
22
22
|
export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
|
|
23
23
|
currentPosition: string, monomerPositionStats: MonomerPositionStats, bound: DG.Rect,
|
|
24
|
-
mutationCliffsSelection: types.PositionToAARList, substitutionsInfo: types.MutationCliffs,
|
|
24
|
+
mutationCliffsSelection: types.PositionToAARList, substitutionsInfo: types.MutationCliffs | null = null,
|
|
25
25
|
twoColorMode: boolean = false): void {
|
|
26
26
|
const positionStats = monomerPositionStats[currentPosition];
|
|
27
27
|
const pVal: number = positionStats[currentAAR].pValue;
|
|
@@ -58,7 +58,7 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
|
58
58
|
canvasContext.closePath();
|
|
59
59
|
|
|
60
60
|
canvasContext.fill();
|
|
61
|
-
if (substitutionsInfo.size > 0) {
|
|
61
|
+
if (substitutionsInfo !== null && substitutionsInfo.size > 0) {
|
|
62
62
|
canvasContext.textBaseline = 'middle';
|
|
63
63
|
canvasContext.textAlign = 'center';
|
|
64
64
|
canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
|
package/src/utils/misc.ts
CHANGED
|
@@ -15,15 +15,16 @@ export function getSeparator(col: DG.Column<string>): string {
|
|
|
15
15
|
return col.getTag(C.TAGS.SEPARATOR) ?? '';
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function scaleActivity(activityCol: DG.Column<number>, scaling:
|
|
18
|
+
export function scaleActivity(activityCol: DG.Column<number>, scaling: C.SCALING_METHODS = C.SCALING_METHODS.NONE,
|
|
19
|
+
): DG.Column<number> {
|
|
19
20
|
let formula = (x: number): number => x;
|
|
20
21
|
switch (scaling) {
|
|
21
|
-
case
|
|
22
|
+
case C.SCALING_METHODS.NONE:
|
|
22
23
|
break;
|
|
23
|
-
case
|
|
24
|
+
case C.SCALING_METHODS.LG:
|
|
24
25
|
formula = (x: number): number => Math.log10(x);
|
|
25
26
|
break;
|
|
26
|
-
case
|
|
27
|
+
case C.SCALING_METHODS.MINUS_LG:
|
|
27
28
|
formula = (x: number): number => -Math.log10(x);
|
|
28
29
|
break;
|
|
29
30
|
default:
|
|
@@ -62,7 +63,7 @@ export function calculateSelected(df: DG.DataFrame): type.MonomerSelectionStats
|
|
|
62
63
|
// gc.cell.value == DG.INT_NULL || gc.cell.value == DG.FLOAT_NULL;
|
|
63
64
|
// }
|
|
64
65
|
|
|
65
|
-
export function
|
|
66
|
+
export function extractColInfo(col: DG.Column<string>): type.RawColumn {
|
|
66
67
|
return {
|
|
67
68
|
name: col.name,
|
|
68
69
|
cat: col.categories,
|
package/src/utils/types.ts
CHANGED
|
@@ -11,18 +11,29 @@ export enum MONOMER_POSITION_MODE {
|
|
|
11
11
|
INVARIANT_MAP = 'Invariant Map',
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export enum MONOMER_POSITION_PROPERTIES {
|
|
15
|
+
COLOR_COLUMN_NAME = 'colorColumnName',
|
|
16
|
+
AGGREGATION = 'aggregation',
|
|
17
|
+
TARGET = 'target',
|
|
18
|
+
};
|
|
19
|
+
|
|
14
20
|
/** Structure-activity relationship viewer */
|
|
15
21
|
export class MonomerPosition extends DG.JsViewer {
|
|
16
|
-
_titleHost = ui.divText(
|
|
22
|
+
_titleHost = ui.divText(MONOMER_POSITION_MODE.MUTATION_CLIFFS, {id: 'pep-viewer-title'});
|
|
17
23
|
_viewerGrid!: DG.Grid;
|
|
18
24
|
_model!: PeptidesModel;
|
|
19
25
|
colorColumnName: string;
|
|
20
26
|
aggregation: string;
|
|
27
|
+
target: string;
|
|
21
28
|
|
|
22
29
|
constructor() {
|
|
23
30
|
super();
|
|
24
|
-
this.
|
|
25
|
-
|
|
31
|
+
this.target = this.string(MONOMER_POSITION_PROPERTIES.TARGET, null,
|
|
32
|
+
{category: MONOMER_POSITION_MODE.MUTATION_CLIFFS, choices: []});
|
|
33
|
+
this.colorColumnName = this.string(MONOMER_POSITION_PROPERTIES.COLOR_COLUMN_NAME, C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
34
|
+
{category: MONOMER_POSITION_MODE.INVARIANT_MAP});
|
|
35
|
+
this.aggregation = this.string(MONOMER_POSITION_PROPERTIES.AGGREGATION, DG.AGG.AVG,
|
|
36
|
+
{category: MONOMER_POSITION_MODE.INVARIANT_MAP, choices: Object.values(DG.AGG)});
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
get name(): string {return VIEWER_TYPE.MONOMER_POSITION;}
|
|
@@ -65,6 +76,9 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
65
76
|
|
|
66
77
|
onPropertyChanged(property: DG.Property): void {
|
|
67
78
|
super.onPropertyChanged(property);
|
|
79
|
+
if (property.name === MONOMER_POSITION_PROPERTIES.TARGET)
|
|
80
|
+
this.model.updateMutationCliffs();
|
|
81
|
+
|
|
68
82
|
this.render();
|
|
69
83
|
}
|
|
70
84
|
|
|
@@ -105,14 +119,14 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
105
119
|
mutationCliffsMode.value = true;
|
|
106
120
|
this.mode = MONOMER_POSITION_MODE.MUTATION_CLIFFS;
|
|
107
121
|
});
|
|
108
|
-
mutationCliffsMode.addPostfix(
|
|
122
|
+
mutationCliffsMode.addPostfix(MONOMER_POSITION_MODE.MUTATION_CLIFFS);
|
|
109
123
|
const invariantMapMode = ui.boolInput('', this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP);
|
|
110
124
|
invariantMapMode.root.addEventListener('click', () => {
|
|
111
125
|
mutationCliffsMode.value = false;
|
|
112
126
|
invariantMapMode.value = true;
|
|
113
127
|
this.mode = MONOMER_POSITION_MODE.INVARIANT_MAP;
|
|
114
128
|
});
|
|
115
|
-
invariantMapMode.addPostfix(
|
|
129
|
+
invariantMapMode.addPostfix(MONOMER_POSITION_MODE.INVARIANT_MAP);
|
|
116
130
|
const setDefaultProperties = (input: DG.InputBase): void => {
|
|
117
131
|
$(input.root).find('.ui-input-editor').css('margin', '0px').attr('type', 'radio');
|
|
118
132
|
$(input.root).find('.ui-input-description').css('padding', '0px').css('padding-left', '5px');
|
|
@@ -236,7 +250,6 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
236
250
|
function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
|
|
237
251
|
colorCol?: DG.Column<number>, colorAgg?: DG.AggregationType): void {
|
|
238
252
|
const renderColNames = [...model.splitSeqDf.columns.names(), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
|
|
239
|
-
// const mdCol = model.monomerPositionStats.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
240
253
|
const canvasContext = args.g;
|
|
241
254
|
const bound = args.bounds;
|
|
242
255
|
|
|
@@ -323,8 +336,12 @@ export function showTooltip(cell: DG.GridCell, x: number, y: number, model: Pept
|
|
|
323
336
|
|
|
324
337
|
function chooseAction(aar: string, position: string, isShiftPressed: boolean, isFilter: boolean,
|
|
325
338
|
model: PeptidesModel): void {
|
|
326
|
-
if (!isShiftPressed)
|
|
327
|
-
|
|
339
|
+
if (!isShiftPressed) {
|
|
340
|
+
if (isFilter)
|
|
341
|
+
model.initMonomerPositionFilter({cleanInit: true, notify: false});
|
|
342
|
+
else
|
|
343
|
+
model.initMonomerPositionSelection({cleanInit: true, notify: false});
|
|
344
|
+
}
|
|
328
345
|
|
|
329
346
|
model.modifyMonomerPositionSelection(aar, position, isFilter);
|
|
330
347
|
}
|
|
@@ -6,53 +6,62 @@ import {PeptidesModel} from '../model';
|
|
|
6
6
|
import {getSeparator} from '../utils/misc';
|
|
7
7
|
|
|
8
8
|
export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
|
|
9
|
-
const
|
|
9
|
+
const filteredIndexes = table.filter.getSelectedIndexes();
|
|
10
10
|
const substInfo = model.mutationCliffs;
|
|
11
11
|
const currentCell = model.monomerPositionSelection;
|
|
12
12
|
const positions = Object.keys(currentCell);
|
|
13
13
|
|
|
14
|
-
if (!positions.length)
|
|
14
|
+
if (!positions.length || substInfo === null)
|
|
15
15
|
return new DG.Widget(ui.label('No mutations table generated'));
|
|
16
16
|
|
|
17
17
|
const substitutionsArray: string[] = [];
|
|
18
18
|
const deltaArray: number[] = [];
|
|
19
19
|
const substitutedToArray: string[] = [];
|
|
20
20
|
const alignedSeqCol = table.getCol(model.settings.sequenceColumnName!);
|
|
21
|
+
const alignedSeqColCategories = alignedSeqCol.categories;
|
|
22
|
+
const alignedSeqColData = alignedSeqCol.getRawData();
|
|
21
23
|
const activityScaledCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
24
|
+
const activityScaledColData = activityScaledCol.getRawData();
|
|
22
25
|
const seenIndexes = new Map<number, number[]>();
|
|
23
26
|
|
|
24
27
|
for (const pos of positions) {
|
|
28
|
+
const posCol = table.getCol(pos);
|
|
29
|
+
const posColCategories = posCol.categories;
|
|
30
|
+
const posColData = posCol.getRawData();
|
|
31
|
+
|
|
25
32
|
for (const aar of currentCell[pos]) {
|
|
26
33
|
const substitutionsMap = substInfo.get(aar)?.get(pos) as Map<number, type.UTypedArray> | undefined;
|
|
27
34
|
if (typeof substitutionsMap === 'undefined')
|
|
28
35
|
continue;
|
|
29
36
|
|
|
30
|
-
const posCol = table.getCol(pos);
|
|
31
37
|
for (const [referenceIdx, indexArray] of substitutionsMap.entries()) {
|
|
32
|
-
if (!
|
|
38
|
+
if (!filteredIndexes.includes(referenceIdx))
|
|
33
39
|
continue;
|
|
34
40
|
|
|
35
41
|
const forbiddentIndexes = seenIndexes.get(referenceIdx) ?? [];
|
|
36
|
-
const baseSequence = alignedSeqCol.get(referenceIdx);
|
|
37
|
-
const
|
|
42
|
+
// const baseSequence = alignedSeqCol.get(referenceIdx);
|
|
43
|
+
const baseSequence = alignedSeqColCategories[alignedSeqColData[referenceIdx]];
|
|
44
|
+
// const baseActivity = activityScaledCol.get(referenceIdx);
|
|
45
|
+
const baseActivity = activityScaledColData[referenceIdx];
|
|
38
46
|
|
|
39
47
|
for (const subIdx of indexArray) {
|
|
40
|
-
if (forbiddentIndexes.includes(subIdx) || !
|
|
48
|
+
if (forbiddentIndexes.includes(subIdx) || !filteredIndexes.includes(subIdx))
|
|
41
49
|
continue;
|
|
42
50
|
|
|
43
51
|
if (!seenIndexes.has(subIdx))
|
|
44
52
|
seenIndexes.set(subIdx, []);
|
|
53
|
+
const subSeq = alignedSeqColCategories[alignedSeqColData[subIdx]];
|
|
45
54
|
|
|
46
55
|
seenIndexes.get(subIdx)!.push(referenceIdx);
|
|
47
|
-
substitutionsArray.push(`${baseSequence}#${
|
|
48
|
-
deltaArray.push(baseActivity -
|
|
49
|
-
substitutedToArray.push(
|
|
56
|
+
substitutionsArray.push(`${baseSequence}#${subSeq}`);
|
|
57
|
+
deltaArray.push(baseActivity - activityScaledColData[subIdx]);
|
|
58
|
+
substitutedToArray.push(posColCategories[posColData[subIdx]]);
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
if (
|
|
64
|
+
if (substitutionsArray.length === 0)
|
|
56
65
|
return new DG.Widget(ui.label('No mutations table generated'));
|
|
57
66
|
|
|
58
67
|
const substCol = DG.Column.fromStrings('Mutation', substitutionsArray);
|
package/src/widgets/peptides.ts
CHANGED
|
@@ -72,8 +72,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
72
72
|
const histogramHost = ui.div([], {id: 'pep-hist-host'});
|
|
73
73
|
|
|
74
74
|
const activityScalingMethod = ui.choiceInput(
|
|
75
|
-
'Scaling',
|
|
76
|
-
async (currentMethod:
|
|
75
|
+
'Scaling', C.SCALING_METHODS.NONE, Object.values(C.SCALING_METHODS),
|
|
76
|
+
async (currentMethod: C.SCALING_METHODS): Promise<void> => {
|
|
77
77
|
scaledCol = scaleActivity(activityColumnChoice.value!, currentMethod);
|
|
78
78
|
|
|
79
79
|
const hist = DG.DataFrame.fromColumns([scaledCol]).plot.histogram({
|
|
@@ -87,7 +87,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
87
87
|
});
|
|
88
88
|
histogramHost.lastChild?.remove();
|
|
89
89
|
histogramHost.appendChild(hist.root);
|
|
90
|
-
})
|
|
90
|
+
}) as DG.InputBase<C.SCALING_METHODS | null>;
|
|
91
91
|
activityScalingMethod.setTooltip('Function to apply for each value in activity column');
|
|
92
92
|
|
|
93
93
|
const activityScalingMethodState = (): void => {
|
|
@@ -101,23 +101,27 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
101
101
|
activityColumnChoice.fireChanged();
|
|
102
102
|
activityScalingMethod.fireChanged();
|
|
103
103
|
|
|
104
|
-
const
|
|
104
|
+
const targetColumnChoice = ui.columnInput('Target', df, null, () => {
|
|
105
|
+
if (targetColumnChoice.value?.type !== DG.COLUMN_TYPE.STRING) {
|
|
106
|
+
grok.shell.warning('Target column should be of string type');
|
|
107
|
+
targetColumnChoice.value = null;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
targetColumnChoice.nullable = true;
|
|
111
|
+
|
|
112
|
+
const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, targetColumnChoice];
|
|
105
113
|
if (seqColInput !== null)
|
|
106
114
|
inputsList.splice(0, 0, seqColInput);
|
|
107
115
|
|
|
108
|
-
const bitsetChanged = df.filter.onChanged.subscribe(() =>
|
|
109
|
-
activityScalingMethodState();
|
|
110
|
-
});
|
|
116
|
+
const bitsetChanged = df.filter.onChanged.subscribe(() => activityScalingMethodState());
|
|
111
117
|
|
|
112
118
|
const startAnalysisCallback = async (): Promise<boolean> => {
|
|
113
119
|
const sequencesCol = col ?? seqColInput!.value;
|
|
120
|
+
bitsetChanged.unsubscribe();
|
|
114
121
|
if (sequencesCol) {
|
|
115
122
|
const model = await startAnalysis(activityColumnChoice.value!, sequencesCol, clustersColumnChoice.value, df,
|
|
116
|
-
scaledCol, activityScalingMethod.
|
|
117
|
-
|
|
118
|
-
bitsetChanged.unsubscribe();
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
123
|
+
scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, targetColumnChoice.value);
|
|
124
|
+
return model != null;
|
|
121
125
|
}
|
|
122
126
|
return false;
|
|
123
127
|
};
|
|
@@ -151,10 +155,11 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
151
155
|
|
|
152
156
|
export async function startAnalysis(activityColumn: DG.Column<number>, peptidesCol: DG.Column<string>,
|
|
153
157
|
clustersColumn: DG.Column | null, currentDf: DG.DataFrame, scaledCol: DG.Column<number>, scaling: C.SCALING_METHODS,
|
|
154
|
-
): Promise<PeptidesModel | null> {
|
|
158
|
+
targetColumn: DG.Column<string> | null = null): Promise<PeptidesModel | null> {
|
|
155
159
|
const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
|
|
156
160
|
let model = null;
|
|
157
|
-
if (activityColumn.type === DG.
|
|
161
|
+
if (activityColumn.type === DG.COLUMN_TYPE.FLOAT || activityColumn.type === DG.COLUMN_TYPE.INT ||
|
|
162
|
+
activityColumn.type === DG.COLUMN_TYPE.BIG_INT || activityColumn.type === DG.COLUMN_TYPE.QNUM) {
|
|
158
163
|
//prepare new DF
|
|
159
164
|
const newDf = DG.DataFrame.create(currentDf.rowCount);
|
|
160
165
|
const newDfCols = newDf.columns;
|
|
@@ -173,6 +178,9 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
173
178
|
minActivityDelta: 0,
|
|
174
179
|
showDendrogram: false,
|
|
175
180
|
};
|
|
181
|
+
if (targetColumn !== null)
|
|
182
|
+
settings.targetColumnName = targetColumn.name;
|
|
183
|
+
|
|
176
184
|
if (clustersColumn) {
|
|
177
185
|
const clusterCol = newDf.getCol(clustersColumn.name);
|
|
178
186
|
if (clusterCol.type != DG.COLUMN_TYPE.STRING)
|