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