@datagrok/peptides 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-test.js +8275 -3759
- package/dist/package.js +19735 -14773
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +306 -215
- package/files/aligned.csv +648 -648
- package/files/aligned_2.csv +541 -10275
- package/files/aligned_3.csv +335 -0
- package/helm/JSDraw/Pistoia.HELM.js +27 -0
- package/package.json +17 -12
- package/src/__jest__/remote.test.ts +31 -13
- package/src/model.ts +321 -342
- package/src/package-test.ts +0 -1
- package/src/package.ts +13 -109
- package/src/tests/core.ts +48 -16
- package/src/tests/utils.ts +0 -11
- package/src/utils/cell-renderer.ts +92 -275
- package/src/utils/constants.ts +5 -4
- package/src/utils/misc.ts +103 -16
- package/src/utils/multiple-sequence-alignment.ts +1 -1
- package/src/utils/peptide-similarity-space.ts +1 -1
- package/src/utils/types.ts +7 -5
- package/src/viewers/peptide-space-viewer.ts +3 -3
- package/src/viewers/sar-viewer.ts +34 -23
- package/src/widgets/analyze-peptides.ts +23 -17
- package/src/widgets/distribution.ts +0 -1
- package/src/widgets/manual-alignment.ts +4 -4
- package/src/widgets/subst-table.ts +5 -2
- package/{test-Peptides-34f75e5127b8-4210edfc.html → test-Peptides-4f0c8bae6479-74cbfe68.html} +8 -8
- package/detectors.js +0 -9
- package/src/monomer-library.ts +0 -193
- package/src/tests/msa-tests.ts +0 -27
- package/src/utils/chem-palette.ts +0 -280
- package/src/viewers/stacked-barchart-viewer.ts +0 -321
- package/src/widgets/multiple-sequence-alignment.ts +0 -9
- package/src/widgets/peptide-molecule.ts +0 -82
package/src/model.ts
CHANGED
|
@@ -3,24 +3,21 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {Subject, Observable} from 'rxjs';
|
|
6
|
-
import {addViewerToHeader, StackedBarChart} from './viewers/stacked-barchart-viewer';
|
|
7
|
-
import {tTest} from '@datagrok-libraries/statistics/src/tests';
|
|
8
|
-
import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests';
|
|
9
|
-
import {ChemPalette} from './utils/chem-palette';
|
|
10
|
-
import {MonomerLibrary} from './monomer-library';
|
|
11
6
|
import * as C from './utils/constants';
|
|
12
7
|
import * as type from './utils/types';
|
|
13
|
-
import {
|
|
8
|
+
import {calculateBarsData, getTypedArrayConstructor, scaleActivity, splitAlignedPeptides} from './utils/misc';
|
|
14
9
|
import {_package} from './package';
|
|
15
|
-
import {SARViewer, SARViewerVertical} from './viewers/sar-viewer';
|
|
10
|
+
import {SARViewer, SARViewerBase, SARViewerVertical} from './viewers/sar-viewer';
|
|
16
11
|
import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
|
|
17
|
-
import {renderSARCell, setAARRenderer} from './utils/cell-renderer';
|
|
12
|
+
import {renderBarchart, renderSARCell, setAARRenderer} from './utils/cell-renderer';
|
|
18
13
|
import {substitutionsWidget} from './widgets/subst-table';
|
|
19
14
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
20
15
|
import {getStats, Stats} from './utils/filtering-statistics';
|
|
16
|
+
import * as rxjs from 'rxjs';
|
|
17
|
+
|
|
21
18
|
|
|
22
19
|
export class PeptidesModel {
|
|
23
|
-
static
|
|
20
|
+
static modelName = 'peptidesModel';
|
|
24
21
|
|
|
25
22
|
_statsDataFrameSubject = new Subject<DG.DataFrame>();
|
|
26
23
|
_sarGridSubject = new Subject<DG.Grid>();
|
|
@@ -32,20 +29,11 @@ export class PeptidesModel {
|
|
|
32
29
|
isBitsetChangedInitialized = false;
|
|
33
30
|
isCellChanging = false;
|
|
34
31
|
|
|
35
|
-
//viewer properties
|
|
36
|
-
_filterMode!: boolean;
|
|
37
|
-
_twoColorMode!: boolean;
|
|
38
|
-
_activityScaling!: string;
|
|
39
|
-
_isSubstitutionOn!: boolean;
|
|
40
|
-
_activityLimit!: number;
|
|
41
|
-
_maxSubstitutions!: number;
|
|
42
|
-
|
|
43
32
|
_sarGrid!: DG.Grid;
|
|
44
33
|
_sarVGrid!: DG.Grid;
|
|
45
34
|
_sourceGrid!: DG.Grid;
|
|
46
|
-
|
|
35
|
+
df: DG.DataFrame;
|
|
47
36
|
splitCol!: DG.Column<boolean>;
|
|
48
|
-
stackedBarchart!: StackedBarChart;
|
|
49
37
|
edf: DG.DataFrame | null = null;
|
|
50
38
|
statsDf!: DG.DataFrame;
|
|
51
39
|
_currentSelection!: type.SelectionObject;
|
|
@@ -55,28 +43,27 @@ export class PeptidesModel {
|
|
|
55
43
|
|
|
56
44
|
isPeptideSpaceChangingBitset = false;
|
|
57
45
|
isChangingEdfBitset = false;
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
|
|
47
|
+
sarViewer!: SARViewer;
|
|
48
|
+
sarViewerVertical!: SARViewerVertical;
|
|
49
|
+
|
|
50
|
+
_usedProperties: {[propName: string]: string | number | boolean} = {};
|
|
51
|
+
monomerMap: {[key: string]: {molfile: string, fullName: string}} = {};
|
|
52
|
+
barData: type.MonomerDfStats = {};
|
|
53
|
+
barsBounds: {[position: string]: type.BarCoordinates} = {};
|
|
54
|
+
cachedBarchartTooltip: {bar: string, tooltip: null | HTMLDivElement} = {bar: '', tooltip: null};
|
|
55
|
+
monomerLib: any;
|
|
60
56
|
|
|
61
57
|
private constructor(dataFrame: DG.DataFrame) {
|
|
62
|
-
this.
|
|
63
|
-
this.updateProperties();
|
|
58
|
+
this.df = dataFrame;
|
|
64
59
|
}
|
|
65
60
|
|
|
66
|
-
static async getInstance(dataFrame: DG.DataFrame
|
|
67
|
-
if (dataFrame.temp[MonomerLibrary.id] === null) {
|
|
68
|
-
const sdf = await (dgPackage ?? _package).files.readAsText('HELMMonomers_June10.sdf');
|
|
69
|
-
dataFrame.temp[MonomerLibrary.id] ??= new MonomerLibrary(sdf);
|
|
70
|
-
}
|
|
61
|
+
static async getInstance(dataFrame: DG.DataFrame): Promise<PeptidesModel> {
|
|
71
62
|
dataFrame.temp[PeptidesModel.modelName] ??= new PeptidesModel(dataFrame);
|
|
72
63
|
await (dataFrame.temp[PeptidesModel.modelName] as PeptidesModel).init();
|
|
73
64
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
74
65
|
}
|
|
75
66
|
|
|
76
|
-
static get modelName(): string {return PeptidesModel._modelName;}
|
|
77
|
-
|
|
78
|
-
static get chemPalette(): typeof ChemPalette {return ChemPalette;}
|
|
79
|
-
|
|
80
67
|
get onStatsDataFrameChanged(): Observable<DG.DataFrame> {return this._statsDataFrameSubject.asObservable();}
|
|
81
68
|
|
|
82
69
|
get onSARGridChanged(): Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
|
|
@@ -86,13 +73,41 @@ export class PeptidesModel {
|
|
|
86
73
|
get onSubstTableChanged(): Observable<type.SubstitutionsInfo> {return this._substitutionTableSubject.asObservable();}
|
|
87
74
|
|
|
88
75
|
get currentSelection(): type.SelectionObject {
|
|
89
|
-
this._currentSelection ??= JSON.parse(this.
|
|
76
|
+
this._currentSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
90
77
|
return this._currentSelection;
|
|
91
78
|
}
|
|
92
79
|
set currentSelection(selection: type.SelectionObject) {
|
|
93
80
|
this._currentSelection = selection;
|
|
94
|
-
this.
|
|
81
|
+
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
95
82
|
this.invalidateSelection();
|
|
83
|
+
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get usedProperties(): {[propName: string]: string | number | boolean} {
|
|
87
|
+
this._usedProperties = JSON.parse(this.df.tags['sarProperties'] ?? '{}');
|
|
88
|
+
return this._usedProperties;
|
|
89
|
+
}
|
|
90
|
+
set usedProperties(properties: {[propName: string]: string | number | boolean}) {
|
|
91
|
+
this.df.tags['sarProperties'] = JSON.stringify(properties);
|
|
92
|
+
this._usedProperties = properties;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get splitByPos() {
|
|
96
|
+
const splitByPosFlag = (this.df.tags['distributionSplit'] ?? '00')[0];
|
|
97
|
+
return splitByPosFlag == '1' ? true : false;
|
|
98
|
+
}
|
|
99
|
+
set splitByPos(flag: boolean) {
|
|
100
|
+
const splitByAARFlag = (this.df.tags['distributionSplit'] ?? '00')[1];
|
|
101
|
+
this.df.tags['distributionSplit'] = `${flag ? 1 : 0}${splitByAARFlag}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get splitByAAR() {
|
|
105
|
+
const splitByPosFlag = (this.df.tags['distributionSplit'] ?? '00')[1];
|
|
106
|
+
return splitByPosFlag == '1' ? true : false;
|
|
107
|
+
}
|
|
108
|
+
set splitByAAR(flag: boolean) {
|
|
109
|
+
const splitByAARFlag = (this.df.tags['distributionSplit'] ?? '00')[0];
|
|
110
|
+
this.df.tags['distributionSplit'] = `${splitByAARFlag}${flag ? 1 : 0}`;
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
invalidateSelection(): void {
|
|
@@ -103,111 +118,87 @@ export class PeptidesModel {
|
|
|
103
118
|
createAccordion() {
|
|
104
119
|
const acc = ui.accordion();
|
|
105
120
|
acc.root.style.width = '100%';
|
|
106
|
-
acc.addTitle(ui.h1(`${this.
|
|
107
|
-
acc.addPane('Substitutions', () => substitutionsWidget(this.
|
|
108
|
-
acc.addPane('Distribtution', () => getDistributionWidget(this.
|
|
121
|
+
acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
|
|
122
|
+
acc.addPane('Substitutions', () => substitutionsWidget(this.df, this).root, true);
|
|
123
|
+
acc.addPane('Distribtution', () => getDistributionWidget(this.df, this).root, true);
|
|
109
124
|
|
|
110
125
|
return acc;
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
this.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
async updateData(
|
|
143
|
-
activityScaling?: string, sourceGrid?: DG.Grid, twoColorMode?: boolean, activityLimit?: number,
|
|
144
|
-
maxSubstitutions?: number, isSubstitutionOn?: boolean, filterMode?: boolean,
|
|
145
|
-
): Promise<void> {
|
|
146
|
-
//FIXME: threre are too many assignments, some are duplicating
|
|
147
|
-
this._activityScaling = activityScaling ?? this._activityScaling;
|
|
148
|
-
this._sourceGrid = sourceGrid ?? this._sourceGrid;
|
|
149
|
-
this._twoColorMode = twoColorMode ?? this._twoColorMode;
|
|
150
|
-
this._activityLimit = activityLimit ?? this._activityLimit;
|
|
151
|
-
this._maxSubstitutions = maxSubstitutions ?? this._maxSubstitutions;
|
|
152
|
-
this._isSubstitutionOn = isSubstitutionOn ?? this._isSubstitutionOn;
|
|
153
|
-
this._filterMode = filterMode ?? this._filterMode;
|
|
154
|
-
this.setProperties(this._activityScaling, this._filterMode, this._twoColorMode, this._isSubstitutionOn,
|
|
155
|
-
this._maxSubstitutions, this._activityLimit, true);
|
|
156
|
-
|
|
157
|
-
await this.updateDefault();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async updateDefault(): Promise<void> {
|
|
161
|
-
if (this._activityScaling && this._sourceGrid && this._twoColorMode !== null && !this._isUpdating) {
|
|
128
|
+
getViewer(): SARViewerBase {
|
|
129
|
+
const viewer = this.sarViewer ?? this.sarViewerVertical;
|
|
130
|
+
if (!viewer)
|
|
131
|
+
throw new Error('ViewerError: none of the SAR viewers is initialized');
|
|
132
|
+
return viewer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
isPropertyChanged(viewer: SARViewerBase): boolean {
|
|
136
|
+
// const viewer = this.getViewer();
|
|
137
|
+
const viewerProps = viewer.props.getProperties();
|
|
138
|
+
let result = false;
|
|
139
|
+
const tempProps = this.usedProperties;
|
|
140
|
+
for (const property of viewerProps) {
|
|
141
|
+
const propName = property.name;
|
|
142
|
+
const propVal = property.get(viewer);
|
|
143
|
+
if (tempProps[propName] != propVal) {
|
|
144
|
+
tempProps[propName] = propVal;
|
|
145
|
+
result = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.usedProperties = tempProps;
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
updateDefault(): void {
|
|
153
|
+
const viewer = this.getViewer();
|
|
154
|
+
const proprtyChanged = this.isPropertyChanged(this.sarViewer) || this.isPropertyChanged(this.sarViewerVertical);
|
|
155
|
+
if ((this._sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
156
|
+
this.isInitialized = true;
|
|
162
157
|
this._isUpdating = true;
|
|
163
|
-
const [viewerGrid, viewerVGrid, statsDf] =
|
|
158
|
+
const [viewerGrid, viewerVGrid, statsDf] = this.initializeViewersComponents();
|
|
164
159
|
//FIXME: modify during the initializeViewersComponents stages
|
|
165
160
|
this._statsDataFrameSubject.next(statsDf);
|
|
166
161
|
this._sarGridSubject.next(viewerGrid);
|
|
167
162
|
this._sarVGridSubject.next(viewerVGrid);
|
|
168
|
-
if (
|
|
163
|
+
if (viewer.showSubstitution) {
|
|
169
164
|
this._substitutionTableSubject.next(this.substitutionsInfo);
|
|
170
165
|
this._isSubstInitialized = true;
|
|
171
166
|
}
|
|
172
|
-
}
|
|
173
|
-
await this.updateBarchart();
|
|
174
|
-
this.invalidateSelection();
|
|
175
167
|
|
|
176
|
-
|
|
177
|
-
}
|
|
168
|
+
this.invalidateSelection();
|
|
178
169
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (this.stackedBarchart && this._sourceGrid)
|
|
182
|
-
addViewerToHeader(this._sourceGrid, this.stackedBarchart);
|
|
170
|
+
this._isUpdating = false;
|
|
171
|
+
}
|
|
183
172
|
}
|
|
184
173
|
|
|
185
|
-
|
|
174
|
+
initializeViewersComponents(): [DG.Grid, DG.Grid, DG.DataFrame] {
|
|
186
175
|
if (this._sourceGrid === null)
|
|
187
176
|
throw new Error(`Source grid is not initialized`);
|
|
188
177
|
|
|
189
178
|
//Split the aligned sequence into separate AARs
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
179
|
+
const col: DG.Column = this.df.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
180
|
+
const alphabet = col.tags[DG.TAGS.UNITS].split(':')[2];
|
|
181
|
+
const splitSeqDf = splitAlignedPeptides(col);
|
|
182
|
+
|
|
183
|
+
this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
|
|
194
184
|
|
|
195
185
|
const positionColumns = splitSeqDf.columns.names();
|
|
196
|
-
const renderColNames: string[] = splitSeqDf.columns.names();
|
|
197
186
|
|
|
198
|
-
const activityCol = this.
|
|
187
|
+
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
199
188
|
splitSeqDf.columns.add(activityCol);
|
|
200
189
|
|
|
201
190
|
this.joinDataFrames(positionColumns, splitSeqDf);
|
|
202
191
|
|
|
203
|
-
for (const dfCol of this.
|
|
192
|
+
for (const dfCol of this.df.columns) {
|
|
204
193
|
if (positionColumns.includes(dfCol.name))
|
|
205
|
-
setAARRenderer(dfCol, this._sourceGrid);
|
|
194
|
+
setAARRenderer(dfCol, alphabet, this._sourceGrid);
|
|
206
195
|
}
|
|
207
196
|
|
|
208
|
-
this.sortSourceGrid(
|
|
197
|
+
this.sortSourceGrid();
|
|
209
198
|
|
|
210
|
-
|
|
199
|
+
const viewer = this.getViewer();
|
|
200
|
+
|
|
201
|
+
this.createScaledCol(viewer.scaling, splitSeqDf);
|
|
211
202
|
|
|
212
203
|
//unpivot a table and handle duplicates
|
|
213
204
|
let matrixDf = splitSeqDf.groupBy(positionColumns).aggregate();
|
|
@@ -226,66 +217,68 @@ export class PeptidesModel {
|
|
|
226
217
|
matrixDf.name = 'SAR';
|
|
227
218
|
|
|
228
219
|
// Setting category order
|
|
229
|
-
|
|
220
|
+
this.setCategoryOrder(matrixDf);
|
|
230
221
|
|
|
231
222
|
// SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
|
|
232
223
|
const sequenceDf = this.createVerticalTable();
|
|
233
|
-
renderColNames.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
234
224
|
|
|
235
|
-
if (
|
|
225
|
+
if (viewer.showSubstitution || !this._isSubstInitialized)
|
|
236
226
|
this.calcSubstitutions();
|
|
237
227
|
|
|
238
|
-
|
|
239
|
-
const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf);
|
|
228
|
+
const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf, alphabet);
|
|
240
229
|
|
|
241
230
|
this._sarGrid = sarGrid;
|
|
242
231
|
this._sarVGrid = sarVGrid;
|
|
243
232
|
|
|
244
|
-
|
|
233
|
+
positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
234
|
+
|
|
235
|
+
this.setBarChartInteraction();
|
|
236
|
+
|
|
237
|
+
this.setCellRenderers(positionColumns, sarGrid, sarVGrid);
|
|
245
238
|
|
|
246
239
|
// show all the statistics in a tooltip over cell
|
|
247
|
-
this.setTooltips(
|
|
240
|
+
this.setTooltips(positionColumns, sarGrid, sarVGrid);
|
|
248
241
|
|
|
249
242
|
this.setInteractionCallback();
|
|
250
243
|
|
|
251
244
|
this.setBitsetCallback();
|
|
252
245
|
|
|
253
|
-
this.postProcessGrids(
|
|
246
|
+
this.postProcessGrids(sarGrid, sarVGrid);
|
|
254
247
|
|
|
255
248
|
//TODO: return class instead
|
|
256
249
|
return [sarGrid, sarVGrid, this.statsDf];
|
|
257
250
|
}
|
|
258
251
|
|
|
259
|
-
//TODO: move to controller?
|
|
260
252
|
calcSubstitutions(): void {
|
|
261
|
-
const activityValues: DG.Column<number> = this.
|
|
262
|
-
const columnList: DG.Column<string>[] = this.
|
|
253
|
+
const activityValues: DG.Column<number> = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
254
|
+
const columnList: DG.Column<string>[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
|
|
263
255
|
const nCols = columnList.length;
|
|
264
256
|
if (nCols == 0)
|
|
265
|
-
throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.
|
|
257
|
+
throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
|
|
266
258
|
|
|
259
|
+
const viewer = this.getViewer();
|
|
267
260
|
this.substitutionsInfo = new Map();
|
|
268
|
-
const nRows = this.
|
|
261
|
+
const nRows = this.df.rowCount;
|
|
269
262
|
for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
|
|
270
263
|
for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
|
|
271
264
|
let substCounter = 0;
|
|
272
|
-
const activityValSeq1 = activityValues.get(seq1Idx)
|
|
273
|
-
const activityValSeq2 = activityValues.get(seq2Idx)
|
|
265
|
+
const activityValSeq1 = activityValues.get(seq1Idx)!;
|
|
266
|
+
const activityValSeq2 = activityValues.get(seq2Idx)!;
|
|
274
267
|
const delta = activityValSeq1 - activityValSeq2;
|
|
275
|
-
if (Math.abs(delta) <
|
|
268
|
+
if (Math.abs(delta) < viewer.minActivityDelta)
|
|
276
269
|
continue;
|
|
277
270
|
|
|
278
271
|
let substCounterFlag = false;
|
|
279
272
|
const tempData: {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number}[] =
|
|
280
273
|
[];
|
|
281
274
|
for (const currentPosCol of columnList) {
|
|
282
|
-
const seq1monomer = currentPosCol.get(seq1Idx)
|
|
283
|
-
const seq2monomer = currentPosCol.get(seq2Idx)
|
|
275
|
+
const seq1monomer = currentPosCol.get(seq1Idx)!;
|
|
276
|
+
const seq2monomer = currentPosCol.get(seq2Idx)!;
|
|
284
277
|
if (seq1monomer == seq2monomer)
|
|
285
278
|
continue;
|
|
286
279
|
|
|
287
280
|
substCounter++;
|
|
288
|
-
substCounterFlag = substCounter >
|
|
281
|
+
substCounterFlag = substCounter > viewer.maxSubstitutions;
|
|
289
282
|
if (substCounterFlag)
|
|
290
283
|
break;
|
|
291
284
|
|
|
@@ -343,58 +336,52 @@ export class PeptidesModel {
|
|
|
343
336
|
|
|
344
337
|
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame): void {
|
|
345
338
|
// append splitSeqDf columns to source table and make sure columns are not added more than once
|
|
346
|
-
const name = this.
|
|
347
|
-
const dfColsSet = new Set(this.
|
|
339
|
+
const name = this.df.name;
|
|
340
|
+
const dfColsSet = new Set(this.df.columns.names());
|
|
348
341
|
if (!positionColumns.every((col: string) => dfColsSet.has(col))) {
|
|
349
|
-
this.
|
|
350
|
-
this.
|
|
342
|
+
this.df.join(splitSeqDf, [C.COLUMNS_NAMES.ACTIVITY], [C.COLUMNS_NAMES.ACTIVITY],
|
|
343
|
+
this.df.columns.names(), positionColumns, 'inner', true);
|
|
351
344
|
}
|
|
352
|
-
this.
|
|
345
|
+
this.df.name = name;
|
|
353
346
|
this.currentView.name = name;
|
|
354
347
|
}
|
|
355
348
|
|
|
356
|
-
sortSourceGrid(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
colNames.push(sourceGrid.columns.byIndex(i)!);
|
|
349
|
+
sortSourceGrid(): void {
|
|
350
|
+
const colNames: DG.GridColumn[] = [];
|
|
351
|
+
for (let i = 1; i < this._sourceGrid.columns.length; i++)
|
|
352
|
+
colNames.push(this._sourceGrid.columns.byIndex(i)!);
|
|
361
353
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
354
|
+
colNames.sort((a, b)=>{
|
|
355
|
+
if (a.column!.semType == C.SEM_TYPES.MONOMER) {
|
|
356
|
+
if (b.column!.semType == C.SEM_TYPES.MONOMER)
|
|
357
|
+
return 0;
|
|
358
|
+
return -1;
|
|
359
|
+
}
|
|
360
|
+
if (b.column!.semType == C.SEM_TYPES.MONOMER)
|
|
361
|
+
return 1;
|
|
362
|
+
return 0;
|
|
363
|
+
});
|
|
364
|
+
this._sourceGrid.columns.setOrder(colNames.map((v) => v.name));
|
|
374
365
|
}
|
|
375
366
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
): Promise<void> {
|
|
380
|
-
const [scaledDf, newColName] = await PeptidesModel.scaleActivity(
|
|
381
|
-
activityScaling, df, df.tags[C.COLUMNS_NAMES.ACTIVITY]);
|
|
367
|
+
createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
|
|
368
|
+
const [scaledDf, newColName] =
|
|
369
|
+
scaleActivity(activityScaling, this.df, this.df.tags[C.COLUMNS_NAMES.ACTIVITY]);
|
|
382
370
|
//TODO: make another func
|
|
383
371
|
const scaledCol = scaledDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
384
372
|
scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
|
|
385
373
|
splitSeqDf.columns.add(scaledCol);
|
|
386
|
-
const oldScaledCol = df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
387
|
-
df.columns.replace(oldScaledCol, scaledCol);
|
|
388
|
-
const gridCol =
|
|
374
|
+
const oldScaledCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
375
|
+
this.df.columns.replace(oldScaledCol, scaledCol);
|
|
376
|
+
const gridCol = this._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
389
377
|
if (gridCol !== null) {
|
|
390
378
|
gridCol.name = newColName;
|
|
391
|
-
df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
|
|
379
|
+
this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
|
|
392
380
|
}
|
|
393
381
|
|
|
394
|
-
|
|
382
|
+
this._sourceGrid.columns.setOrder([newColName]);
|
|
395
383
|
}
|
|
396
384
|
|
|
397
|
-
//TODO: move out
|
|
398
385
|
calculateStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
399
386
|
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.AMINO_ACID_RESIDUE]).aggregate();
|
|
400
387
|
|
|
@@ -406,16 +393,15 @@ export class PeptidesModel {
|
|
|
406
393
|
const ratioCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
|
|
407
394
|
const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
|
|
408
395
|
const posCol = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
409
|
-
const activityCol: number[] = this.
|
|
396
|
+
const activityCol: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
|
|
410
397
|
const sourceDfLen = activityCol.length;
|
|
411
398
|
|
|
412
399
|
for (let i = 0; i < matrixDf.rowCount; i++) {
|
|
413
400
|
const position: string = posCol.get(i);
|
|
414
401
|
const aar: string = aarCol.get(i);
|
|
415
|
-
const mask = DG.BitSet.create(sourceDfLen, (j) => this.
|
|
402
|
+
const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) == aar);
|
|
416
403
|
const stats = getStats(activityCol, mask);
|
|
417
404
|
|
|
418
|
-
//TODO: store as object in a single column
|
|
419
405
|
mdCol.set(i, stats.meanDifference);
|
|
420
406
|
pValCol.set(i, stats.pValue);
|
|
421
407
|
countCol.set(i, stats.count);
|
|
@@ -429,11 +415,14 @@ export class PeptidesModel {
|
|
|
429
415
|
return matrixDf as DG.DataFrame;
|
|
430
416
|
}
|
|
431
417
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
418
|
+
setCategoryOrder(matrixDf: DG.DataFrame): void {
|
|
419
|
+
let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
|
|
420
|
+
if (this.getViewer().bidirectionalAnalysis) {
|
|
421
|
+
const mdCol = this.statsDf.getCol(sortArgument);
|
|
422
|
+
sortArgument = 'Absolute Mean difference';
|
|
423
|
+
const absMDCol = this.statsDf.columns.addNewFloat(sortArgument);
|
|
424
|
+
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
425
|
+
}
|
|
437
426
|
|
|
438
427
|
const aarWeightsDf = this.statsDf.groupBy([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE]).sum(sortArgument, 'weight')
|
|
439
428
|
.aggregate();
|
|
@@ -464,7 +453,7 @@ export class PeptidesModel {
|
|
|
464
453
|
const rowCount = sequenceDf.rowCount;
|
|
465
454
|
for (const pos of posColCategories) {
|
|
466
455
|
tempStats = DG.Stats.fromColumn(mdCol, DG.BitSet.create(rowCount, (i) => posCol.get(i) === pos));
|
|
467
|
-
maxAtPos[pos] = this.
|
|
456
|
+
maxAtPos[pos] = this.getViewer().bidirectionalAnalysis ?
|
|
468
457
|
(tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) :
|
|
469
458
|
tempStats.max;
|
|
470
459
|
}
|
|
@@ -473,7 +462,8 @@ export class PeptidesModel {
|
|
|
473
462
|
return sequenceDf;
|
|
474
463
|
}
|
|
475
464
|
|
|
476
|
-
createGrids(
|
|
465
|
+
createGrids(
|
|
466
|
+
matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame, alphabet: string): DG.Grid[] {
|
|
477
467
|
const sarGrid = matrixDf.plot.grid();
|
|
478
468
|
sarGrid.sort([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE]);
|
|
479
469
|
sarGrid.columns.setOrder([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE].concat(positionColumns as C.COLUMNS_NAMES[]));
|
|
@@ -486,16 +476,58 @@ export class PeptidesModel {
|
|
|
486
476
|
|
|
487
477
|
let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
|
|
488
478
|
if (tempCol)
|
|
489
|
-
setAARRenderer(tempCol, sarGrid);
|
|
479
|
+
setAARRenderer(tempCol, alphabet, sarGrid);
|
|
490
480
|
|
|
491
481
|
tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
|
|
492
482
|
if (tempCol)
|
|
493
|
-
setAARRenderer(tempCol, sarGrid);
|
|
483
|
+
setAARRenderer(tempCol, alphabet, sarGrid);
|
|
494
484
|
|
|
495
485
|
return [sarGrid, sarVGrid];
|
|
496
486
|
}
|
|
497
487
|
|
|
498
|
-
|
|
488
|
+
setBarChartInteraction() {
|
|
489
|
+
const eventAction = (ev: MouseEvent): void => {
|
|
490
|
+
const cell = this._sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
491
|
+
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
|
|
492
|
+
const newBarPart = this.findAARandPosition(cell, ev);
|
|
493
|
+
this.requestBarchartAction(ev, newBarPart);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// The following events makes the barchart interactive
|
|
498
|
+
rxjs.fromEvent<MouseEvent>(this._sourceGrid.overlay, 'mousemove').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
499
|
+
rxjs.fromEvent<MouseEvent>(this._sourceGrid.overlay, 'click').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
findAARandPosition(cell: DG.GridCell, ev: MouseEvent) {
|
|
503
|
+
const barCoords = this.barsBounds[cell.tableColumn!.name];
|
|
504
|
+
for (const [monomer, coords] of Object.entries(barCoords)) {
|
|
505
|
+
const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
|
|
506
|
+
const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
|
|
507
|
+
if (isIntersectingX && isIntersectingY)
|
|
508
|
+
return {monomer: monomer, position: cell.tableColumn!.name};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
requestBarchartAction(ev: MouseEvent, barPart: {position: string, monomer: string} | null): void {
|
|
515
|
+
if (!barPart)
|
|
516
|
+
return;
|
|
517
|
+
const monomer = barPart.monomer;
|
|
518
|
+
const position = barPart.position;
|
|
519
|
+
if (ev.type === 'click') {
|
|
520
|
+
ev.shiftKey ? this.modifyCurrentSelection(monomer, position) :
|
|
521
|
+
this.initCurrentSelection(monomer, position);
|
|
522
|
+
} else {
|
|
523
|
+
const bar = `${monomer}:${position}`;
|
|
524
|
+
if (this.cachedBarchartTooltip.bar == bar)
|
|
525
|
+
ui.tooltip.show(this.cachedBarchartTooltip.tooltip!, ev.clientX, ev.clientY);
|
|
526
|
+
else
|
|
527
|
+
this.cachedBarchartTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
499
531
|
setCellRenderers(renderColNames: string[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
|
|
500
532
|
const mdCol = this.statsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
501
533
|
//decompose into two different renering funcs
|
|
@@ -527,8 +559,9 @@ export class PeptidesModel {
|
|
|
527
559
|
tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
528
560
|
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIndex);
|
|
529
561
|
|
|
530
|
-
|
|
531
|
-
|
|
562
|
+
const viewer = this.getViewer();
|
|
563
|
+
renderSARCell(canvasContext, currentAAR, currentPosition, this.statsDf, viewer.bidirectionalAnalysis, mdCol,
|
|
564
|
+
bound, cellValue, this.currentSelection, viewer.showSubstitution ? this.substitutionsInfo : null);
|
|
532
565
|
}
|
|
533
566
|
args.preventDefault();
|
|
534
567
|
}
|
|
@@ -536,26 +569,45 @@ export class PeptidesModel {
|
|
|
536
569
|
};
|
|
537
570
|
sarGrid.onCellRender.subscribe(renderCell);
|
|
538
571
|
sarVGrid.onCellRender.subscribe(renderCell);
|
|
572
|
+
|
|
573
|
+
this._sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
574
|
+
this._sourceGrid.onCellRender.subscribe((gcArgs) => {
|
|
575
|
+
const context = gcArgs.g;
|
|
576
|
+
const bounds = gcArgs.bounds;
|
|
577
|
+
const col = gcArgs.cell.tableColumn;
|
|
578
|
+
|
|
579
|
+
context.save();
|
|
580
|
+
context.beginPath();
|
|
581
|
+
context.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
582
|
+
context.clip();
|
|
583
|
+
|
|
584
|
+
if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
|
|
585
|
+
const barBounds = renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
|
|
586
|
+
this.barsBounds[col.name] = barBounds;
|
|
587
|
+
gcArgs.preventDefault();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
context.restore();
|
|
591
|
+
});
|
|
539
592
|
}
|
|
540
593
|
|
|
541
594
|
setTooltips(renderColNames: string[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
|
|
542
595
|
const showTooltip = (cell: DG.GridCell, x: number, y: number): boolean => {
|
|
543
596
|
const tableCol = cell.tableColumn;
|
|
544
597
|
const tableColName = tableCol?.name;
|
|
598
|
+
const tableRowIndex = cell.tableRowIndex;
|
|
545
599
|
|
|
546
|
-
if (!cell.isRowHeader && !cell.isColHeader && tableCol
|
|
547
|
-
const
|
|
600
|
+
if (!cell.isRowHeader && !cell.isColHeader && tableCol && tableRowIndex) {
|
|
601
|
+
const table = cell.grid.table;
|
|
602
|
+
const currentAAR = table.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIndex);
|
|
548
603
|
|
|
549
|
-
if (tableCol.semType == C.SEM_TYPES.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
} else if (cell.cell.value !== null && tableRowIndex !== null && renderColNames.includes(tableColName!)) {
|
|
553
|
-
const table = cell.grid.table;
|
|
604
|
+
if (tableCol.semType == C.SEM_TYPES.MONOMER)
|
|
605
|
+
this.showMonomerTooltip(currentAAR, x, y);
|
|
606
|
+
else if (cell.cell.value && renderColNames.includes(tableColName!)) {
|
|
554
607
|
const currentPosition = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ? tableColName :
|
|
555
608
|
table.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
556
|
-
const currentAAR = table.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIndex);
|
|
557
609
|
|
|
558
|
-
this.
|
|
610
|
+
this.showTooltipAt(currentAAR, currentPosition, x, y);
|
|
559
611
|
}
|
|
560
612
|
}
|
|
561
613
|
return true;
|
|
@@ -563,13 +615,36 @@ export class PeptidesModel {
|
|
|
563
615
|
|
|
564
616
|
sarGrid.onCellTooltip(showTooltip);
|
|
565
617
|
sarVGrid.onCellTooltip(showTooltip);
|
|
618
|
+
this._sourceGrid.onCellTooltip((cell, x, y) => {
|
|
619
|
+
const col = cell.tableColumn;
|
|
620
|
+
const cellValue = cell.cell.value;
|
|
621
|
+
if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
|
|
622
|
+
this.showMonomerTooltip(cellValue, x, y);
|
|
623
|
+
return true;
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
showMonomerTooltip(aar: string, x: number, y: number): void {
|
|
628
|
+
const tooltipElements: HTMLDivElement[] = [];
|
|
629
|
+
//@ts-ignore: no types for org
|
|
630
|
+
// const monomer: type.HELMMonomer = org.helm.webeditor.monomers.getMonomer('HELM_AA', aar);
|
|
631
|
+
const monomer: type.HELMMonomer = this.monomerLib[aar.toLowerCase()];
|
|
632
|
+
|
|
633
|
+
if (monomer) {
|
|
634
|
+
tooltipElements.push(ui.div(monomer.n));
|
|
635
|
+
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
636
|
+
tooltipElements.push(grok.chem.svgMol(monomer.m, undefined, undefined, options));
|
|
637
|
+
} else
|
|
638
|
+
tooltipElements.push(ui.div(aar));
|
|
639
|
+
|
|
640
|
+
ui.tooltip.show(ui.divV(tooltipElements), x, y);
|
|
566
641
|
}
|
|
567
642
|
|
|
568
|
-
|
|
643
|
+
showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
|
|
569
644
|
const currentStatsDf = this.statsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
|
|
570
|
-
const activityCol = this.
|
|
645
|
+
const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
571
646
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
572
|
-
const currentPosCol = this.
|
|
647
|
+
const currentPosCol = this.df.getCol(position);
|
|
573
648
|
splitCol.init((i) => currentPosCol.get(i) == aar);
|
|
574
649
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
575
650
|
const stats: Stats = {
|
|
@@ -578,13 +653,16 @@ export class PeptidesModel {
|
|
|
578
653
|
pValue: currentStatsDf.get(C.COLUMNS_NAMES.P_VALUE, 0),
|
|
579
654
|
meanDifference: currentStatsDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, 0),
|
|
580
655
|
};
|
|
581
|
-
|
|
582
|
-
|
|
656
|
+
if (!stats.count)
|
|
657
|
+
return null;
|
|
658
|
+
|
|
659
|
+
const tooltip = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
|
|
583
660
|
|
|
584
661
|
ui.tooltip.show(tooltip, x, y);
|
|
662
|
+
|
|
663
|
+
return tooltip;
|
|
585
664
|
}
|
|
586
665
|
|
|
587
|
-
//TODO: think about it, move out?
|
|
588
666
|
setInteractionCallback(): void {
|
|
589
667
|
const sarDf = this._sarGrid.dataFrame;
|
|
590
668
|
const sarVDf = this._sarVGrid.dataFrame;
|
|
@@ -649,7 +727,7 @@ export class PeptidesModel {
|
|
|
649
727
|
}
|
|
650
728
|
|
|
651
729
|
invalidateGrids(): void {
|
|
652
|
-
this.stackedBarchart?.computeData();
|
|
730
|
+
// this.stackedBarchart?.computeData();
|
|
653
731
|
this._sarGrid.invalidate();
|
|
654
732
|
this._sarVGrid.invalidate();
|
|
655
733
|
this._sourceGrid?.invalidate();
|
|
@@ -659,12 +737,9 @@ export class PeptidesModel {
|
|
|
659
737
|
setBitsetCallback(): void {
|
|
660
738
|
if (this.isBitsetChangedInitialized)
|
|
661
739
|
return;
|
|
662
|
-
const
|
|
663
|
-
const selection = this._dataFrame.selection;
|
|
664
|
-
|
|
665
|
-
const changeBitset = (currentBitset: DG.BitSet, previousBitset: DG.BitSet): void => {
|
|
666
|
-
previousBitset.setAll(!this._filterMode, false);
|
|
740
|
+
const selection = this.df.selection;
|
|
667
741
|
|
|
742
|
+
const changeBitset = (currentBitset: DG.BitSet): void => {
|
|
668
743
|
const edfSelection = this.edf?.selection;
|
|
669
744
|
if (this.isPeptideSpaceChangingBitset) {
|
|
670
745
|
if (edfSelection == null)
|
|
@@ -690,8 +765,8 @@ export class PeptidesModel {
|
|
|
690
765
|
//TODO: move out
|
|
691
766
|
const getBitAt = (i: number) => {
|
|
692
767
|
for (const position of positionList) {
|
|
693
|
-
const positionCol: DG.Column<string> = this.
|
|
694
|
-
if (this._currentSelection[position].includes(positionCol.get(i)))
|
|
768
|
+
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
769
|
+
if (this._currentSelection[position].includes(positionCol.get(i)!))
|
|
695
770
|
return true;
|
|
696
771
|
}
|
|
697
772
|
return false;
|
|
@@ -701,29 +776,19 @@ export class PeptidesModel {
|
|
|
701
776
|
updateEdfSelection();
|
|
702
777
|
};
|
|
703
778
|
|
|
704
|
-
|
|
705
|
-
selection.onChanged.subscribe(() => changeBitset(selection, filter));
|
|
779
|
+
selection.onChanged.subscribe(() => changeBitset(selection));
|
|
706
780
|
this.isBitsetChangedInitialized = true;
|
|
707
781
|
}
|
|
708
782
|
|
|
709
783
|
fireBitsetChanged(isPeptideSpaceSource: boolean = false): void {
|
|
710
784
|
this.isPeptideSpaceChangingBitset = isPeptideSpaceSource;
|
|
711
|
-
this.
|
|
785
|
+
this.df.selection.fireChanged();
|
|
712
786
|
this.modifyOrCreateSplitCol();
|
|
713
787
|
grok.shell.o = this.createAccordion().root;
|
|
714
788
|
this.isPeptideSpaceChangingBitset = false;
|
|
715
789
|
}
|
|
716
790
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
//TODO: move out
|
|
720
|
-
postProcessGrids(sourceGrid: DG.Grid, invalidIndexes: number[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
|
|
721
|
-
sourceGrid.onCellPrepare((cell: DG.GridCell) => {
|
|
722
|
-
const currentRowIndex = cell.tableRowIndex;
|
|
723
|
-
if (currentRowIndex && invalidIndexes.includes(currentRowIndex) && !cell.isRowHeader)
|
|
724
|
-
cell.style.backColor = DG.Color.lightLightGray;
|
|
725
|
-
});
|
|
726
|
-
|
|
791
|
+
postProcessGrids(sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
|
|
727
792
|
const mdCol: DG.GridColumn = sarVGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
728
793
|
mdCol.name = 'Diff';
|
|
729
794
|
|
|
@@ -753,141 +818,54 @@ export class PeptidesModel {
|
|
|
753
818
|
}
|
|
754
819
|
|
|
755
820
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
756
|
-
const currentAAR = this.
|
|
821
|
+
const currentAAR = this.df.get(position, index) as string;
|
|
757
822
|
return currentAAR === aar ? aarLabel : C.CATEGORIES.OTHER;
|
|
758
823
|
}
|
|
759
824
|
|
|
760
825
|
modifyOrCreateSplitCol(): void {
|
|
761
|
-
const bs = this.
|
|
762
|
-
this.splitCol = this.
|
|
763
|
-
this.
|
|
826
|
+
const bs = this.df.selection;
|
|
827
|
+
this.splitCol = this.df.col(C.COLUMNS_NAMES.SPLIT_COL) ??
|
|
828
|
+
this.df.columns.addNewBool(C.COLUMNS_NAMES.SPLIT_COL);
|
|
764
829
|
this.splitCol.init((i) => bs.get(i));
|
|
765
830
|
this.splitCol.compact();
|
|
766
831
|
}
|
|
767
832
|
|
|
768
|
-
static async scaleActivity(
|
|
769
|
-
activityScaling: string, df: DG.DataFrame, originalActivityName?: string, cloneBitset = false,
|
|
770
|
-
): Promise<[DG.DataFrame, string]> {
|
|
771
|
-
let currentActivityColName = originalActivityName ?? C.COLUMNS_NAMES.ACTIVITY;
|
|
772
|
-
const flag = df.columns.names().includes(currentActivityColName) &&
|
|
773
|
-
currentActivityColName === originalActivityName;
|
|
774
|
-
currentActivityColName = flag ? currentActivityColName : C.COLUMNS_NAMES.ACTIVITY;
|
|
775
|
-
const tempDf = df.clone(cloneBitset ? df.filter : null, [currentActivityColName]);
|
|
776
|
-
|
|
777
|
-
let formula = '${' + currentActivityColName + '}';
|
|
778
|
-
let newColName = 'activity';
|
|
779
|
-
switch (activityScaling) {
|
|
780
|
-
case 'none':
|
|
781
|
-
break;
|
|
782
|
-
case 'lg':
|
|
783
|
-
formula = `Log10(${formula})`;
|
|
784
|
-
newColName = `Log10(${newColName})`;
|
|
785
|
-
break;
|
|
786
|
-
case '-lg':
|
|
787
|
-
formula = `-1*Log10(${formula})`;
|
|
788
|
-
newColName = `-Log10(${newColName})`;
|
|
789
|
-
break;
|
|
790
|
-
default:
|
|
791
|
-
throw new Error(`ScalingError: method \`${activityScaling}\` is not available.`);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
await tempDf.columns.addNewCalculated(C.COLUMNS_NAMES.ACTIVITY_SCALED, formula);
|
|
795
|
-
df.tags['scaling'] = activityScaling;
|
|
796
|
-
|
|
797
|
-
return [tempDf, newColName];
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
static splitAlignedPeptides(peptideColumn: DG.Column<string>, filter: boolean = true): [DG.DataFrame, number[]] {
|
|
801
|
-
const separator = peptideColumn.tags[C.TAGS.SEPARATOR] ?? getSeparator(peptideColumn);
|
|
802
|
-
const splitPeptidesArray: string[][] = [];
|
|
803
|
-
let currentSplitPeptide: string[];
|
|
804
|
-
let modeMonomerCount = 0;
|
|
805
|
-
let currentLength;
|
|
806
|
-
const colLength = peptideColumn.length;
|
|
807
|
-
|
|
808
|
-
// splitting data
|
|
809
|
-
const monomerLengths: {[index: string]: number} = {};
|
|
810
|
-
for (let i = 0; i < colLength; i++) {
|
|
811
|
-
currentSplitPeptide = peptideColumn.get(i).split(separator).map((value: string) => value ? value : '-');
|
|
812
|
-
splitPeptidesArray.push(currentSplitPeptide);
|
|
813
|
-
currentLength = currentSplitPeptide.length;
|
|
814
|
-
monomerLengths[currentLength + ''] =
|
|
815
|
-
monomerLengths[currentLength + ''] ? monomerLengths[currentLength + ''] + 1 : 1;
|
|
816
|
-
}
|
|
817
|
-
modeMonomerCount =
|
|
818
|
-
parseInt(Object.keys(monomerLengths).reduce((a, b) => monomerLengths[a] > monomerLengths[b] ? a : b));
|
|
819
|
-
|
|
820
|
-
// making sure all of the sequences are of the same size
|
|
821
|
-
// and marking invalid sequences
|
|
822
|
-
let nTerminal: string;
|
|
823
|
-
const invalidIndexes: number[] = [];
|
|
824
|
-
let splitColumns: string[][] = Array.from({length: modeMonomerCount}, (_) => []);
|
|
825
|
-
modeMonomerCount--; // minus N-terminal
|
|
826
|
-
for (let i = 0; i < colLength; i++) {
|
|
827
|
-
currentSplitPeptide = splitPeptidesArray[i];
|
|
828
|
-
nTerminal = currentSplitPeptide.pop()!; // it is guaranteed that there will be at least one element
|
|
829
|
-
currentLength = currentSplitPeptide.length;
|
|
830
|
-
if (currentLength !== modeMonomerCount)
|
|
831
|
-
invalidIndexes.push(i);
|
|
832
|
-
|
|
833
|
-
for (let j = 0; j < modeMonomerCount; j++)
|
|
834
|
-
splitColumns[j].push(j < currentLength ? currentSplitPeptide[j] : '-');
|
|
835
|
-
|
|
836
|
-
splitColumns[modeMonomerCount].push(nTerminal);
|
|
837
|
-
}
|
|
838
|
-
modeMonomerCount--; // minus C-terminal
|
|
839
|
-
|
|
840
|
-
//create column names list
|
|
841
|
-
const columnNames = Array.from({length: modeMonomerCount}, (_, index) => `${index + 1 < 10 ? 0 : ''}${index + 1 }`);
|
|
842
|
-
columnNames.splice(0, 0, 'N');
|
|
843
|
-
columnNames.push('C');
|
|
844
|
-
|
|
845
|
-
// filter out the columns with the same values
|
|
846
|
-
if (filter) {
|
|
847
|
-
splitColumns = splitColumns.filter((positionArray, index) => {
|
|
848
|
-
const isRetained = new Set(positionArray).size > 1;
|
|
849
|
-
if (!isRetained)
|
|
850
|
-
columnNames.splice(index, 1);
|
|
851
|
-
|
|
852
|
-
return isRetained;
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
return [
|
|
857
|
-
DG.DataFrame.fromColumns(splitColumns.map((positionArray, index) => {
|
|
858
|
-
return DG.Column.fromList('string', columnNames[index], positionArray);
|
|
859
|
-
})),
|
|
860
|
-
invalidIndexes,
|
|
861
|
-
];
|
|
862
|
-
}
|
|
863
|
-
|
|
864
833
|
syncProperties(isSourceSAR = true): void {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
834
|
+
if (this.sarViewer && this.sarViewerVertical) {
|
|
835
|
+
const [sourceViewer, targetViewer] = isSourceSAR ? [this.sarViewer, this.sarViewerVertical] :
|
|
836
|
+
[this.sarViewerVertical, this.sarViewer];
|
|
837
|
+
const properties = sourceViewer.props.getProperties();
|
|
838
|
+
const newProps: {[propName: string]: string | number | boolean} = {};
|
|
839
|
+
for (const property of properties) {
|
|
840
|
+
const propName = property.name;
|
|
841
|
+
const propVal = property.get(sourceViewer);
|
|
842
|
+
targetViewer.props.set(propName, propVal);
|
|
843
|
+
newProps[propName] = propVal;
|
|
844
|
+
}
|
|
845
|
+
this.usedProperties = newProps;
|
|
846
|
+
} else
|
|
847
|
+
console.warn('Warning: could not sync viewer properties, one of the viewers is not initialized');
|
|
872
848
|
}
|
|
873
849
|
|
|
874
850
|
/** Class initializer */
|
|
875
851
|
async init(): Promise<void> {
|
|
876
852
|
if (this.isInitialized)
|
|
877
853
|
return;
|
|
878
|
-
this.isInitialized = true;
|
|
879
854
|
|
|
880
|
-
this.
|
|
881
|
-
grok.
|
|
882
|
-
|
|
883
|
-
|
|
855
|
+
this.monomerLib =
|
|
856
|
+
JSON.parse(await grok.functions.call('Helm:getMonomerLib', {type: this.df.getTag('monomerType')}));
|
|
857
|
+
|
|
858
|
+
this.currentView = this.df.tags[C.PEPTIDES_ANALYSIS] == 'true' ? grok.shell.v as DG.TableView :
|
|
859
|
+
grok.shell.addTableView(this.df);
|
|
860
|
+
this._sourceGrid = this.currentView.grid;
|
|
861
|
+
if (this.df.tags[C.PEPTIDES_ANALYSIS] == 'true')
|
|
884
862
|
return;
|
|
885
863
|
|
|
886
|
-
this.
|
|
887
|
-
|
|
888
|
-
|
|
864
|
+
this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
865
|
+
this._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
|
|
866
|
+
this._sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
|
|
889
867
|
|
|
890
|
-
this.
|
|
868
|
+
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
891
869
|
const adjustCellSize = (grid: DG.Grid): void => {
|
|
892
870
|
const colNum = grid.columns.length;
|
|
893
871
|
for (let i = 0; i < colNum; ++i) {
|
|
@@ -897,36 +875,37 @@ export class PeptidesModel {
|
|
|
897
875
|
grid.props.rowHeight = 20;
|
|
898
876
|
};
|
|
899
877
|
|
|
900
|
-
for (let i = 0; i <
|
|
901
|
-
const aarCol =
|
|
902
|
-
if (aarCol && aarCol.name && aarCol.column?.semType !== C.SEM_TYPES.
|
|
903
|
-
aarCol.name !== this.
|
|
878
|
+
for (let i = 0; i < this._sourceGrid.columns.length; i++) {
|
|
879
|
+
const aarCol = this._sourceGrid.columns.byIndex(i);
|
|
880
|
+
if (aarCol && aarCol.name && aarCol.column?.semType !== C.SEM_TYPES.MONOMER &&
|
|
881
|
+
aarCol.name !== this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED])
|
|
904
882
|
aarCol.visible = false;
|
|
905
883
|
}
|
|
906
884
|
|
|
907
|
-
const options = {scaling: this.
|
|
908
|
-
await this.updateData(this._dataFrame.tags['scaling'], sourceGrid, false, 1, 2, false, false);
|
|
885
|
+
const options = {scaling: this.df.tags['scaling']};
|
|
909
886
|
|
|
910
887
|
const dockManager = this.currentView.dockManager;
|
|
911
888
|
|
|
912
|
-
|
|
889
|
+
this.sarViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as SARViewer;
|
|
913
890
|
|
|
914
|
-
|
|
915
|
-
await this.
|
|
891
|
+
this.sarViewerVertical =
|
|
892
|
+
await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as SARViewerVertical;
|
|
916
893
|
|
|
917
|
-
const sarViewersGroup: viewerTypes[] = [sarViewer, sarViewerVertical];
|
|
894
|
+
const sarViewersGroup: viewerTypes[] = [this.sarViewer, this.sarViewerVertical];
|
|
918
895
|
|
|
919
|
-
if (this.
|
|
896
|
+
if (this.df.rowCount <= 10000) {
|
|
920
897
|
const peptideSpaceViewerOptions = {method: 'UMAP', measure: 'Levenshtein', cyclesCount: 100};
|
|
921
898
|
const peptideSpaceViewer =
|
|
922
|
-
await this.
|
|
899
|
+
await this.df.plot.fromType('peptide-space-viewer', peptideSpaceViewerOptions) as PeptideSpaceViewer;
|
|
923
900
|
dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.RIGHT, null, 'Peptide Space Viewer');
|
|
924
901
|
}
|
|
925
902
|
|
|
903
|
+
this.updateDefault();
|
|
904
|
+
|
|
926
905
|
dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
|
|
927
906
|
|
|
928
|
-
|
|
929
|
-
adjustCellSize(
|
|
907
|
+
this._sourceGrid.props.allowEdit = false;
|
|
908
|
+
adjustCellSize(this._sourceGrid);
|
|
930
909
|
|
|
931
910
|
this.invalidateGrids();
|
|
932
911
|
}
|