@datagrok/peptides 0.4.3 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/detectors.js +1 -1
- package/package.json +15 -10
- package/src/describe.ts +216 -102
- package/src/{viewers/model.ts → model.ts} +28 -1
- package/src/package.ts +15 -23
- package/src/peptides.ts +60 -6
- package/src/styles.css +46 -0
- package/src/utils/cell-renderer.ts +126 -19
- package/src/utils/chem-palette.ts +317 -206
- package/src/utils/peptide-similarity-space.ts +21 -41
- package/src/utils/split-aligned.ts +8 -0
- package/src/viewers/logo-viewer.ts +48 -0
- package/src/viewers/sar-viewer.ts +263 -170
- package/src/viewers/stacked-barchart-viewer.ts +12 -2
- package/src/viewers/subst-viewer.ts +276 -0
- package/src/widgets/analyze-peptides.ts +26 -6
- package/src/widgets/manual-alignment.ts +14 -6
- package/src/widgets/peptide-molecule.ts +7 -0
- package/src/workers/dimensionality-reducer.ts +2 -2
- package/webpack.config.js +5 -1
- package/src/utils/correlation-analysis.ts +0 -123
|
@@ -4,13 +4,20 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
|
|
7
|
-
import {model} from '
|
|
8
|
-
|
|
7
|
+
import {model} from '../model';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Structure-activity relationship viewer.
|
|
11
|
+
*
|
|
12
|
+
* @export
|
|
13
|
+
* @class SARViewer
|
|
14
|
+
* @extends {DG.JsViewer}
|
|
15
|
+
*/
|
|
9
16
|
export class SARViewer extends DG.JsViewer {
|
|
10
17
|
protected viewerGrid: DG.Grid | null;
|
|
11
18
|
protected sourceGrid: DG.Grid | null;
|
|
12
|
-
protected
|
|
13
|
-
protected
|
|
19
|
+
protected activityColumnName: string;
|
|
20
|
+
protected scaling: string;
|
|
14
21
|
protected bidirectionalAnalysis: boolean;
|
|
15
22
|
protected filterMode: boolean;
|
|
16
23
|
protected statsDf: DG.DataFrame | null;
|
|
@@ -25,6 +32,12 @@ export class SARViewer extends DG.JsViewer {
|
|
|
25
32
|
// protected pValueThreshold: number;
|
|
26
33
|
// protected amountOfBestAARs: number;
|
|
27
34
|
// duplicatesHandingMethod: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates an instance of SARViewer.
|
|
38
|
+
*
|
|
39
|
+
* @memberof SARViewer
|
|
40
|
+
*/
|
|
28
41
|
constructor() {
|
|
29
42
|
super();
|
|
30
43
|
|
|
@@ -38,9 +51,9 @@ export class SARViewer extends DG.JsViewer {
|
|
|
38
51
|
this.viewGridInitialized = false;
|
|
39
52
|
this.currentBitset = null;
|
|
40
53
|
|
|
41
|
-
//TODO: find a way to restrict
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
54
|
+
//TODO: find a way to restrict activityColumnName to accept only numerical columns (double even better)
|
|
55
|
+
this.activityColumnName = this.string('activityColumnName');
|
|
56
|
+
this.scaling = this.string('scaling', 'none', {choices: ['none', 'lg', '-lg']});
|
|
44
57
|
this.filterMode = this.bool('filterMode', false);
|
|
45
58
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
46
59
|
this.grouping = this.bool('grouping', false);
|
|
@@ -51,8 +64,14 @@ export class SARViewer extends DG.JsViewer {
|
|
|
51
64
|
this.sourceGrid = null;
|
|
52
65
|
}
|
|
53
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Initializes SARViewer.
|
|
69
|
+
*
|
|
70
|
+
* @memberof SARViewer
|
|
71
|
+
*/
|
|
54
72
|
init() {
|
|
55
73
|
this._initialBitset = this.dataFrame!.filter.clone();
|
|
74
|
+
this.currentBitset = this._initialBitset.clone();
|
|
56
75
|
this.initialized = true;
|
|
57
76
|
this.subs.push(model.statsDf$.subscribe((data) => this.statsDf = data));
|
|
58
77
|
this.subs.push(model.viewerGrid$.subscribe((data) => {
|
|
@@ -63,16 +82,32 @@ export class SARViewer extends DG.JsViewer {
|
|
|
63
82
|
this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
|
|
64
83
|
}
|
|
65
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Function that is executed when the table is attached.
|
|
87
|
+
*
|
|
88
|
+
* @memberof SARViewer
|
|
89
|
+
*/
|
|
66
90
|
onTableAttached() {
|
|
67
91
|
this.sourceGrid = this.view.grid;
|
|
68
92
|
this.sourceGrid?.dataFrame?.setTag('dataType', 'peptides');
|
|
69
93
|
this.render();
|
|
70
94
|
}
|
|
71
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Function that is executed when the viewer is detached from the table.
|
|
98
|
+
*
|
|
99
|
+
* @memberof SARViewer
|
|
100
|
+
*/
|
|
72
101
|
detach() {
|
|
73
102
|
this.subs.forEach((sub) => sub.unsubscribe());
|
|
74
103
|
}
|
|
75
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Function that is executed when the property is changed.
|
|
107
|
+
*
|
|
108
|
+
* @param {DG.Property} property New property.
|
|
109
|
+
* @memberof SARViewer
|
|
110
|
+
*/
|
|
76
111
|
onPropertyChanged(property: DG.Property) {
|
|
77
112
|
super.onPropertyChanged(property);
|
|
78
113
|
|
|
@@ -81,14 +116,14 @@ export class SARViewer extends DG.JsViewer {
|
|
|
81
116
|
return;
|
|
82
117
|
}
|
|
83
118
|
|
|
84
|
-
if (property.name === '
|
|
119
|
+
if (property.name === 'scaling' && typeof this.dataFrame !== 'undefined') {
|
|
85
120
|
const minActivity = DG.Stats.fromColumn(
|
|
86
|
-
this.dataFrame!.col(this.
|
|
121
|
+
this.dataFrame!.col(this.activityColumnName)!,
|
|
87
122
|
this._initialBitset,
|
|
88
123
|
).min;
|
|
89
|
-
if (minActivity && minActivity <= 0 && this.
|
|
90
|
-
grok.shell.warning(`Could not apply ${this.
|
|
91
|
-
`activity column ${this.
|
|
124
|
+
if (minActivity && minActivity <= 0 && this.scaling !== 'none') {
|
|
125
|
+
grok.shell.warning(`Could not apply ${this.scaling}: ` +
|
|
126
|
+
`activity column ${this.activityColumnName} contains zero or negative values, falling back to 'none'.`);
|
|
92
127
|
property.set(this, 'none');
|
|
93
128
|
return;
|
|
94
129
|
}
|
|
@@ -97,167 +132,23 @@ export class SARViewer extends DG.JsViewer {
|
|
|
97
132
|
this.render();
|
|
98
133
|
}
|
|
99
134
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.viewerGrid.dataFrame.currentCol.name !== this.aminoAcidResidue
|
|
107
|
-
) {
|
|
108
|
-
const currentAAR: string =
|
|
109
|
-
this.viewerGrid.dataFrame.get(this.aminoAcidResidue, this.viewerGrid.dataFrame.currentRowIdx);
|
|
110
|
-
const currentPosition = this.viewerGrid.dataFrame.currentCol.name;
|
|
111
|
-
|
|
112
|
-
const splitColName = '~splitCol';
|
|
113
|
-
const otherLabel = 'Other';
|
|
114
|
-
const aarLabel = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
115
|
-
|
|
116
|
-
let splitCol = this.dataFrame.col(splitColName);
|
|
117
|
-
if (!splitCol) {
|
|
118
|
-
splitCol = this.dataFrame.columns.addNew(splitColName, 'string');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const isChosen = (i: number) => this.groupMapping![this.dataFrame!.get(currentPosition, i)] === currentAAR;
|
|
122
|
-
splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
|
|
123
|
-
|
|
124
|
-
//TODO: use column.compact
|
|
125
|
-
this.currentBitset = DG.BitSet.create(this.dataFrame.rowCount, isChosen).and(this._initialBitset!);
|
|
126
|
-
this.sourceFilteringFunc();
|
|
127
|
-
|
|
128
|
-
const colorMap: {[index: string]: string | number} = {};
|
|
129
|
-
colorMap[otherLabel] = DG.Color.blue;
|
|
130
|
-
colorMap[aarLabel] = DG.Color.orange;
|
|
131
|
-
// colorMap[currentAAR] = cp.getColor(currentAAR);
|
|
132
|
-
this.dataFrame.getCol(splitColName).colors.setCategorical(colorMap);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private sourceFilteringFunc() {
|
|
137
|
-
if (this.filterMode) {
|
|
138
|
-
this.dataFrame!.selection.setAll(false, false);
|
|
139
|
-
this.dataFrame!.filter.copyFrom(this.currentBitset!);
|
|
140
|
-
} else {
|
|
141
|
-
this.dataFrame!.filter.copyFrom(this._initialBitset!);
|
|
142
|
-
this.dataFrame!.selection.copyFrom(this.currentBitset!);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
private accordionFunc(accordion: DG.Accordion) {
|
|
147
|
-
if (accordion.context instanceof DG.RowGroup) {
|
|
148
|
-
const originalDf: DG.DataFrame = DG.toJs(accordion.context.dataFrame);
|
|
149
|
-
const viewerDf = this.viewerGrid?.dataFrame;
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
originalDf.getTag('dataType') === 'peptides' &&
|
|
153
|
-
originalDf.col('~splitCol') &&
|
|
154
|
-
this.viewerGrid &&
|
|
155
|
-
viewerDf &&
|
|
156
|
-
viewerDf.currentCol !== null
|
|
157
|
-
) {
|
|
158
|
-
const currentAAR: string = viewerDf.get(
|
|
159
|
-
this.aminoAcidResidue,
|
|
160
|
-
viewerDf.currentRowIdx,
|
|
161
|
-
);
|
|
162
|
-
const currentPosition = viewerDf.currentCol.name;
|
|
163
|
-
|
|
164
|
-
const labelStr = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
165
|
-
const currentColor = DG.Color.toHtml(DG.Color.orange);
|
|
166
|
-
const otherColor = DG.Color.toHtml(DG.Color.blue);
|
|
167
|
-
const currentLabel = ui.label(labelStr, {style: {color: currentColor}});
|
|
168
|
-
const otherLabel = ui.label('Other', {style: {color: otherColor}});
|
|
169
|
-
|
|
170
|
-
const elements: (HTMLLabelElement | HTMLElement)[] = [currentLabel, otherLabel];
|
|
171
|
-
|
|
172
|
-
const distPane = accordion.getPane('Distribution');
|
|
173
|
-
if (distPane) {
|
|
174
|
-
accordion.removePane(distPane);
|
|
175
|
-
}
|
|
176
|
-
accordion.addPane('Distribution', () => {
|
|
177
|
-
const hist = originalDf.clone(this._initialBitset).plot.histogram({
|
|
178
|
-
// const hist = originalDf.plot.histogram({
|
|
179
|
-
filteringEnabled: false,
|
|
180
|
-
valueColumnName: `${this.activityColumnColumnName}Scaled`,
|
|
181
|
-
splitColumnName: '~splitCol',
|
|
182
|
-
legendVisibility: 'Never',
|
|
183
|
-
showXAxis: true,
|
|
184
|
-
showColumnSelector: false,
|
|
185
|
-
showRangeSlider: false,
|
|
186
|
-
}).root;
|
|
187
|
-
hist.style.width = 'auto';
|
|
188
|
-
elements.push(hist);
|
|
189
|
-
|
|
190
|
-
const tableMap: {[key: string]: string} = {'Statistics:': ''};
|
|
191
|
-
for (const colName of new Set(['Count', 'pValue', 'Mean difference'])) {
|
|
192
|
-
const query = `${this.aminoAcidResidue} = ${currentAAR} and Position = ${currentPosition}`;
|
|
193
|
-
const textNum = this.statsDf?.groupBy([colName]).where(query).aggregate().get(colName, 0);
|
|
194
|
-
// const text = textNum === 0 ? '<0.01' : `${colName === 'Count' ? textNum : textNum.toFixed(2)}`;
|
|
195
|
-
const text = colName === 'Count' ? `${textNum}` : textNum < 0.01 ? '<0.01' : textNum.toFixed(2);
|
|
196
|
-
tableMap[colName === 'pValue' ? 'p-value' : colName] = text;
|
|
197
|
-
}
|
|
198
|
-
elements.push(ui.tableFromMap(tableMap));
|
|
199
|
-
|
|
200
|
-
return ui.divV(elements);
|
|
201
|
-
}, true);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
syncGridsFunc(sourceVertical: boolean) { //TODO: refactor
|
|
207
|
-
if (this.viewerGrid && this.viewerGrid.dataFrame && this.viewerVGrid && this.viewerVGrid.dataFrame) {
|
|
208
|
-
if (sourceVertical) {
|
|
209
|
-
const dfCell = this.viewerVGrid.dataFrame.currentCell;
|
|
210
|
-
if (dfCell.column === null || dfCell.column.name !== 'Mean difference') {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const otherColName: string = this.viewerVGrid.dataFrame.get('Position', dfCell.rowIndex);
|
|
214
|
-
const otherRowName: string = this.viewerVGrid.dataFrame.get(this.aminoAcidResidue, dfCell.rowIndex);
|
|
215
|
-
let otherRowIndex = -1;
|
|
216
|
-
for (let i = 0; i < this.viewerGrid.dataFrame.rowCount; i++) {
|
|
217
|
-
if (this.viewerGrid.dataFrame.get(this.aminoAcidResidue, i) === otherRowName) {
|
|
218
|
-
otherRowIndex = i;
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (otherRowIndex !== -1) {
|
|
223
|
-
this.viewerGrid.dataFrame.currentCell = this.viewerGrid.dataFrame.cell(otherRowIndex, otherColName);
|
|
224
|
-
}
|
|
225
|
-
} else {
|
|
226
|
-
const otherPos: string = this.viewerGrid.dataFrame.currentCol?.name;
|
|
227
|
-
if (typeof otherPos === 'undefined' && otherPos !== this.aminoAcidResidue) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const otherAAR: string =
|
|
231
|
-
this.viewerGrid.dataFrame.get(this.aminoAcidResidue, this.viewerGrid.dataFrame.currentRowIdx);
|
|
232
|
-
let otherRowIndex = -1;
|
|
233
|
-
for (let i = 0; i < this.viewerVGrid.dataFrame.rowCount; i++) {
|
|
234
|
-
if (
|
|
235
|
-
this.viewerVGrid.dataFrame.get(this.aminoAcidResidue, i) === otherAAR &&
|
|
236
|
-
this.viewerVGrid.dataFrame.get('Position', i) === otherPos
|
|
237
|
-
) {
|
|
238
|
-
otherRowIndex = i;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (otherRowIndex !== -1) {
|
|
243
|
-
this.viewerVGrid.dataFrame.currentCell = this.viewerVGrid.dataFrame.cell(otherRowIndex, 'Mean difference');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// argument compute data can be used to just redraw grids.
|
|
249
|
-
// Probably iirelevant since mostly grids are updating themselves, and render is only used to update data.
|
|
135
|
+
/**
|
|
136
|
+
* Viewer render function.
|
|
137
|
+
*
|
|
138
|
+
* @param {boolean} [computeData=true] Recalculate data.
|
|
139
|
+
* @memberof SARViewer
|
|
140
|
+
*/
|
|
250
141
|
async render(computeData = true) {
|
|
251
142
|
if (!this.initialized) {
|
|
252
143
|
return;
|
|
253
144
|
}
|
|
254
145
|
//TODO: optimize. Don't calculate everything again if only view changes
|
|
255
146
|
if (computeData) {
|
|
256
|
-
if (typeof this.dataFrame !== 'undefined' && this.
|
|
147
|
+
if (typeof this.dataFrame !== 'undefined' && this.activityColumnName && this.sourceGrid) {
|
|
257
148
|
await model?.updateData(
|
|
258
149
|
this.dataFrame!,
|
|
259
|
-
this.
|
|
260
|
-
this.
|
|
150
|
+
this.activityColumnName,
|
|
151
|
+
this.scaling,
|
|
261
152
|
this.sourceGrid,
|
|
262
153
|
this.bidirectionalAnalysis,
|
|
263
154
|
this._initialBitset,
|
|
@@ -268,12 +159,24 @@ export class SARViewer extends DG.JsViewer {
|
|
|
268
159
|
$(this.root).empty();
|
|
269
160
|
this.root.appendChild(this.viewerGrid.root);
|
|
270
161
|
this.viewerGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
|
|
271
|
-
this.applyBitset(
|
|
272
|
-
|
|
162
|
+
this.currentBitset = applyBitset(
|
|
163
|
+
this.dataFrame!, this.viewerGrid!, this.aminoAcidResidue,
|
|
164
|
+
this.groupMapping!, this._initialBitset!, this.filterMode,
|
|
165
|
+
) ?? this.currentBitset;
|
|
166
|
+
syncGridsFunc(false, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
|
|
167
|
+
});
|
|
168
|
+
this.viewerVGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
|
|
169
|
+
syncGridsFunc(true, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
|
|
170
|
+
});
|
|
171
|
+
this.dataFrame!.onRowsFiltering.subscribe((_) => {
|
|
172
|
+
sourceFilteringFunc(this.filterMode, this.dataFrame!, this.currentBitset!, this._initialBitset!);
|
|
173
|
+
});
|
|
174
|
+
grok.events.onAccordionConstructed.subscribe((accordion: DG.Accordion) => {
|
|
175
|
+
accordionFunc(
|
|
176
|
+
accordion, this.viewerGrid!, this.aminoAcidResidue,
|
|
177
|
+
this._initialBitset!, this.activityColumnName, this.statsDf!,
|
|
178
|
+
);
|
|
273
179
|
});
|
|
274
|
-
this.viewerVGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => this.syncGridsFunc(true));
|
|
275
|
-
this.dataFrame!.onRowsFiltering.subscribe((_) => this.sourceFilteringFunc());
|
|
276
|
-
grok.events.onAccordionConstructed.subscribe((accordion: DG.Accordion) => this.accordionFunc(accordion));
|
|
277
180
|
}
|
|
278
181
|
}
|
|
279
182
|
}
|
|
@@ -282,8 +185,21 @@ export class SARViewer extends DG.JsViewer {
|
|
|
282
185
|
}
|
|
283
186
|
}
|
|
284
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Vertical structure activity relationship viewer.
|
|
190
|
+
*
|
|
191
|
+
* @export
|
|
192
|
+
* @class SARViewerVertical
|
|
193
|
+
* @extends {DG.JsViewer}
|
|
194
|
+
*/
|
|
285
195
|
export class SARViewerVertical extends DG.JsViewer {
|
|
286
196
|
viewerVGrid: DG.Grid | null;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Creates an instance of SARViewerVertical.
|
|
200
|
+
*
|
|
201
|
+
* @memberof SARViewerVertical
|
|
202
|
+
*/
|
|
287
203
|
constructor() {
|
|
288
204
|
super();
|
|
289
205
|
|
|
@@ -294,6 +210,11 @@ export class SARViewerVertical extends DG.JsViewer {
|
|
|
294
210
|
}));
|
|
295
211
|
}
|
|
296
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Viewer render function.
|
|
215
|
+
*
|
|
216
|
+
* @memberof SARViewerVertical
|
|
217
|
+
*/
|
|
297
218
|
render() {
|
|
298
219
|
if (this.viewerVGrid) {
|
|
299
220
|
$(this.root).empty();
|
|
@@ -302,3 +223,175 @@ export class SARViewerVertical extends DG.JsViewer {
|
|
|
302
223
|
this.viewerVGrid?.invalidate();
|
|
303
224
|
}
|
|
304
225
|
}
|
|
226
|
+
|
|
227
|
+
function syncGridsFunc(
|
|
228
|
+
sourceVertical: boolean,
|
|
229
|
+
viewerGrid: DG.Grid,
|
|
230
|
+
viewerVGrid: DG.Grid,
|
|
231
|
+
aminoAcidResidue: string,
|
|
232
|
+
) { //TODO: refactor, move
|
|
233
|
+
if (viewerGrid && viewerGrid.dataFrame && viewerVGrid && viewerVGrid.dataFrame) {
|
|
234
|
+
if (sourceVertical) {
|
|
235
|
+
const dfCell = viewerVGrid.dataFrame.currentCell;
|
|
236
|
+
if (dfCell.column === null || dfCell.column.name !== 'Mean difference') {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const otherColName: string = viewerVGrid.dataFrame.get('Position', dfCell.rowIndex);
|
|
240
|
+
const otherRowName: string = viewerVGrid.dataFrame.get(aminoAcidResidue, dfCell.rowIndex);
|
|
241
|
+
let otherRowIndex = -1;
|
|
242
|
+
for (let i = 0; i < viewerGrid.dataFrame.rowCount; i++) {
|
|
243
|
+
if (viewerGrid.dataFrame.get(aminoAcidResidue, i) === otherRowName) {
|
|
244
|
+
otherRowIndex = i;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (otherRowIndex !== -1) {
|
|
249
|
+
viewerGrid.dataFrame.currentCell = viewerGrid.dataFrame.cell(otherRowIndex, otherColName);
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
const otherPos: string = viewerGrid.dataFrame.currentCol?.name;
|
|
253
|
+
if (typeof otherPos === 'undefined' && otherPos !== aminoAcidResidue) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const otherAAR: string =
|
|
257
|
+
viewerGrid.dataFrame.get(aminoAcidResidue, viewerGrid.dataFrame.currentRowIdx);
|
|
258
|
+
let otherRowIndex = -1;
|
|
259
|
+
for (let i = 0; i < viewerVGrid.dataFrame.rowCount; i++) {
|
|
260
|
+
if (
|
|
261
|
+
viewerVGrid.dataFrame.get(aminoAcidResidue, i) === otherAAR &&
|
|
262
|
+
viewerVGrid.dataFrame.get('Position', i) === otherPos
|
|
263
|
+
) {
|
|
264
|
+
otherRowIndex = i;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (otherRowIndex !== -1) {
|
|
269
|
+
viewerVGrid.dataFrame.currentCell = viewerVGrid.dataFrame.cell(otherRowIndex, 'Mean difference');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function sourceFilteringFunc(
|
|
276
|
+
filterMode: boolean,
|
|
277
|
+
dataFrame: DG.DataFrame,
|
|
278
|
+
currentBitset: DG.BitSet,
|
|
279
|
+
initialBitset: DG.BitSet,
|
|
280
|
+
) {
|
|
281
|
+
if (filterMode) {
|
|
282
|
+
dataFrame.selection.setAll(false, false);
|
|
283
|
+
dataFrame.filter.copyFrom(currentBitset);
|
|
284
|
+
} else {
|
|
285
|
+
dataFrame.filter.copyFrom(initialBitset);
|
|
286
|
+
dataFrame.selection.copyFrom(currentBitset);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function applyBitset(
|
|
291
|
+
dataFrame: DG.DataFrame,
|
|
292
|
+
viewerGrid: DG.Grid,
|
|
293
|
+
aminoAcidResidue: string,
|
|
294
|
+
groupMapping: {[key: string]: string},
|
|
295
|
+
initialBitset: DG.BitSet,
|
|
296
|
+
filterMode: boolean,
|
|
297
|
+
) {
|
|
298
|
+
let currentBitset = null;
|
|
299
|
+
if (
|
|
300
|
+
viewerGrid.dataFrame &&
|
|
301
|
+
viewerGrid.dataFrame.currentCell.value &&
|
|
302
|
+
viewerGrid.dataFrame.currentCol.name !== aminoAcidResidue
|
|
303
|
+
) {
|
|
304
|
+
const currentAAR: string =
|
|
305
|
+
viewerGrid.dataFrame.get(aminoAcidResidue, viewerGrid.dataFrame.currentRowIdx);
|
|
306
|
+
const currentPosition = viewerGrid.dataFrame.currentCol.name;
|
|
307
|
+
|
|
308
|
+
const splitColName = '~splitCol';
|
|
309
|
+
const otherLabel = 'Other';
|
|
310
|
+
const aarLabel = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
311
|
+
|
|
312
|
+
let splitCol = dataFrame.col(splitColName);
|
|
313
|
+
if (!splitCol) {
|
|
314
|
+
splitCol = dataFrame.columns.addNew(splitColName, 'string');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const isChosen = (i: number) => groupMapping[dataFrame!.get(currentPosition, i)] === currentAAR;
|
|
318
|
+
splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
|
|
319
|
+
|
|
320
|
+
//TODO: use column.compact
|
|
321
|
+
currentBitset = DG.BitSet.create(dataFrame.rowCount, isChosen).and(initialBitset);
|
|
322
|
+
sourceFilteringFunc(filterMode, dataFrame, currentBitset, initialBitset);
|
|
323
|
+
|
|
324
|
+
const colorMap: {[index: string]: string | number} = {};
|
|
325
|
+
colorMap[otherLabel] = DG.Color.blue;
|
|
326
|
+
colorMap[aarLabel] = DG.Color.orange;
|
|
327
|
+
// colorMap[currentAAR] = cp.getColor(currentAAR);
|
|
328
|
+
dataFrame.getCol(splitColName).colors.setCategorical(colorMap);
|
|
329
|
+
}
|
|
330
|
+
return currentBitset;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function accordionFunc(
|
|
334
|
+
accordion: DG.Accordion,
|
|
335
|
+
viewerGrid: DG.Grid,
|
|
336
|
+
aminoAcidResidue: string,
|
|
337
|
+
initialBitset: DG.BitSet,
|
|
338
|
+
activityColumnName: string,
|
|
339
|
+
statsDf: DG.DataFrame,
|
|
340
|
+
) {
|
|
341
|
+
if (accordion.context instanceof DG.RowGroup) {
|
|
342
|
+
const originalDf: DG.DataFrame = DG.toJs(accordion.context.dataFrame);
|
|
343
|
+
const viewerDf = viewerGrid.dataFrame;
|
|
344
|
+
|
|
345
|
+
if (
|
|
346
|
+
originalDf.getTag('dataType') === 'peptides' &&
|
|
347
|
+
originalDf.col('~splitCol') &&
|
|
348
|
+
viewerDf &&
|
|
349
|
+
viewerDf.currentCol !== null
|
|
350
|
+
) {
|
|
351
|
+
const currentAAR: string = viewerDf.get(
|
|
352
|
+
aminoAcidResidue,
|
|
353
|
+
viewerDf.currentRowIdx,
|
|
354
|
+
);
|
|
355
|
+
const currentPosition = viewerDf.currentCol.name;
|
|
356
|
+
|
|
357
|
+
const labelStr = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
358
|
+
const currentColor = DG.Color.toHtml(DG.Color.orange);
|
|
359
|
+
const otherColor = DG.Color.toHtml(DG.Color.blue);
|
|
360
|
+
const currentLabel = ui.label(labelStr, {style: {color: currentColor}});
|
|
361
|
+
const otherLabel = ui.label('Other', {style: {color: otherColor}});
|
|
362
|
+
|
|
363
|
+
const elements: (HTMLLabelElement | HTMLElement)[] = [currentLabel, otherLabel];
|
|
364
|
+
|
|
365
|
+
const distPane = accordion.getPane('Distribution');
|
|
366
|
+
if (distPane) {
|
|
367
|
+
accordion.removePane(distPane);
|
|
368
|
+
}
|
|
369
|
+
accordion.addPane('Distribution', () => {
|
|
370
|
+
const hist = originalDf.clone(initialBitset).plot.histogram({
|
|
371
|
+
// const hist = originalDf.plot.histogram({
|
|
372
|
+
filteringEnabled: false,
|
|
373
|
+
valueColumnName: `${activityColumnName}Scaled`,
|
|
374
|
+
splitColumnName: '~splitCol',
|
|
375
|
+
legendVisibility: 'Never',
|
|
376
|
+
showXAxis: true,
|
|
377
|
+
showColumnSelector: false,
|
|
378
|
+
showRangeSlider: false,
|
|
379
|
+
}).root;
|
|
380
|
+
hist.style.width = 'auto';
|
|
381
|
+
elements.push(hist);
|
|
382
|
+
|
|
383
|
+
const tableMap: {[key: string]: string} = {'Statistics:': ''};
|
|
384
|
+
for (const colName of new Set(['Count', 'pValue', 'Mean difference'])) {
|
|
385
|
+
const query = `${aminoAcidResidue} = ${currentAAR} and Position = ${currentPosition}`;
|
|
386
|
+
const textNum = statsDf.groupBy([colName]).where(query).aggregate().get(colName, 0);
|
|
387
|
+
// const text = textNum === 0 ? '<0.01' : `${colName === 'Count' ? textNum : textNum.toFixed(2)}`;
|
|
388
|
+
const text = colName === 'Count' ? `${textNum}` : textNum < 0.01 ? '<0.01' : textNum.toFixed(2);
|
|
389
|
+
tableMap[colName === 'pValue' ? 'p-value' : colName] = text;
|
|
390
|
+
}
|
|
391
|
+
elements.push(ui.tableFromMap(tableMap));
|
|
392
|
+
|
|
393
|
+
return ui.divV(elements);
|
|
394
|
+
}, true);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
@@ -42,7 +42,9 @@ export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
|
42
42
|
return true;
|
|
43
43
|
} else {
|
|
44
44
|
if (barchart.highlighted) {
|
|
45
|
-
|
|
45
|
+
let elements: HTMLElement[] = [];
|
|
46
|
+
elements = elements.concat([ui.divText(barchart.highlighted.aaName)]);
|
|
47
|
+
ui.tooltip.show(ui.divV(elements), x, y);
|
|
46
48
|
}
|
|
47
49
|
return true;
|
|
48
50
|
}
|
|
@@ -54,6 +56,7 @@ export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
|
54
56
|
args.g.beginPath();
|
|
55
57
|
args.g.rect(args.bounds.x, args.bounds.y, args.bounds.width, args.bounds.height);
|
|
56
58
|
args.g.clip();
|
|
59
|
+
|
|
57
60
|
if (args.cell.isColHeader && barchart.aminoColumnNames.includes(args.cell.gridColumn.name)) {
|
|
58
61
|
barchart.renderBarToCanvas(
|
|
59
62
|
args.g,
|
|
@@ -251,7 +254,14 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
251
254
|
this.max = df.filter.trueCount;
|
|
252
255
|
}
|
|
253
256
|
|
|
254
|
-
renderBarToCanvas(
|
|
257
|
+
renderBarToCanvas(
|
|
258
|
+
g: CanvasRenderingContext2D,
|
|
259
|
+
cell: DG.GridCell,
|
|
260
|
+
x: number,
|
|
261
|
+
y: number,
|
|
262
|
+
w: number,
|
|
263
|
+
h: number,
|
|
264
|
+
) {
|
|
255
265
|
const margin = 0.2;
|
|
256
266
|
const innerMargin = 0.02;
|
|
257
267
|
const selectLineRatio = 0.1;
|