@datagrok/peptides 1.3.9 → 1.5.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 +1929 -1130
- package/dist/package.js +1701 -1045
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +3 -3
- package/files/tests/aligned_5k.d42 +0 -0
- package/package.json +3 -3
- package/src/__jest__/remote.test.ts +12 -5
- package/src/__jest__/test-node.ts +1 -1
- package/src/model.ts +481 -592
- package/src/package-test.ts +1 -0
- package/src/package.ts +15 -14
- package/src/tests/algorithms.ts +29 -20
- package/src/tests/core.ts +3 -6
- package/src/tests/utils.ts +4 -4
- package/src/utils/algorithms.ts +25 -22
- package/src/utils/cell-renderer.ts +34 -15
- package/src/utils/constants.ts +3 -36
- package/src/utils/misc.ts +26 -35
- package/src/utils/peptide-similarity-space.ts +4 -7
- package/src/utils/statistics.ts +20 -12
- package/src/utils/types.ts +8 -3
- package/src/viewers/logo-summary.ts +188 -7
- package/src/viewers/peptide-space-viewer.ts +5 -5
- package/src/viewers/sar-viewer.ts +230 -57
- package/src/widgets/distribution.ts +35 -10
- package/src/widgets/manual-alignment.ts +1 -1
- package/src/widgets/mutation-cliffs.ts +2 -3
- package/src/widgets/peptides.ts +67 -36
- package/src/widgets/settings.ts +20 -16
- package/{test-Peptides-62cc009524f3-0949dc07.html → test-Peptides-4775b69ad08a-1bc1a2b4.html} +33 -32
- package/tsconfig.json +3 -3
package/src/model.ts
CHANGED
|
@@ -9,8 +9,8 @@ import * as rxjs from 'rxjs';
|
|
|
9
9
|
|
|
10
10
|
import * as C from './utils/constants';
|
|
11
11
|
import * as type from './utils/types';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import {calculateSelected, extractMonomerInfo, scaleActivity} from './utils/misc';
|
|
13
|
+
import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
14
14
|
import * as CR from './utils/cell-renderer';
|
|
15
15
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
16
16
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
@@ -24,29 +24,24 @@ import {findMutations} from './utils/algorithms';
|
|
|
24
24
|
export class PeptidesModel {
|
|
25
25
|
static modelName = 'peptidesModel';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
|
|
27
|
+
settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
|
|
28
|
+
_mutatinCliffsSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
30
29
|
|
|
31
30
|
_isUpdating: boolean = false;
|
|
32
31
|
isBitsetChangedInitialized = false;
|
|
33
32
|
isCellChanging = false;
|
|
34
33
|
|
|
35
|
-
mutationCliffsGrid!: DG.Grid;
|
|
36
|
-
mostPotentResiduesGrid!: DG.Grid;
|
|
37
|
-
logoSummaryGrid!: DG.Grid;
|
|
38
|
-
sourceGrid!: DG.Grid;
|
|
39
34
|
df: DG.DataFrame;
|
|
40
35
|
splitCol!: DG.Column<boolean>;
|
|
41
36
|
edf: DG.DataFrame | null = null;
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
_monomerPositionStatsDf?: DG.DataFrame;
|
|
38
|
+
_clusterStatsDf?: DG.DataFrame;
|
|
44
39
|
_mutationCliffsSelection!: type.PositionToAARList;
|
|
45
40
|
_invariantMapSelection!: type.PositionToAARList;
|
|
46
41
|
_logoSummarySelection!: number[];
|
|
47
|
-
|
|
42
|
+
_substitutionsInfo?: type.SubstitutionsInfo;
|
|
48
43
|
isInitialized = false;
|
|
49
|
-
|
|
44
|
+
_analysisView?: DG.TableView;
|
|
50
45
|
|
|
51
46
|
isPeptideSpaceChangingBitset = false;
|
|
52
47
|
isChangingEdfBitset = false;
|
|
@@ -58,33 +53,123 @@ export class PeptidesModel {
|
|
|
58
53
|
_settings!: type.PeptidesSettings;
|
|
59
54
|
isRibbonSet = false;
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
xorBitset?: DG.BitSet;
|
|
56
|
+
_cp?: bio.SeqPalette;
|
|
63
57
|
initBitset: DG.BitSet;
|
|
64
|
-
isInvariantMapTrigger: boolean = false
|
|
58
|
+
isInvariantMapTrigger: boolean = false;
|
|
59
|
+
headerSelectedMonomers: type.MonomerSelectionStats = {};
|
|
60
|
+
webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
|
|
61
|
+
cachedWebLogoTooltip: {bar: string; tooltip: HTMLDivElement | null;} = {bar: '', tooltip: null};
|
|
62
|
+
_monomerPositionDf?: DG.DataFrame;
|
|
63
|
+
_alphabet?: string;
|
|
64
|
+
_mostPotentResiduesDf?: DG.DataFrame;
|
|
65
|
+
_matrixDf?: DG.DataFrame;
|
|
66
|
+
_splitSeqDf?: DG.DataFrame;
|
|
65
67
|
|
|
66
68
|
private constructor(dataFrame: DG.DataFrame) {
|
|
67
69
|
this.df = dataFrame;
|
|
68
70
|
this.initBitset = this.df.filter.clone();
|
|
69
|
-
this.cp = bio.pickUpPalette(this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE));
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
static
|
|
73
|
+
static getInstance(dataFrame: DG.DataFrame): PeptidesModel {
|
|
73
74
|
dataFrame.temp[PeptidesModel.modelName] ??= new PeptidesModel(dataFrame);
|
|
74
|
-
|
|
75
|
+
(dataFrame.temp[PeptidesModel.modelName] as PeptidesModel).init();
|
|
75
76
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
get
|
|
79
|
-
|
|
79
|
+
get monomerPositionDf(): DG.DataFrame {
|
|
80
|
+
this._monomerPositionDf ??= this.createMonomerPositionDf();
|
|
81
|
+
return this._monomerPositionDf;
|
|
82
|
+
}
|
|
83
|
+
set monomerPositionDf(df: DG.DataFrame) {
|
|
84
|
+
this._monomerPositionDf = df;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get monomerPositionStatsDf(): DG.DataFrame {
|
|
88
|
+
this._monomerPositionStatsDf ??= this.calculateMonomerPositionStatistics();
|
|
89
|
+
return this._monomerPositionStatsDf;
|
|
90
|
+
}
|
|
91
|
+
set monomerPositionStatsDf(df: DG.DataFrame) {
|
|
92
|
+
this._monomerPositionStatsDf = df;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get matrixDf(): DG.DataFrame {
|
|
96
|
+
this._matrixDf ??= this.buildMatrixDf();
|
|
97
|
+
return this._matrixDf;
|
|
98
|
+
}
|
|
99
|
+
set matrixDf(df: DG.DataFrame) {
|
|
100
|
+
this._matrixDf = df;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get splitSeqDf(): DG.DataFrame {
|
|
104
|
+
this._splitSeqDf ??= this.buildSplitSeqDf();
|
|
105
|
+
return this._splitSeqDf;
|
|
106
|
+
}
|
|
107
|
+
set splitSeqDf(df: DG.DataFrame) {
|
|
108
|
+
this._splitSeqDf = df;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get mostPotentResiduesDf(): DG.DataFrame {
|
|
112
|
+
this._mostPotentResiduesDf ??= this.createMostPotentResiduesDf();
|
|
113
|
+
return this._mostPotentResiduesDf;
|
|
114
|
+
}
|
|
115
|
+
set mostPotentResiduesDf(df: DG.DataFrame) {
|
|
116
|
+
this._mostPotentResiduesDf = df;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get alphabet(): string {
|
|
120
|
+
const col = this.settings.sequenceColumnName ? this.df.getCol(this.settings.sequenceColumnName) :
|
|
121
|
+
this.df.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!;
|
|
122
|
+
return col.getTag(bio.TAGS.alphabet);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get substitutionsInfo(): type.SubstitutionsInfo {
|
|
126
|
+
if (this._substitutionsInfo)
|
|
127
|
+
return this._substitutionsInfo;
|
|
128
|
+
|
|
129
|
+
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
130
|
+
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
131
|
+
const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
132
|
+
this._substitutionsInfo = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
|
|
133
|
+
return this._substitutionsInfo;
|
|
134
|
+
}
|
|
135
|
+
set substitutionsInfo(si: type.SubstitutionsInfo) {
|
|
136
|
+
this._substitutionsInfo = si;
|
|
80
137
|
}
|
|
81
138
|
|
|
82
|
-
get
|
|
83
|
-
|
|
139
|
+
get clusterStatsDf(): DG.DataFrame {
|
|
140
|
+
this._clusterStatsDf ??= this.calculateClusterStatistics();
|
|
141
|
+
return this._clusterStatsDf;
|
|
142
|
+
}
|
|
143
|
+
set clusterStatsDf(df: DG.DataFrame) {
|
|
144
|
+
this._clusterStatsDf = df;
|
|
84
145
|
}
|
|
85
146
|
|
|
86
|
-
get
|
|
87
|
-
|
|
147
|
+
get cp(): bio.SeqPalette {
|
|
148
|
+
this._cp ??= bio.pickUpPalette(this.df.getCol(this.settings.sequenceColumnName!));
|
|
149
|
+
return this._cp;
|
|
150
|
+
}
|
|
151
|
+
set cp(_cp: bio.SeqPalette) {
|
|
152
|
+
this._cp = _cp;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get analysisView(): DG.TableView {
|
|
156
|
+
const shell = grok.shell;
|
|
157
|
+
if (this.df.getTag('newAnalysis') !== '1') {
|
|
158
|
+
this._analysisView = wu(shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === '1')!;
|
|
159
|
+
grok.shell.v = this._analysisView;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this._analysisView ??= shell.addTableView(this.df);
|
|
163
|
+
this.df.setTag('newAnalysis', '');
|
|
164
|
+
return this._analysisView;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get onMutationCliffsSelectionChanged(): rxjs.Observable<undefined> {
|
|
168
|
+
return this._mutatinCliffsSelectionSubject.asObservable();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get onSettingsChanged(): rxjs.Observable<type.PeptidesSettings> {
|
|
172
|
+
return this.settingsSubject.asObservable();
|
|
88
173
|
}
|
|
89
174
|
|
|
90
175
|
get mutationCliffsSelection(): type.PositionToAARList {
|
|
@@ -96,7 +181,7 @@ export class PeptidesModel {
|
|
|
96
181
|
this._mutationCliffsSelection = selection;
|
|
97
182
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
98
183
|
this.fireBitsetChanged();
|
|
99
|
-
this.
|
|
184
|
+
this._mutatinCliffsSelectionSubject.next();
|
|
100
185
|
}
|
|
101
186
|
|
|
102
187
|
get invariantMapSelection(): type.PositionToAARList {
|
|
@@ -110,7 +195,7 @@ export class PeptidesModel {
|
|
|
110
195
|
this.isInvariantMapTrigger = true;
|
|
111
196
|
this.df.filter.fireChanged();
|
|
112
197
|
this.isInvariantMapTrigger = false;
|
|
113
|
-
this.
|
|
198
|
+
this.analysisView.grid.invalidate();
|
|
114
199
|
}
|
|
115
200
|
|
|
116
201
|
get logoSummarySelection(): number[] {
|
|
@@ -122,7 +207,7 @@ export class PeptidesModel {
|
|
|
122
207
|
this._logoSummarySelection = selection;
|
|
123
208
|
this.df.tags[C.TAGS.CLUSTER_SELECTION] = JSON.stringify(selection);
|
|
124
209
|
this.fireBitsetChanged();
|
|
125
|
-
this.
|
|
210
|
+
this.analysisView.grid.invalidate();
|
|
126
211
|
}
|
|
127
212
|
|
|
128
213
|
get splitByPos(): boolean {
|
|
@@ -170,150 +255,165 @@ export class PeptidesModel {
|
|
|
170
255
|
return this._settings;
|
|
171
256
|
}
|
|
172
257
|
set settings(s: type.PeptidesSettings) {
|
|
173
|
-
|
|
258
|
+
const newSettingsEntries = Object.entries(s);
|
|
259
|
+
const updateVars: Set<string> = new Set();
|
|
260
|
+
for (const [key, value] of newSettingsEntries) {
|
|
174
261
|
this._settings[key as keyof type.PeptidesSettings] = value as any;
|
|
262
|
+
switch (key) {
|
|
263
|
+
case 'scaling':
|
|
264
|
+
updateVars.add('activity');
|
|
265
|
+
updateVars.add('mutationCliffs');
|
|
266
|
+
updateVars.add('stats');
|
|
267
|
+
break;
|
|
268
|
+
// case 'columns':
|
|
269
|
+
// updateVars.add('grid');
|
|
270
|
+
// break;
|
|
271
|
+
case 'maxMutations':
|
|
272
|
+
case 'minActivityDelta':
|
|
273
|
+
updateVars.add('mutationCliffs');
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
175
277
|
this.df.setTag('settings', JSON.stringify(this._settings));
|
|
176
|
-
//
|
|
177
|
-
|
|
278
|
+
// this.updateDefault();
|
|
279
|
+
for (const variable of updateVars) {
|
|
280
|
+
switch (variable) {
|
|
281
|
+
case 'activity':
|
|
282
|
+
this.createScaledCol();
|
|
283
|
+
break;
|
|
284
|
+
case 'mutationCliffs':
|
|
285
|
+
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
286
|
+
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
287
|
+
const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
288
|
+
this.substitutionsInfo = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
|
|
289
|
+
break;
|
|
290
|
+
case 'stats':
|
|
291
|
+
this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics();
|
|
292
|
+
this.monomerPositionDf = this.createMonomerPositionDf();
|
|
293
|
+
this.mostPotentResiduesDf = this.createMostPotentResiduesDf();
|
|
294
|
+
this.clusterStatsDf = this.calculateClusterStatistics();
|
|
295
|
+
break;
|
|
296
|
+
case 'grid':
|
|
297
|
+
this.updateGrid();
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
//TODO: handle settings change
|
|
303
|
+
this.settingsSubject.next(this.settings);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
createMonomerPositionDf(): DG.DataFrame {
|
|
307
|
+
const matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
308
|
+
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
309
|
+
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
310
|
+
.aggregate();
|
|
311
|
+
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
312
|
+
for (let i = 0; i < monomerCol.length; ++i) {
|
|
313
|
+
if (monomerCol.get(i) == '') {
|
|
314
|
+
matrixDf.rows.removeAt(i);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
matrixDf.name = 'SAR';
|
|
319
|
+
|
|
320
|
+
return matrixDf;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
buildMatrixDf(): DG.DataFrame {
|
|
324
|
+
const splitSeqDfColumns = this.splitSeqDf.columns;
|
|
325
|
+
const positionColumns = splitSeqDfColumns.names();
|
|
326
|
+
return this.splitSeqDf
|
|
327
|
+
.groupBy(positionColumns)
|
|
328
|
+
.aggregate()
|
|
329
|
+
.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
buildSplitSeqDf(): DG.DataFrame {
|
|
333
|
+
const sequenceCol = this.df.getCol(this.settings.sequenceColumnName!);
|
|
334
|
+
const splitSeqDf = splitAlignedSequences(sequenceCol);
|
|
335
|
+
|
|
336
|
+
return splitSeqDf;
|
|
178
337
|
}
|
|
179
338
|
|
|
180
339
|
createAccordion(): DG.Accordion {
|
|
181
340
|
const acc = ui.accordion();
|
|
182
341
|
acc.root.style.width = '100%';
|
|
183
342
|
acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
|
|
184
|
-
acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root
|
|
185
|
-
acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root
|
|
343
|
+
acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root);
|
|
344
|
+
acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root);
|
|
186
345
|
|
|
187
346
|
return acc;
|
|
188
347
|
}
|
|
189
348
|
|
|
190
349
|
updateDefault(): void {
|
|
191
|
-
if (
|
|
192
|
-
// this.isInitialized = true;
|
|
350
|
+
if (!this._isUpdating || !this.isInitialized) {
|
|
193
351
|
this._isUpdating = true;
|
|
194
|
-
this.
|
|
195
|
-
|
|
196
|
-
this.
|
|
197
|
-
this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
|
|
198
|
-
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
199
|
-
this.logoSummaryGridSubject.next(this.logoSummaryGrid);
|
|
200
|
-
|
|
201
|
-
this.fireBitsetChanged();
|
|
202
|
-
this.invalidateGrids();
|
|
352
|
+
this.updateGrid();
|
|
353
|
+
|
|
354
|
+
this.analysisView.grid.invalidate();
|
|
203
355
|
this._isUpdating = false;
|
|
204
356
|
}
|
|
205
357
|
}
|
|
206
358
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
throw new Error(`Source grid is not initialized`);
|
|
210
|
-
|
|
211
|
-
//Split the aligned sequence into separate AARs
|
|
212
|
-
const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
213
|
-
const alphabet = col.tags['alphabet'];
|
|
214
|
-
const splitSeqDf = splitAlignedSequences(col);
|
|
215
|
-
|
|
216
|
-
// this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
|
|
217
|
-
|
|
218
|
-
const positionColumns = splitSeqDf.columns.names();
|
|
219
|
-
|
|
220
|
-
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
221
|
-
splitSeqDf.columns.add(activityCol);
|
|
222
|
-
|
|
223
|
-
this.joinDataFrames(positionColumns, splitSeqDf, alphabet);
|
|
359
|
+
updateGrid(): void {
|
|
360
|
+
this.joinDataFrames();
|
|
224
361
|
|
|
225
362
|
this.sortSourceGrid();
|
|
226
363
|
|
|
227
|
-
this.createScaledCol(
|
|
228
|
-
|
|
229
|
-
//unpivot a table and handle duplicates
|
|
230
|
-
let matrixDf = splitSeqDf.groupBy(positionColumns).aggregate();
|
|
231
|
-
|
|
232
|
-
matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
|
|
233
|
-
|
|
234
|
-
//statistics for specific AAR at a specific position
|
|
235
|
-
this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics(matrixDf);
|
|
236
|
-
|
|
237
|
-
// SAR matrix table
|
|
238
|
-
//pivot a table to make it matrix-like
|
|
239
|
-
matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
240
|
-
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
241
|
-
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
242
|
-
.aggregate();
|
|
243
|
-
matrixDf.name = 'SAR';
|
|
244
|
-
|
|
245
|
-
// Setting category order
|
|
246
|
-
this.setCategoryOrder(matrixDf);
|
|
247
|
-
|
|
248
|
-
// SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
|
|
249
|
-
const sequenceDf = this.createVerticalTable();
|
|
250
|
-
|
|
251
|
-
const scaledActivityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
252
|
-
const monomerColumns = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
|
|
253
|
-
this.substitutionsInfo = findMutations(scaledActivityCol, monomerColumns, this.settings);
|
|
254
|
-
|
|
255
|
-
[this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
|
|
256
|
-
this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
|
|
364
|
+
this.createScaledCol();
|
|
257
365
|
|
|
258
|
-
|
|
259
|
-
this.clusterStatsDf = this.calculateClusterStatistics();
|
|
260
|
-
this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// init invariant map & mutation cliffs selections
|
|
264
|
-
this.initSelections(positionColumns);
|
|
265
|
-
|
|
266
|
-
positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
366
|
+
this.initSelections();
|
|
267
367
|
|
|
268
|
-
|
|
368
|
+
this.setWebLogoInteraction();
|
|
369
|
+
this.webLogoBounds = {};
|
|
269
370
|
|
|
270
|
-
this.setCellRenderers(
|
|
371
|
+
this.setCellRenderers();
|
|
271
372
|
|
|
272
|
-
|
|
273
|
-
this.setTooltips(positionColumns);
|
|
274
|
-
|
|
275
|
-
this.setInteractionCallback();
|
|
373
|
+
this.setTooltips();
|
|
276
374
|
|
|
277
375
|
this.setBitsetCallback();
|
|
278
376
|
|
|
279
377
|
this.postProcessGrids();
|
|
280
378
|
}
|
|
281
379
|
|
|
282
|
-
initSelections(
|
|
380
|
+
initSelections(): void {
|
|
283
381
|
const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
|
|
284
382
|
const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
383
|
+
const positionColumns = this.splitSeqDf.columns.names();
|
|
285
384
|
for (const pos of positionColumns) {
|
|
286
385
|
tempInvariantMapSelection[pos] ??= [];
|
|
287
386
|
mutationCliffsSelection[pos] ??= [];
|
|
288
387
|
}
|
|
289
388
|
this.invariantMapSelection = tempInvariantMapSelection;
|
|
290
389
|
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
291
|
-
// this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
292
390
|
}
|
|
293
391
|
|
|
294
|
-
joinDataFrames(
|
|
392
|
+
joinDataFrames(): void {
|
|
295
393
|
// append splitSeqDf columns to source table and make sure columns are not added more than once
|
|
296
394
|
const name = this.df.name;
|
|
297
395
|
const cols = this.df.columns;
|
|
396
|
+
const positionColumns = this.splitSeqDf.columns.names();
|
|
298
397
|
for (const colName of positionColumns) {
|
|
299
398
|
const col = this.df.col(colName);
|
|
300
|
-
const newCol = splitSeqDf.getCol(colName);
|
|
399
|
+
const newCol = this.splitSeqDf.getCol(colName);
|
|
301
400
|
if (col === null)
|
|
302
401
|
cols.add(newCol);
|
|
303
402
|
else {
|
|
304
403
|
cols.remove(colName);
|
|
305
404
|
cols.add(newCol);
|
|
306
405
|
}
|
|
307
|
-
CR.setAARRenderer(newCol,
|
|
406
|
+
CR.setAARRenderer(newCol, this.alphabet);
|
|
308
407
|
}
|
|
309
408
|
this.df.name = name;
|
|
310
|
-
this.currentView.name = name;
|
|
311
409
|
}
|
|
312
410
|
|
|
313
411
|
sortSourceGrid(): void {
|
|
314
412
|
const colNames: DG.GridColumn[] = [];
|
|
315
|
-
|
|
316
|
-
|
|
413
|
+
const sourceGridCols = this.analysisView.grid.columns;
|
|
414
|
+
const sourceGridColsCount = sourceGridCols.length;
|
|
415
|
+
for (let i = 1; i < sourceGridColsCount; i++)
|
|
416
|
+
colNames.push(sourceGridCols.byIndex(i)!);
|
|
317
417
|
|
|
318
418
|
colNames.sort((a, b) => {
|
|
319
419
|
if (a.column!.semType == C.SEM_TYPES.MONOMER) {
|
|
@@ -325,99 +425,133 @@ export class PeptidesModel {
|
|
|
325
425
|
return 1;
|
|
326
426
|
return 0;
|
|
327
427
|
});
|
|
328
|
-
|
|
428
|
+
sourceGridCols.setOrder(colNames.map((v) => v.name));
|
|
329
429
|
}
|
|
330
430
|
|
|
331
|
-
createScaledCol(
|
|
332
|
-
const
|
|
431
|
+
createScaledCol(): void {
|
|
432
|
+
const sourceGrid = this.analysisView.grid;
|
|
433
|
+
const scaledCol = scaleActivity(this.df.getCol(this.settings.activityColumnName!), this.settings.scaling);
|
|
333
434
|
//TODO: make another func
|
|
334
|
-
splitSeqDf.columns.add(scaledCol);
|
|
335
435
|
this.df.columns.replace(C.COLUMNS_NAMES.ACTIVITY_SCALED, scaledCol);
|
|
336
|
-
const gridCol =
|
|
337
|
-
if (gridCol)
|
|
338
|
-
|
|
436
|
+
// const gridCol = sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
437
|
+
// if (gridCol)
|
|
438
|
+
// gridCol.name = scaledCol.getTag('gridName');
|
|
339
439
|
|
|
340
|
-
|
|
440
|
+
sourceGrid.columns.setOrder([scaledCol.name]);
|
|
341
441
|
}
|
|
342
442
|
|
|
343
|
-
calculateMonomerPositionStatistics(
|
|
344
|
-
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
443
|
+
calculateMonomerPositionStatistics(): DG.DataFrame {
|
|
444
|
+
const matrixDf = this.matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
445
|
+
const matrixLen = matrixDf.rowCount;
|
|
446
|
+
|
|
447
|
+
const posRawColumns: type.RawColumn[] = [];
|
|
448
|
+
|
|
449
|
+
const posCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
450
|
+
const posColData = posCol.getRawData();
|
|
451
|
+
const posColCategories = posCol.categories;
|
|
452
|
+
for (const position of posColCategories) {
|
|
453
|
+
const currentCol = this.df.getCol(position);
|
|
454
|
+
posRawColumns.push({
|
|
455
|
+
name: position,
|
|
456
|
+
rawData: currentCol.getRawData(),
|
|
457
|
+
cat: currentCol.categories,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const monomerCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
462
|
+
const monomerColData = monomerCol.getRawData();
|
|
463
|
+
const monomerColCategories = monomerCol.categories;
|
|
345
464
|
|
|
346
465
|
//calculate p-values based on t-test
|
|
347
466
|
const matrixCols = matrixDf.columns;
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
467
|
+
const mdColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE).init(0).getRawData();
|
|
468
|
+
const pValColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE).init(0).getRawData();
|
|
469
|
+
const countColData = matrixCols.addNewInt(C.COLUMNS_NAMES.COUNT).init(0).getRawData();
|
|
470
|
+
const ratioColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO).init(0).getRawData();
|
|
471
|
+
const activityColData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
472
|
+
const sourceDfLen = activityColData.length;
|
|
473
|
+
|
|
474
|
+
for (let i = 0; i < matrixLen; i++) {
|
|
475
|
+
const positionRawIdx = posColData[i];
|
|
476
|
+
const currentPosRawCol = posRawColumns[positionRawIdx];
|
|
477
|
+
const monomerRawIdx = monomerColData[i];
|
|
478
|
+
const mask: boolean[] = new Array(sourceDfLen);
|
|
479
|
+
const monomer = monomerColCategories[monomerRawIdx];
|
|
480
|
+
if (monomer == '')
|
|
481
|
+
continue;
|
|
482
|
+
|
|
483
|
+
let trueCount = 0;
|
|
484
|
+
for (let j = 0; j < sourceDfLen; ++j) {
|
|
485
|
+
mask[j] = currentPosRawCol.cat![currentPosRawCol.rawData[j]] == monomer;
|
|
486
|
+
|
|
487
|
+
if (mask[j])
|
|
488
|
+
++trueCount;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const maskInfo = {
|
|
492
|
+
trueCount: trueCount,
|
|
493
|
+
falseCount: sourceDfLen - trueCount,
|
|
494
|
+
mask: mask,
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const stats = getStats(activityColData, maskInfo);
|
|
498
|
+
|
|
499
|
+
mdColData[i] = stats.meanDifference;
|
|
500
|
+
pValColData[i] = stats.pValue;
|
|
501
|
+
countColData[i] = stats.count;
|
|
502
|
+
ratioColData[i] = stats.ratio;
|
|
367
503
|
}
|
|
504
|
+
matrixDf.fireValuesChanged();
|
|
368
505
|
|
|
369
|
-
const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
370
|
-
matrixDf = matrixDf.clone(DG.BitSet.create(matrixDf.rowCount, (i) => monomerCol.get(i) ? true : false));
|
|
371
506
|
return matrixDf as DG.DataFrame;
|
|
372
507
|
}
|
|
373
508
|
|
|
374
509
|
calculateClusterStatistics(): DG.DataFrame {
|
|
375
|
-
const originalClustersCol = this.df.getCol(
|
|
376
|
-
const
|
|
377
|
-
const
|
|
510
|
+
const originalClustersCol = this.df.getCol(this.settings.clustersColumnName!);
|
|
511
|
+
const originalClustersColData = originalClustersCol.getRawData();
|
|
512
|
+
const originalClustersColCategories = originalClustersCol.categories;
|
|
513
|
+
|
|
514
|
+
const statsDf = this.df.groupBy([this.settings.clustersColumnName!]).aggregate();
|
|
515
|
+
const clustersCol = statsDf.getCol(this.settings.clustersColumnName!);
|
|
516
|
+
clustersCol.setCategoryOrder(originalClustersColCategories);
|
|
517
|
+
const clustersColData = clustersCol.getRawData();
|
|
518
|
+
|
|
378
519
|
const statsDfCols = statsDf.columns;
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
520
|
+
const mdColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE).getRawData();
|
|
521
|
+
const pValColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE).getRawData();
|
|
522
|
+
const countColData = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT).getRawData();
|
|
523
|
+
const ratioColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO).getRawData();
|
|
524
|
+
const activityColData: type.RawData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
525
|
+
const activityColLen = activityColData.length;
|
|
526
|
+
|
|
527
|
+
for (let rowIdx = 0; rowIdx < clustersColData.length; ++rowIdx) {
|
|
528
|
+
const clusterIdx = clustersColData[rowIdx];
|
|
529
|
+
const mask = new Array(activityColLen);
|
|
530
|
+
let trueCount = 0;
|
|
531
|
+
for (let maskIdx = 0; maskIdx < activityColLen; ++maskIdx) {
|
|
532
|
+
mask[maskIdx] = clusterIdx == originalClustersColData[maskIdx];
|
|
533
|
+
|
|
534
|
+
if (mask[maskIdx])
|
|
535
|
+
++trueCount;
|
|
536
|
+
}
|
|
397
537
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
|
|
404
|
-
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
405
|
-
}
|
|
538
|
+
const maskInfo = {
|
|
539
|
+
trueCount: trueCount,
|
|
540
|
+
falseCount: activityColLen - trueCount,
|
|
541
|
+
mask: mask,
|
|
542
|
+
};
|
|
406
543
|
|
|
407
|
-
|
|
408
|
-
.aggregate();
|
|
409
|
-
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
410
|
-
const getWeight = (aar: string): number => aarWeightsDf
|
|
411
|
-
.groupBy(['weight'])
|
|
412
|
-
.where(`${C.COLUMNS_NAMES.MONOMER} = ${aar}`)
|
|
413
|
-
.aggregate()
|
|
414
|
-
.get('weight', 0) as number;
|
|
415
|
-
aarList.sort((first, second) => getWeight(second) - getWeight(first));
|
|
544
|
+
const stats = getStats(activityColData, maskInfo);
|
|
416
545
|
|
|
417
|
-
|
|
546
|
+
mdColData[rowIdx] = stats.meanDifference;
|
|
547
|
+
pValColData[rowIdx] = stats.pValue;
|
|
548
|
+
countColData[rowIdx] = stats.count;
|
|
549
|
+
ratioColData[rowIdx] = stats.ratio;
|
|
550
|
+
}
|
|
551
|
+
return statsDf;
|
|
418
552
|
}
|
|
419
553
|
|
|
420
|
-
|
|
554
|
+
createMostPotentResiduesDf(): DG.DataFrame {
|
|
421
555
|
// TODO: aquire ALL of the positions
|
|
422
556
|
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
|
|
423
557
|
'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
|
|
@@ -430,6 +564,7 @@ export class PeptidesModel {
|
|
|
430
564
|
const posColCategories = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION).categories;
|
|
431
565
|
const mdCol = sequenceDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
432
566
|
const posCol = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
567
|
+
const monomerCol = sequenceDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
433
568
|
const rowCount = sequenceDf.rowCount;
|
|
434
569
|
for (const pos of posColCategories) {
|
|
435
570
|
tempStats = DG.Stats.fromColumn(mdCol, DG.BitSet.create(rowCount, (i) => posCol.get(i) === pos));
|
|
@@ -437,115 +572,12 @@ export class PeptidesModel {
|
|
|
437
572
|
(tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) :
|
|
438
573
|
tempStats.max;
|
|
439
574
|
}
|
|
440
|
-
sequenceDf = sequenceDf.clone(DG.BitSet.create(rowCount,
|
|
575
|
+
sequenceDf = sequenceDf.clone(DG.BitSet.create(rowCount,
|
|
576
|
+
(i) => monomerCol.get(i) !== '' && maxAtPos[posCol.get(i)] != 0 && mdCol.get(i) === maxAtPos[posCol.get(i)]));
|
|
441
577
|
|
|
442
578
|
return sequenceDf;
|
|
443
579
|
}
|
|
444
580
|
|
|
445
|
-
createGrids(mutationCliffsDf: DG.DataFrame, mostPotentResiduesDf: DG.DataFrame, positionColumns: string[],
|
|
446
|
-
alphabet: string): [DG.Grid, DG.Grid] {
|
|
447
|
-
// Creating Mutation Cliffs grid and sorting columns
|
|
448
|
-
const mutationCliffsGrid = mutationCliffsDf.plot.grid();
|
|
449
|
-
mutationCliffsGrid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
450
|
-
mutationCliffsGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
|
|
451
|
-
|
|
452
|
-
// Creating Monomer-Position grid, sorting and setting column format
|
|
453
|
-
const mostPotentResiduesGrid = mostPotentResiduesDf.plot.grid();
|
|
454
|
-
mostPotentResiduesGrid.sort([C.COLUMNS_NAMES.POSITION]);
|
|
455
|
-
const pValGridCol = mostPotentResiduesGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
|
|
456
|
-
pValGridCol.format = '#.000';
|
|
457
|
-
pValGridCol.name = 'P-value';
|
|
458
|
-
|
|
459
|
-
// Setting Monomer column renderer
|
|
460
|
-
CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
461
|
-
CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
462
|
-
|
|
463
|
-
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
createLogoSummaryGrid(): DG.Grid {
|
|
467
|
-
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
468
|
-
const summaryTableLength = summaryTable.rowCount;
|
|
469
|
-
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
470
|
-
const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
|
|
471
|
-
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
472
|
-
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
473
|
-
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
474
|
-
const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
475
|
-
|
|
476
|
-
for (let index = 0; index < summaryTableLength; ++index) {
|
|
477
|
-
const indexes: number[] = [];
|
|
478
|
-
for (let j = 0; j < originalClustersCol.length; ++j) {
|
|
479
|
-
if (originalClustersCol.get(j) === clustersCol.get(index))
|
|
480
|
-
indexes.push(j);
|
|
481
|
-
}
|
|
482
|
-
const tCol = DG.Column.string('peptides', indexes.length);
|
|
483
|
-
tCol.init((i) => peptideCol.get(indexes[i]));
|
|
484
|
-
|
|
485
|
-
for (const tag of peptideCol.tags)
|
|
486
|
-
tCol.setTag(tag[0], tag[1]);
|
|
487
|
-
|
|
488
|
-
const dfSlice = DG.DataFrame.fromColumns([tCol]);
|
|
489
|
-
tempDfList[index] = dfSlice;
|
|
490
|
-
webLogoCol.set(index, index.toString());
|
|
491
|
-
membersCol.set(index, dfSlice.rowCount);
|
|
492
|
-
//TODO: user should be able to choose threshold
|
|
493
|
-
if (dfSlice.rowCount <= Math.ceil(this.clusterStatsDf.getCol(C.COLUMNS_NAMES.COUNT).stats.max * 0.70))
|
|
494
|
-
summaryTable.filter.set(index, false, false);
|
|
495
|
-
}
|
|
496
|
-
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
497
|
-
|
|
498
|
-
const grid = summaryTable.plot.grid();
|
|
499
|
-
const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
|
|
500
|
-
gridClustersCol.name = 'Clusters';
|
|
501
|
-
gridClustersCol.visible = true;
|
|
502
|
-
grid.columns.rowHeader!.visible = false;
|
|
503
|
-
grid.props.rowHeight = 55;
|
|
504
|
-
grid.onCellPrepare((cell) => {
|
|
505
|
-
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
|
|
506
|
-
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
|
|
507
|
-
.then((viewer) => cell.element = viewer.root);
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
grid.root.addEventListener('click', (ev) => {
|
|
511
|
-
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
512
|
-
if (!cell || !cell.isTableCell)
|
|
513
|
-
return;
|
|
514
|
-
|
|
515
|
-
const cluster = clustersCol.get(cell.tableRowIndex!)!;
|
|
516
|
-
summaryTable.currentRowIdx = -1;
|
|
517
|
-
if (ev.shiftKey)
|
|
518
|
-
this.modifyClusterSelection(cluster);
|
|
519
|
-
else
|
|
520
|
-
this.initClusterSelection(cluster);
|
|
521
|
-
// this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
522
|
-
});
|
|
523
|
-
grid.onCellRender.subscribe((gridCellArgs) => {
|
|
524
|
-
const gc = gridCellArgs.cell;
|
|
525
|
-
if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
|
|
526
|
-
return;
|
|
527
|
-
const canvasContext = gridCellArgs.g;
|
|
528
|
-
const bound = gridCellArgs.bounds;
|
|
529
|
-
canvasContext.save();
|
|
530
|
-
canvasContext.beginPath();
|
|
531
|
-
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
532
|
-
canvasContext.clip();
|
|
533
|
-
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
|
|
534
|
-
gridCellArgs.preventDefault();
|
|
535
|
-
canvasContext.restore();
|
|
536
|
-
});
|
|
537
|
-
grid.onCellTooltip((cell, x, y) => {
|
|
538
|
-
if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
|
|
539
|
-
this.showTooltipCluster(cell.cell.value, x, y);
|
|
540
|
-
return true;
|
|
541
|
-
});
|
|
542
|
-
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
543
|
-
webLogoGridCol.cellType = 'html';
|
|
544
|
-
webLogoGridCol.width = 350;
|
|
545
|
-
|
|
546
|
-
return grid;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
581
|
modifyClusterSelection(cluster: number): void {
|
|
550
582
|
const tempSelection = this.logoSummarySelection;
|
|
551
583
|
const idx = tempSelection.indexOf(cluster);
|
|
@@ -561,167 +593,102 @@ export class PeptidesModel {
|
|
|
561
593
|
this.logoSummarySelection = [cluster];
|
|
562
594
|
}
|
|
563
595
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
// };
|
|
572
|
-
|
|
573
|
-
// // The following events makes the barchart interactive
|
|
574
|
-
// rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
|
|
575
|
-
// .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
576
|
-
// rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
|
|
577
|
-
// .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
578
|
-
// }
|
|
579
|
-
|
|
580
|
-
// findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
|
|
581
|
-
// const barCoords = this.barsBounds[cell.tableColumn!.name];
|
|
582
|
-
// for (const [monomer, coords] of Object.entries(barCoords)) {
|
|
583
|
-
// const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
|
|
584
|
-
// const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
|
|
585
|
-
// if (isIntersectingX && isIntersectingY)
|
|
586
|
-
// return {monomer: monomer, position: cell.tableColumn!.name};
|
|
587
|
-
// }
|
|
588
|
-
|
|
589
|
-
// return null;
|
|
590
|
-
// }
|
|
591
|
-
|
|
592
|
-
// requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
|
|
593
|
-
// if (!barPart)
|
|
594
|
-
// return;
|
|
595
|
-
// const monomer = barPart.monomer;
|
|
596
|
-
// const position = barPart.position;
|
|
597
|
-
// if (ev.type === 'click') {
|
|
598
|
-
// ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
|
|
599
|
-
// this.initMonomerPositionSelection(monomer, position, true);
|
|
600
|
-
// this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
601
|
-
// } else {
|
|
602
|
-
// const bar = `${monomer}:${position}`;
|
|
603
|
-
// if (this.cachedBarchartTooltip.bar == bar)
|
|
604
|
-
// ui.tooltip.show(this.cachedBarchartTooltip.tooltip!, ev.clientX, ev.clientY);
|
|
605
|
-
// else
|
|
606
|
-
// this.cachedBarchartTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
|
|
607
|
-
// }
|
|
608
|
-
// }
|
|
609
|
-
|
|
610
|
-
setCellRenderers(renderColNames: string[]): void {
|
|
611
|
-
const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
612
|
-
//decompose into two different renering funcs
|
|
613
|
-
const renderCell = (args: DG.GridCellRenderArgs): void => {
|
|
614
|
-
const canvasContext = args.g;
|
|
615
|
-
const bound = args.bounds;
|
|
616
|
-
|
|
617
|
-
canvasContext.save();
|
|
618
|
-
canvasContext.beginPath();
|
|
619
|
-
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
620
|
-
canvasContext.clip();
|
|
621
|
-
|
|
622
|
-
// Hide row column
|
|
623
|
-
const cell = args.cell;
|
|
624
|
-
if (cell.isRowHeader && cell.gridColumn.visible) {
|
|
625
|
-
cell.gridColumn.visible = false;
|
|
626
|
-
args.preventDefault();
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
const tableColName = cell.tableColumn?.name;
|
|
631
|
-
const tableRowIndex = cell.tableRowIndex!;
|
|
632
|
-
if (cell.isTableCell && tableColName && tableRowIndex !== null && renderColNames.indexOf(tableColName) !== -1) {
|
|
633
|
-
const cellValue: number | null = cell.cell.value;
|
|
634
|
-
|
|
635
|
-
if (cellValue && cellValue !== DG.INT_NULL && cellValue !== DG.FLOAT_NULL) {
|
|
636
|
-
const gridTable = cell.grid.table;
|
|
637
|
-
const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
|
|
638
|
-
tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
639
|
-
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
640
|
-
|
|
641
|
-
if (this.isInvariantMap) {
|
|
642
|
-
const value: number = this.monomerPositionStatsDf
|
|
643
|
-
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
|
|
644
|
-
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
645
|
-
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
646
|
-
CR.renderInvaraintMapCell(
|
|
647
|
-
canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
|
|
648
|
-
} else {
|
|
649
|
-
CR.renderMutationCliffCell(canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf,
|
|
650
|
-
mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo,
|
|
651
|
-
this.settings.isBidirectional);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
args.preventDefault();
|
|
596
|
+
setWebLogoInteraction(): void {
|
|
597
|
+
const sourceView = this.analysisView.grid;
|
|
598
|
+
const eventAction = (ev: MouseEvent): void => {
|
|
599
|
+
const cell = sourceView.hitTest(ev.offsetX, ev.offsetY);
|
|
600
|
+
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
|
|
601
|
+
const newBarPart = this.findAARandPosition(cell, ev);
|
|
602
|
+
this.requestBarchartAction(ev, newBarPart);
|
|
655
603
|
}
|
|
656
|
-
canvasContext.restore();
|
|
657
604
|
};
|
|
658
|
-
this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
|
|
659
|
-
this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
|
|
660
605
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
606
|
+
// The following events makes the barchart interactive
|
|
607
|
+
rxjs.fromEvent<MouseEvent>(sourceView.overlay, 'mousemove')
|
|
608
|
+
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
609
|
+
rxjs.fromEvent<MouseEvent>(sourceView.overlay, 'click')
|
|
610
|
+
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
611
|
+
}
|
|
666
612
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const positionStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
676
|
-
const rowMask = DG.BitSet.create(this.monomerPositionStatsDf.rowCount, (i) => positionStatsCol.get(i) === col.name);
|
|
677
|
-
//TODO: precalc on stats creation
|
|
678
|
-
const sortedStatsOrder = this.monomerPositionStatsDf.getSortedOrder([C.COLUMNS_NAMES.COUNT], [false], rowMask)
|
|
679
|
-
.sort((a, b) => {
|
|
680
|
-
if (monomerStatsCol.get(a) === '-')
|
|
681
|
-
return -1;
|
|
682
|
-
else if (monomerStatsCol.get(b) === '-')
|
|
683
|
-
return +1;
|
|
684
|
-
return 0;
|
|
685
|
-
});
|
|
686
|
-
const statsInfo: type.StatsInfo = {
|
|
687
|
-
countCol: countStatsCol,
|
|
688
|
-
monomerCol: monomerStatsCol,
|
|
689
|
-
orderedIndexes: sortedStatsOrder,
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
CR.drawLogoInBounds(ctx, bounds, statsInfo, this.df.rowCount, this.cp);
|
|
693
|
-
gcArgs.preventDefault();
|
|
694
|
-
}
|
|
613
|
+
findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
|
|
614
|
+
const barCoords = this.webLogoBounds[cell.tableColumn!.name];
|
|
615
|
+
for (const [monomer, coords] of Object.entries(barCoords)) {
|
|
616
|
+
const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
|
|
617
|
+
const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
|
|
618
|
+
if (isIntersectingX && isIntersectingY)
|
|
619
|
+
return {monomer: monomer, position: cell.tableColumn!.name};
|
|
620
|
+
}
|
|
695
621
|
|
|
696
|
-
|
|
697
|
-
});
|
|
622
|
+
return null;
|
|
698
623
|
}
|
|
699
624
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
625
|
+
requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
|
|
626
|
+
if (!barPart)
|
|
627
|
+
return;
|
|
628
|
+
const monomer = barPart.monomer;
|
|
629
|
+
const position = barPart.position;
|
|
630
|
+
if (ev.type === 'click') {
|
|
631
|
+
ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, false) :
|
|
632
|
+
this.initMonomerPositionSelection(monomer, position, false);
|
|
633
|
+
} else {
|
|
634
|
+
const bar = `${position} = ${monomer}`;
|
|
635
|
+
if (this.cachedWebLogoTooltip.bar == bar)
|
|
636
|
+
ui.tooltip.show(this.cachedWebLogoTooltip.tooltip!, ev.clientX, ev.clientY);
|
|
637
|
+
else
|
|
638
|
+
this.cachedWebLogoTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
|
|
705
639
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
640
|
+
//TODO: how to unghighlight?
|
|
641
|
+
// this.df.rows.match(bar).highlight();
|
|
642
|
+
}
|
|
643
|
+
}
|
|
709
644
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
645
|
+
setCellRenderers(): void {
|
|
646
|
+
const sourceGrid = this.analysisView.grid;
|
|
647
|
+
sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
648
|
+
sourceGrid.onCellRender.subscribe((gcArgs) => {
|
|
649
|
+
const ctx = gcArgs.g;
|
|
650
|
+
const bounds = gcArgs.bounds;
|
|
651
|
+
const col = gcArgs.cell.tableColumn;
|
|
715
652
|
|
|
716
|
-
|
|
653
|
+
ctx.save();
|
|
654
|
+
try {
|
|
655
|
+
// ctx.beginPath();
|
|
656
|
+
// ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
657
|
+
// ctx.clip();
|
|
658
|
+
|
|
659
|
+
//TODO: optimize
|
|
660
|
+
if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
|
|
661
|
+
const monomerStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
662
|
+
const positionStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
663
|
+
const rowMask = DG.BitSet.create(this.monomerPositionStatsDf.rowCount,
|
|
664
|
+
(i) => positionStatsCol.get(i) === col.name);
|
|
665
|
+
//TODO: precalc on stats creation
|
|
666
|
+
const sortedStatsOrder = this.monomerPositionStatsDf.getSortedOrder([C.COLUMNS_NAMES.COUNT], [false], rowMask)
|
|
667
|
+
.sort((a, b) => {
|
|
668
|
+
if (monomerStatsCol.get(a) === '-' || monomerStatsCol.get(a) === '')
|
|
669
|
+
return -1;
|
|
670
|
+
else if (monomerStatsCol.get(b) === '-' || monomerStatsCol.get(b) === '')
|
|
671
|
+
return +1;
|
|
672
|
+
return 0;
|
|
673
|
+
});
|
|
674
|
+
const statsInfo: type.StatsInfo = {
|
|
675
|
+
countCol: this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.COUNT),
|
|
676
|
+
monomerCol: monomerStatsCol,
|
|
677
|
+
orderedIndexes: sortedStatsOrder,
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
this.webLogoBounds[col.name] =
|
|
681
|
+
CR.drawLogoInBounds(ctx, bounds, statsInfo, this.df.rowCount, this.cp, this.headerSelectedMonomers[col.name]);
|
|
682
|
+
gcArgs.preventDefault();
|
|
717
683
|
}
|
|
684
|
+
} finally {
|
|
685
|
+
ctx.restore();
|
|
718
686
|
}
|
|
719
|
-
|
|
720
|
-
|
|
687
|
+
});
|
|
688
|
+
}
|
|
721
689
|
|
|
722
|
-
|
|
723
|
-
this.
|
|
724
|
-
this.sourceGrid.onCellTooltip((cell, x, y) => {
|
|
690
|
+
setTooltips(): void {
|
|
691
|
+
this.analysisView.grid.onCellTooltip((cell, x, y) => {
|
|
725
692
|
const col = cell.tableColumn;
|
|
726
693
|
const cellValue = cell.cell.value;
|
|
727
694
|
if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
|
|
@@ -734,8 +701,8 @@ export class PeptidesModel {
|
|
|
734
701
|
const tooltipElements: HTMLDivElement[] = [];
|
|
735
702
|
const monomerName = aar.toLowerCase();
|
|
736
703
|
|
|
737
|
-
|
|
738
|
-
|
|
704
|
+
const mw = getMonomerWorks();
|
|
705
|
+
const mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
|
|
739
706
|
|
|
740
707
|
if (mol) {
|
|
741
708
|
tooltipElements.push(ui.div(monomerName));
|
|
@@ -747,13 +714,30 @@ export class PeptidesModel {
|
|
|
747
714
|
ui.tooltip.show(ui.divV(tooltipElements), x, y);
|
|
748
715
|
}
|
|
749
716
|
|
|
717
|
+
//TODO: move out to viewer code
|
|
750
718
|
showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
|
|
751
719
|
const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
|
|
752
|
-
const activityCol = this.df.
|
|
720
|
+
const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
753
721
|
//TODO: use bitset instead of splitCol
|
|
754
722
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
755
723
|
const currentPosCol = this.df.getCol(position);
|
|
756
|
-
|
|
724
|
+
const indexes: number[] = [];
|
|
725
|
+
splitCol.init((i) => {
|
|
726
|
+
const sameMonomer = currentPosCol.get(i) == aar;
|
|
727
|
+
if (sameMonomer)
|
|
728
|
+
indexes.push(i);
|
|
729
|
+
|
|
730
|
+
return sameMonomer;
|
|
731
|
+
});
|
|
732
|
+
const colResults: {[colName: string]: number} = {};
|
|
733
|
+
for (const [col, agg] of Object.entries(this.settings.columns ?? {})) {
|
|
734
|
+
const currentCol = this.df.getCol(col);
|
|
735
|
+
const currentColData = currentCol.getRawData();
|
|
736
|
+
const tempCol = DG.Column.float('', indexes.length);
|
|
737
|
+
tempCol.init((i) => currentColData[indexes[i]]);
|
|
738
|
+
colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
|
|
739
|
+
}
|
|
740
|
+
|
|
757
741
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
758
742
|
const stats: Stats = {
|
|
759
743
|
count: currentStatsDf.get(C.COLUMNS_NAMES.COUNT, 0),
|
|
@@ -764,19 +748,21 @@ export class PeptidesModel {
|
|
|
764
748
|
if (!stats.count)
|
|
765
749
|
return null;
|
|
766
750
|
|
|
767
|
-
const
|
|
751
|
+
const distroStatsElem = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
|
|
768
752
|
|
|
769
|
-
ui.tooltip.show(
|
|
753
|
+
ui.tooltip.show(ui.divV([distroStatsElem, ui.tableFromMap(colResults)]), x, y);
|
|
770
754
|
|
|
771
|
-
return
|
|
755
|
+
return distroStatsElem;
|
|
772
756
|
}
|
|
773
757
|
|
|
774
758
|
showTooltipCluster(cluster: number, x: number, y: number): HTMLDivElement | null {
|
|
775
|
-
const
|
|
776
|
-
|
|
759
|
+
const matcher: {[key: string]: number} = {};
|
|
760
|
+
matcher[this.settings.clustersColumnName!] = cluster;
|
|
761
|
+
const currentStatsDf = this.clusterStatsDf.rows.match(matcher).toDataFrame();
|
|
762
|
+
const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
777
763
|
//TODO: use bitset instead of splitCol
|
|
778
764
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
779
|
-
const currentClusterCol = this.df.getCol(
|
|
765
|
+
const currentClusterCol = this.df.getCol(this.settings.clustersColumnName!);
|
|
780
766
|
splitCol.init((i) => currentClusterCol.get(i) == cluster);
|
|
781
767
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
782
768
|
const stats: Stats = {
|
|
@@ -795,49 +781,6 @@ export class PeptidesModel {
|
|
|
795
781
|
return tooltip;
|
|
796
782
|
}
|
|
797
783
|
|
|
798
|
-
setInteractionCallback(): void {
|
|
799
|
-
const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
|
|
800
|
-
const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
|
|
801
|
-
|
|
802
|
-
const chooseAction =
|
|
803
|
-
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
|
|
804
|
-
isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
|
|
805
|
-
this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
|
|
806
|
-
// this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
807
|
-
};
|
|
808
|
-
|
|
809
|
-
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
810
|
-
const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
811
|
-
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
|
|
812
|
-
return;
|
|
813
|
-
|
|
814
|
-
const position = gridCell!.tableColumn!.name;
|
|
815
|
-
const aar = mutationCliffsDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
|
|
816
|
-
chooseAction(aar, position, ev.shiftKey, this.isInvariantMap);
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
this.mostPotentResiduesGrid.root.addEventListener('click', (ev) => {
|
|
820
|
-
const gridCell = this.mostPotentResiduesGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
821
|
-
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
822
|
-
return;
|
|
823
|
-
|
|
824
|
-
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
825
|
-
const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
826
|
-
const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
827
|
-
chooseAction(aar, position, ev.shiftKey, false);
|
|
828
|
-
});
|
|
829
|
-
|
|
830
|
-
const cellChanged = (table: DG.DataFrame): void => {
|
|
831
|
-
if (this.isCellChanging)
|
|
832
|
-
return;
|
|
833
|
-
this.isCellChanging = true;
|
|
834
|
-
table.currentRowIdx = -1;
|
|
835
|
-
this.isCellChanging = false;
|
|
836
|
-
};
|
|
837
|
-
this.mutationCliffsGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mutationCliffsDf));
|
|
838
|
-
this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
|
|
839
|
-
}
|
|
840
|
-
|
|
841
784
|
modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
842
785
|
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
843
786
|
const tempSelectionAt = tempSelection[position];
|
|
@@ -865,19 +808,13 @@ export class PeptidesModel {
|
|
|
865
808
|
this.mutationCliffsSelection = tempSelection;
|
|
866
809
|
}
|
|
867
810
|
|
|
868
|
-
invalidateGrids(): void {
|
|
869
|
-
this.mutationCliffsGrid.invalidate();
|
|
870
|
-
this.mostPotentResiduesGrid.invalidate();
|
|
871
|
-
this.logoSummaryGrid?.invalidate();
|
|
872
|
-
this.sourceGrid?.invalidate();
|
|
873
|
-
}
|
|
874
|
-
|
|
875
811
|
setBitsetCallback(): void {
|
|
876
812
|
if (this.isBitsetChangedInitialized)
|
|
877
813
|
return;
|
|
878
814
|
const selection = this.df.selection;
|
|
879
815
|
const filter = this.df.filter;
|
|
880
|
-
const clusterCol = this.df.col(
|
|
816
|
+
const clusterCol = this.df.col(this.settings.clustersColumnName!);
|
|
817
|
+
const clusterColData = clusterCol?.getRawData();
|
|
881
818
|
|
|
882
819
|
const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
|
|
883
820
|
const edfSelection = this.edf?.selection;
|
|
@@ -904,7 +841,7 @@ export class PeptidesModel {
|
|
|
904
841
|
if (this.mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
905
842
|
return true;
|
|
906
843
|
}
|
|
907
|
-
if (this.logoSummarySelection.includes(
|
|
844
|
+
if (clusterColData && this.logoSummarySelection.includes(clusterColData[i]))
|
|
908
845
|
return true;
|
|
909
846
|
return false;
|
|
910
847
|
};
|
|
@@ -914,7 +851,7 @@ export class PeptidesModel {
|
|
|
914
851
|
};
|
|
915
852
|
|
|
916
853
|
selection.onChanged.subscribe(() => changeSelectionBitset(selection));
|
|
917
|
-
|
|
854
|
+
|
|
918
855
|
filter.onChanged.subscribe(() => {
|
|
919
856
|
const positionList = Object.keys(this.invariantMapSelection);
|
|
920
857
|
const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
|
|
@@ -931,7 +868,6 @@ export class PeptidesModel {
|
|
|
931
868
|
if (!this.isInvariantMapTrigger)
|
|
932
869
|
this.initBitset = filter.clone();
|
|
933
870
|
|
|
934
|
-
// filter.copyFrom(invariantMapBitset.and(this.initBitset), false);
|
|
935
871
|
const temp = invariantMapBitset.and(this.initBitset);
|
|
936
872
|
filter.init((i) => temp.get(i), false);
|
|
937
873
|
});
|
|
@@ -942,48 +878,36 @@ export class PeptidesModel {
|
|
|
942
878
|
this.isPeptideSpaceChangingBitset = isPeptideSpaceSource;
|
|
943
879
|
this.df.selection.fireChanged();
|
|
944
880
|
this.modifyOrCreateSplitCol();
|
|
945
|
-
|
|
881
|
+
this.headerSelectedMonomers = calculateSelected(this.df);
|
|
882
|
+
const acc = this.createAccordion();
|
|
883
|
+
grok.shell.o = acc.root;
|
|
884
|
+
for (const pane of acc.panes)
|
|
885
|
+
pane.expanded = true;
|
|
946
886
|
this.isPeptideSpaceChangingBitset = false;
|
|
947
887
|
}
|
|
948
888
|
|
|
949
889
|
postProcessGrids(): void {
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
const setViewerGridProps = (grid: DG.Grid): void => {
|
|
968
|
-
const gridProps = grid.props;
|
|
969
|
-
gridProps.allowEdit = false;
|
|
970
|
-
gridProps.allowRowSelection = false;
|
|
971
|
-
gridProps.allowBlockSelection = false;
|
|
972
|
-
gridProps.allowColSelection = false;
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
setViewerGridProps(this.mutationCliffsGrid);
|
|
976
|
-
setViewerGridProps(this.mostPotentResiduesGrid);
|
|
977
|
-
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
978
|
-
setViewerGridProps(this.logoSummaryGrid);
|
|
979
|
-
|
|
980
|
-
for (let gcIndex = 0; gcIndex < this.sourceGrid.columns.length; ++gcIndex) {
|
|
981
|
-
const col = this.sourceGrid.columns.byIndex(gcIndex)!;
|
|
982
|
-
col.visible =
|
|
983
|
-
col.column?.semType === C.SEM_TYPES.MONOMER ||
|
|
984
|
-
col.column?.name === C.COLUMNS_NAMES.ACTIVITY_SCALED ||
|
|
985
|
-
Object.keys(this.settings.columns ?? {}).includes(col.column?.name ?? '');
|
|
890
|
+
const posCols = this.splitSeqDf.columns.names();
|
|
891
|
+
const sourceGrid = this.analysisView.grid;
|
|
892
|
+
const sourceGridCols = sourceGrid.columns;
|
|
893
|
+
const sourceGridColsLen = sourceGridCols.length;
|
|
894
|
+
const visibleColumns = Object.keys(this.settings.columns ?? {});
|
|
895
|
+
const sourceGridProps = sourceGrid.props;
|
|
896
|
+
sourceGridProps.allowColSelection = false;
|
|
897
|
+
sourceGridProps.allowEdit = false;
|
|
898
|
+
sourceGridProps.allowRowResizing = false;
|
|
899
|
+
sourceGridProps.showCurrentRowIndicator = false;
|
|
900
|
+
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
901
|
+
for (let colIdx = 1; colIdx < sourceGridColsLen; ++colIdx) {
|
|
902
|
+
const gridCol = sourceGridCols.byIndex(colIdx)!;
|
|
903
|
+
const tableColName = gridCol.column!.name;
|
|
904
|
+
gridCol.visible = posCols.includes(tableColName) || (tableColName === C.COLUMNS_NAMES.ACTIVITY_SCALED) ||
|
|
905
|
+
visibleColumns.includes(tableColName);
|
|
906
|
+
gridCol.width = 60;
|
|
986
907
|
}
|
|
908
|
+
setTimeout(() => {
|
|
909
|
+
sourceGridProps.rowHeight = 20;
|
|
910
|
+
}, 500);
|
|
987
911
|
}
|
|
988
912
|
|
|
989
913
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
@@ -1000,76 +924,41 @@ export class PeptidesModel {
|
|
|
1000
924
|
}
|
|
1001
925
|
|
|
1002
926
|
/** Class initializer */
|
|
1003
|
-
|
|
927
|
+
init(): void {
|
|
1004
928
|
if (this.isInitialized)
|
|
1005
929
|
return;
|
|
1006
930
|
this.isInitialized = true;
|
|
1007
931
|
|
|
1008
|
-
// Don't find the dataset if the analysis started from button
|
|
1009
|
-
if (this.df.getTag('newAnalysis') !== '1')
|
|
1010
|
-
this.currentView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === '1')!;
|
|
1011
|
-
|
|
1012
|
-
this.currentView ??= grok.shell.addTableView(this.df);
|
|
1013
|
-
|
|
1014
|
-
this.df.setTag('newAnalysis', '');
|
|
1015
932
|
if (!this.isRibbonSet) {
|
|
933
|
+
//TODO: don't pass model, pass parameters instead
|
|
1016
934
|
const settingsButton = ui.bigButton('Settings', () => getSettingsDialog(this), 'Peptides analysis settings');
|
|
1017
|
-
this.
|
|
935
|
+
this.analysisView.setRibbonPanels([[settingsButton]], false);
|
|
1018
936
|
this.isRibbonSet = true;
|
|
1019
937
|
}
|
|
1020
|
-
grok.shell.v = this.currentView;
|
|
1021
|
-
this.sourceGrid = this.currentView.grid;
|
|
1022
|
-
if (this.df.tags[C.PEPTIDES_ANALYSIS] === '1')
|
|
1023
|
-
return;
|
|
1024
938
|
|
|
1025
939
|
this.df.tags[C.PEPTIDES_ANALYSIS] = '1';
|
|
1026
|
-
const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
|
|
1027
|
-
scaledGridCol.name = scaledGridCol.column!.getTag('gridName');
|
|
1028
|
-
scaledGridCol.format = '#.000';
|
|
1029
|
-
this.sourceGrid.columns.setOrder([scaledGridCol.name]);
|
|
1030
|
-
this.sourceGrid.props.allowColSelection = false;
|
|
1031
|
-
|
|
1032
|
-
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
1033
|
-
const adjustCellSize = (grid: DG.Grid): void => {
|
|
1034
|
-
const colNum = grid.columns.length;
|
|
1035
|
-
for (let i = 0; i < colNum; ++i) {
|
|
1036
|
-
const iCol = grid.columns.byIndex(i)!;
|
|
1037
|
-
iCol.width = isNaN(parseInt(iCol.name)) ? 50 : 40;
|
|
1038
|
-
}
|
|
1039
|
-
grid.props.rowHeight = 20;
|
|
1040
|
-
};
|
|
1041
940
|
|
|
1042
|
-
|
|
1043
|
-
const currentCol = this.sourceGrid.columns.byIndex(i);
|
|
1044
|
-
if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
|
|
1045
|
-
currentCol.visible = false;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
const options = {scaling: this.df.tags['scaling']};
|
|
941
|
+
this.updateDefault();
|
|
1049
942
|
|
|
1050
|
-
|
|
943
|
+
this.analysisView.grid.invalidate();
|
|
944
|
+
}
|
|
1051
945
|
|
|
1052
|
-
|
|
946
|
+
async addViewers(): Promise<void> {
|
|
947
|
+
const dockManager = this.analysisView.dockManager;
|
|
948
|
+
const dfPlt = this.df.plot;
|
|
1053
949
|
|
|
1054
|
-
const
|
|
1055
|
-
|
|
950
|
+
const mutationCliffsViewer = await dfPlt.fromType('peptide-sar-viewer') as MonomerPosition;
|
|
951
|
+
const mostPotentResiduesViewer = await dfPlt.fromType('peptide-sar-viewer-vertical') as MostPotentResiduesViewer;
|
|
952
|
+
if (this.settings.clustersColumnName)
|
|
953
|
+
await this.addLogoSummaryTableViewer();
|
|
1056
954
|
|
|
1057
|
-
|
|
1058
|
-
const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
|
|
1059
|
-
dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
this.updateDefault();
|
|
1063
|
-
|
|
1064
|
-
const mcNode =
|
|
1065
|
-
dockManager.dock(mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, mutationCliffsViewer.name);
|
|
955
|
+
const mcNode = dockManager.dock(mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, mutationCliffsViewer.name);
|
|
1066
956
|
|
|
1067
957
|
dockManager.dock(mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, mostPotentResiduesViewer.name, 0.3);
|
|
958
|
+
}
|
|
1068
959
|
|
|
1069
|
-
|
|
1070
|
-
this.
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
this.invalidateGrids();
|
|
960
|
+
async addLogoSummaryTableViewer(): Promise<void> {
|
|
961
|
+
const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
|
|
962
|
+
this.analysisView.dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
1074
963
|
}
|
|
1075
964
|
}
|