@datagrok/peptides 0.4.2 → 0.6.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 +5 -3
- package/src/describe.ts +36 -40
- package/src/package.ts +27 -44
- package/src/peptides.ts +70 -6
- package/src/styles.css +37 -0
- package/src/utils/cell-renderer.ts +126 -19
- package/src/utils/chem-palette.ts +317 -214
- package/src/utils/correlation-analysis.ts +149 -71
- package/src/utils/peptide-similarity-space.ts +23 -19
- package/src/utils/split-aligned.ts +8 -1
- package/src/viewers/logo-viewer.ts +48 -5
- package/src/viewers/model.ts +27 -0
- package/src/viewers/sar-viewer.ts +99 -38
- package/src/viewers/spiral-plot.ts +97 -0
- package/src/viewers/stacked-barchart-viewer.ts +82 -7
- package/src/viewers/subst-viewer.ts +276 -0
- package/src/widgets/analyze-peptides.ts +14 -4
- package/src/widgets/manual-alignment.ts +11 -4
- package/src/widgets/peptide-molecule.ts +7 -0
- package/webpack.config.js +4 -0
|
@@ -6,11 +6,18 @@ import $ from 'cash-dom';
|
|
|
6
6
|
|
|
7
7
|
import {model} from './model';
|
|
8
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;
|
|
@@ -22,10 +29,15 @@ export class SARViewer extends DG.JsViewer {
|
|
|
22
29
|
protected currentBitset: DG.BitSet | null;
|
|
23
30
|
grouping: boolean;
|
|
24
31
|
groupMapping: {[key: string]: string} | null;
|
|
25
|
-
// private df: DG.DataFrame | null;
|
|
26
32
|
// protected pValueThreshold: number;
|
|
27
33
|
// protected amountOfBestAARs: number;
|
|
28
34
|
// duplicatesHandingMethod: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates an instance of SARViewer.
|
|
38
|
+
*
|
|
39
|
+
* @memberof SARViewer
|
|
40
|
+
*/
|
|
29
41
|
constructor() {
|
|
30
42
|
super();
|
|
31
43
|
|
|
@@ -39,9 +51,9 @@ export class SARViewer extends DG.JsViewer {
|
|
|
39
51
|
this.viewGridInitialized = false;
|
|
40
52
|
this.currentBitset = null;
|
|
41
53
|
|
|
42
|
-
//TODO: find a way to restrict
|
|
43
|
-
this.
|
|
44
|
-
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']});
|
|
45
57
|
this.filterMode = this.bool('filterMode', false);
|
|
46
58
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
47
59
|
this.grouping = this.bool('grouping', false);
|
|
@@ -52,6 +64,11 @@ export class SARViewer extends DG.JsViewer {
|
|
|
52
64
|
this.sourceGrid = null;
|
|
53
65
|
}
|
|
54
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Initializes SARViewer.
|
|
69
|
+
*
|
|
70
|
+
* @memberof SARViewer
|
|
71
|
+
*/
|
|
55
72
|
init() {
|
|
56
73
|
this._initialBitset = this.dataFrame!.filter.clone();
|
|
57
74
|
this.initialized = true;
|
|
@@ -64,16 +81,32 @@ export class SARViewer extends DG.JsViewer {
|
|
|
64
81
|
this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
|
|
65
82
|
}
|
|
66
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Function that is executed when the table is attached.
|
|
86
|
+
*
|
|
87
|
+
* @memberof SARViewer
|
|
88
|
+
*/
|
|
67
89
|
onTableAttached() {
|
|
68
90
|
this.sourceGrid = this.view.grid;
|
|
69
91
|
this.sourceGrid?.dataFrame?.setTag('dataType', 'peptides');
|
|
70
92
|
this.render();
|
|
71
93
|
}
|
|
72
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Function that is executed when the viewer is detached from the table.
|
|
97
|
+
*
|
|
98
|
+
* @memberof SARViewer
|
|
99
|
+
*/
|
|
73
100
|
detach() {
|
|
74
101
|
this.subs.forEach((sub) => sub.unsubscribe());
|
|
75
102
|
}
|
|
76
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Function that is executed when the property is changed.
|
|
106
|
+
*
|
|
107
|
+
* @param {DG.Property} property New property.
|
|
108
|
+
* @memberof SARViewer
|
|
109
|
+
*/
|
|
77
110
|
onPropertyChanged(property: DG.Property) {
|
|
78
111
|
super.onPropertyChanged(property);
|
|
79
112
|
|
|
@@ -82,14 +115,14 @@ export class SARViewer extends DG.JsViewer {
|
|
|
82
115
|
return;
|
|
83
116
|
}
|
|
84
117
|
|
|
85
|
-
if (property.name === '
|
|
118
|
+
if (property.name === 'scaling' && typeof this.dataFrame !== 'undefined') {
|
|
86
119
|
const minActivity = DG.Stats.fromColumn(
|
|
87
|
-
this.dataFrame!.col(this.
|
|
120
|
+
this.dataFrame!.col(this.activityColumnName)!,
|
|
88
121
|
this._initialBitset,
|
|
89
122
|
).min;
|
|
90
|
-
if (minActivity && minActivity <= 0 && this.
|
|
91
|
-
grok.shell.warning(`Could not apply ${this.
|
|
92
|
-
`activity column ${this.
|
|
123
|
+
if (minActivity && minActivity <= 0 && this.scaling !== 'none') {
|
|
124
|
+
grok.shell.warning(`Could not apply ${this.scaling}: ` +
|
|
125
|
+
`activity column ${this.activityColumnName} contains zero or negative values, falling back to 'none'.`);
|
|
93
126
|
property.set(this, 'none');
|
|
94
127
|
return;
|
|
95
128
|
}
|
|
@@ -98,6 +131,12 @@ export class SARViewer extends DG.JsViewer {
|
|
|
98
131
|
this.render();
|
|
99
132
|
}
|
|
100
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Filtering/selecting sequences with the specified amino acid residue at specified position.
|
|
136
|
+
*
|
|
137
|
+
* @private
|
|
138
|
+
* @memberof SARViewer
|
|
139
|
+
*/
|
|
101
140
|
private applyBitset() {
|
|
102
141
|
if (
|
|
103
142
|
this.dataFrame &&
|
|
@@ -123,21 +162,9 @@ export class SARViewer extends DG.JsViewer {
|
|
|
123
162
|
splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
|
|
124
163
|
|
|
125
164
|
//TODO: use column.compact
|
|
126
|
-
|
|
127
|
-
// if (this.filterMode) {
|
|
128
|
-
// this.dataFrame.selection.setAll(false, false);
|
|
129
|
-
// this.dataFrame.filter.init(isChosen).and(this._initialBitset!, false);
|
|
130
|
-
// } else {
|
|
131
|
-
// this.dataFrame.filter.copyFrom(this._initialBitset!);
|
|
132
|
-
// this.dataFrame.selection.init(isChosen).and(this._initialBitset!, false);
|
|
133
|
-
// }
|
|
134
165
|
this.currentBitset = DG.BitSet.create(this.dataFrame.rowCount, isChosen).and(this._initialBitset!);
|
|
135
|
-
// (this.filterMode ? this.dataFrame.selection.setAll(false) :
|
|
136
|
-
// this.dataFrame.filter.copyFrom(this._initialBitset!)).fireChanged();
|
|
137
166
|
this.sourceFilteringFunc();
|
|
138
167
|
|
|
139
|
-
|
|
140
|
-
// df.getCol(splitColName).setCategoryOrder([otherLabel, aarLabel]);
|
|
141
168
|
const colorMap: {[index: string]: string | number} = {};
|
|
142
169
|
colorMap[otherLabel] = DG.Color.blue;
|
|
143
170
|
colorMap[aarLabel] = DG.Color.orange;
|
|
@@ -146,6 +173,12 @@ export class SARViewer extends DG.JsViewer {
|
|
|
146
173
|
}
|
|
147
174
|
}
|
|
148
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Selecting/filtering sequences according to the current bitset.
|
|
178
|
+
*
|
|
179
|
+
* @private
|
|
180
|
+
* @memberof SARViewer
|
|
181
|
+
*/
|
|
149
182
|
private sourceFilteringFunc() {
|
|
150
183
|
if (this.filterMode) {
|
|
151
184
|
this.dataFrame!.selection.setAll(false, false);
|
|
@@ -156,6 +189,13 @@ export class SARViewer extends DG.JsViewer {
|
|
|
156
189
|
}
|
|
157
190
|
}
|
|
158
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Property panel accordion construction function.
|
|
194
|
+
*
|
|
195
|
+
* @private
|
|
196
|
+
* @param {DG.Accordion} accordion Accordion.
|
|
197
|
+
* @memberof SARViewer
|
|
198
|
+
*/
|
|
159
199
|
private accordionFunc(accordion: DG.Accordion) {
|
|
160
200
|
if (accordion.context instanceof DG.RowGroup) {
|
|
161
201
|
const originalDf: DG.DataFrame = DG.toJs(accordion.context.dataFrame);
|
|
@@ -190,7 +230,7 @@ export class SARViewer extends DG.JsViewer {
|
|
|
190
230
|
const hist = originalDf.clone(this._initialBitset).plot.histogram({
|
|
191
231
|
// const hist = originalDf.plot.histogram({
|
|
192
232
|
filteringEnabled: false,
|
|
193
|
-
valueColumnName: `${this.
|
|
233
|
+
valueColumnName: `${this.activityColumnName}Scaled`,
|
|
194
234
|
splitColumnName: '~splitCol',
|
|
195
235
|
legendVisibility: 'Never',
|
|
196
236
|
showXAxis: true,
|
|
@@ -216,7 +256,13 @@ export class SARViewer extends DG.JsViewer {
|
|
|
216
256
|
}
|
|
217
257
|
}
|
|
218
258
|
|
|
219
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Function for viewer girds synchronization.
|
|
261
|
+
*
|
|
262
|
+
* @param {boolean} sourceVertical Event source is vertical viewer.
|
|
263
|
+
* @memberof SARViewer
|
|
264
|
+
*/
|
|
265
|
+
syncGridsFunc(sourceVertical: boolean) { //TODO: refactor, move
|
|
220
266
|
if (this.viewerGrid && this.viewerGrid.dataFrame && this.viewerVGrid && this.viewerVGrid.dataFrame) {
|
|
221
267
|
if (sourceVertical) {
|
|
222
268
|
const dfCell = this.viewerVGrid.dataFrame.currentCell;
|
|
@@ -258,27 +304,24 @@ export class SARViewer extends DG.JsViewer {
|
|
|
258
304
|
}
|
|
259
305
|
}
|
|
260
306
|
}
|
|
261
|
-
|
|
262
|
-
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Viewer render function.
|
|
310
|
+
*
|
|
311
|
+
* @param {boolean} [computeData=true] Recalculate data.
|
|
312
|
+
* @memberof SARViewer
|
|
313
|
+
*/
|
|
263
314
|
async render(computeData = true) {
|
|
264
315
|
if (!this.initialized) {
|
|
265
316
|
return;
|
|
266
317
|
}
|
|
267
318
|
//TODO: optimize. Don't calculate everything again if only view changes
|
|
268
319
|
if (computeData) {
|
|
269
|
-
if (typeof this.dataFrame !== 'undefined' && this.
|
|
270
|
-
// [this.viewerGrid, this.viewerVGrid, this.statsDf] = await describe(
|
|
271
|
-
// this.dataFrame,
|
|
272
|
-
// this.activityColumnColumnName,
|
|
273
|
-
// this.activityScalingMethod,
|
|
274
|
-
// this.sourceGrid,
|
|
275
|
-
// this.bidirectionalAnalysis,
|
|
276
|
-
// this._initialBitset,
|
|
277
|
-
// );
|
|
320
|
+
if (typeof this.dataFrame !== 'undefined' && this.activityColumnName && this.sourceGrid) {
|
|
278
321
|
await model?.updateData(
|
|
279
322
|
this.dataFrame!,
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
323
|
+
this.activityColumnName,
|
|
324
|
+
this.scaling,
|
|
282
325
|
this.sourceGrid,
|
|
283
326
|
this.bidirectionalAnalysis,
|
|
284
327
|
this._initialBitset,
|
|
@@ -303,8 +346,21 @@ export class SARViewer extends DG.JsViewer {
|
|
|
303
346
|
}
|
|
304
347
|
}
|
|
305
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Vertical structure activity relationship viewer.
|
|
351
|
+
*
|
|
352
|
+
* @export
|
|
353
|
+
* @class SARViewerVertical
|
|
354
|
+
* @extends {DG.JsViewer}
|
|
355
|
+
*/
|
|
306
356
|
export class SARViewerVertical extends DG.JsViewer {
|
|
307
357
|
viewerVGrid: DG.Grid | null;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Creates an instance of SARViewerVertical.
|
|
361
|
+
*
|
|
362
|
+
* @memberof SARViewerVertical
|
|
363
|
+
*/
|
|
308
364
|
constructor() {
|
|
309
365
|
super();
|
|
310
366
|
|
|
@@ -315,6 +371,11 @@ export class SARViewerVertical extends DG.JsViewer {
|
|
|
315
371
|
}));
|
|
316
372
|
}
|
|
317
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Viewer render function.
|
|
376
|
+
*
|
|
377
|
+
* @memberof SARViewerVertical
|
|
378
|
+
*/
|
|
318
379
|
render() {
|
|
319
380
|
if (this.viewerVGrid) {
|
|
320
381
|
$(this.root).empty();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
import {_toJson} from 'datagrok-api/src/utils';
|
|
3
|
+
|
|
4
|
+
import {assert, argSort} from '@datagrok-libraries/utils/src/operations';
|
|
5
|
+
import {Options} from '@datagrok-libraries/utils/src/type-declarations';
|
|
6
|
+
|
|
7
|
+
const api = <any>window;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Draws 2D scatter plot from 1D series.
|
|
11
|
+
*
|
|
12
|
+
* @export
|
|
13
|
+
* @class SpiralPlot
|
|
14
|
+
* @extends {DG.ScatterPlotViewer}
|
|
15
|
+
*/
|
|
16
|
+
export class SpiralPlot extends DG.ScatterPlotViewer {
|
|
17
|
+
static axesNames = ['~X', '~Y'];
|
|
18
|
+
static valuesKey = 'valuesColumnName';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Calculates coordinates of the projection into a spiral.
|
|
22
|
+
*
|
|
23
|
+
* @static
|
|
24
|
+
* @param {DG.DataFrame} t Source data frame.
|
|
25
|
+
* @param {Options} options Options to read values column name from. Must include {valuesColumnName: string}.
|
|
26
|
+
* @return {DG.DataFrame} Updated dataframe.
|
|
27
|
+
* @memberof SpiralPlot
|
|
28
|
+
*/
|
|
29
|
+
static updateCoordinates(t: DG.DataFrame, options: Options): DG.DataFrame {
|
|
30
|
+
assert(options[SpiralPlot.valuesKey] != undefined);
|
|
31
|
+
|
|
32
|
+
const values = t.getCol(options[SpiralPlot.valuesKey]).getRawData() as Float32Array;
|
|
33
|
+
const columns = _calcSpiralProjection(values);
|
|
34
|
+
const cdf = DG.DataFrame.fromColumns(
|
|
35
|
+
Array.from(columns).map((v, i) => DG.Column.fromFloat32Array(SpiralPlot.axesNames[i], v)),
|
|
36
|
+
);
|
|
37
|
+
return _updateCoordinates(t, cdf);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates new SpiralPlot from a data frame with selected values column.
|
|
42
|
+
*
|
|
43
|
+
* @static
|
|
44
|
+
* @param {DG.DataFrame} t A data frame.
|
|
45
|
+
* @param {Options} options Controlling options.
|
|
46
|
+
* @return {SpiralPlot} The plot.
|
|
47
|
+
* @memberof SpiralPlot
|
|
48
|
+
*/
|
|
49
|
+
static fromTable(t: DG.DataFrame, options: Options): SpiralPlot {
|
|
50
|
+
t = SpiralPlot.updateCoordinates(t, options);
|
|
51
|
+
[options.x, options.y] = SpiralPlot.axesNames;
|
|
52
|
+
options.color = options[SpiralPlot.valuesKey];
|
|
53
|
+
return new SpiralPlot(api.grok_Viewer_ScatterPlot(t.dart, _toJson(options)));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Calculates 2D projection of 1D series as a spiral.
|
|
59
|
+
*
|
|
60
|
+
* @param {(number[] | Float32Array)} values The series.
|
|
61
|
+
* @return {[Float32Array, Float32Array]} X and Y componenets of the projection.
|
|
62
|
+
*/
|
|
63
|
+
function _calcSpiralProjection(values: number[] | Float32Array): [Float32Array, Float32Array] {
|
|
64
|
+
const nItems = values.length;
|
|
65
|
+
const order = argSort(Array.from(values), true);
|
|
66
|
+
const maxV = values[order[0]];
|
|
67
|
+
const X = new Float32Array(nItems).fill(0);
|
|
68
|
+
const Y = new Float32Array(nItems).fill(0);
|
|
69
|
+
|
|
70
|
+
for (const i of order) {
|
|
71
|
+
const v = maxV - values[i];
|
|
72
|
+
X[i] = v * Math.cos(Math.PI * v) - Math.random() * 1.5 + 0.75;
|
|
73
|
+
Y[i] = v * Math.sin(Math.PI * v) - Math.random() * 1.5 + 0.75;
|
|
74
|
+
}
|
|
75
|
+
return [X, Y];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Adds new columns from one data frame into another one.
|
|
80
|
+
*
|
|
81
|
+
* @param {DG.DataFrame} table Destination data frame.
|
|
82
|
+
* @param {DG.DataFrame} coords Source data frame.
|
|
83
|
+
* @return {DG.DataFrame} Updated data frame.
|
|
84
|
+
*/
|
|
85
|
+
function _updateCoordinates(table: DG.DataFrame, coords: DG.DataFrame): DG.DataFrame {
|
|
86
|
+
const coordsColNames: string[] = coords.columns.names();
|
|
87
|
+
const tableColNames: string[] = table.columns.names();
|
|
88
|
+
const restColNames = tableColNames.filter((v: string) => !coordsColNames.includes(v));
|
|
89
|
+
|
|
90
|
+
if (tableColNames.length == restColNames.length) {
|
|
91
|
+
for (const col of coords.columns) {
|
|
92
|
+
table.columns.add(col);
|
|
93
|
+
}
|
|
94
|
+
return table;
|
|
95
|
+
}
|
|
96
|
+
return table.join(coords, coordsColNames, coordsColNames, restColNames, [], 'right', false);
|
|
97
|
+
}
|
|
@@ -5,6 +5,8 @@ import {ChemPalette} from '../utils/chem-palette';
|
|
|
5
5
|
import * as rxjs from 'rxjs';
|
|
6
6
|
const cp = new ChemPalette('grok');
|
|
7
7
|
|
|
8
|
+
import {CorrelationAnalysisVisualizer} from '../utils/correlation-analysis';
|
|
9
|
+
|
|
8
10
|
//TODO: the function should not accept promise. Await the parameters where it is used
|
|
9
11
|
export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
10
12
|
viewer.then((viewer) => {
|
|
@@ -42,7 +44,10 @@ export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
|
42
44
|
return true;
|
|
43
45
|
} else {
|
|
44
46
|
if (barchart.highlighted) {
|
|
45
|
-
|
|
47
|
+
let elements: HTMLElement[] = [];
|
|
48
|
+
elements = elements.concat([ui.divText(barchart.highlighted.aaName)]);
|
|
49
|
+
elements = elements.concat(barchart.getTooltipElements(cell.tableColumn.name, barchart.aminoColumnNames));
|
|
50
|
+
ui.tooltip.show(ui.divV(elements), x, y);
|
|
46
51
|
}
|
|
47
52
|
return true;
|
|
48
53
|
}
|
|
@@ -54,6 +59,7 @@ export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
|
54
59
|
args.g.beginPath();
|
|
55
60
|
args.g.rect(args.bounds.x, args.bounds.y, args.bounds.width, args.bounds.height);
|
|
56
61
|
args.g.clip();
|
|
62
|
+
|
|
57
63
|
if (args.cell.isColHeader && barchart.aminoColumnNames.includes(args.cell.gridColumn.name)) {
|
|
58
64
|
barchart.renderBarToCanvas(
|
|
59
65
|
args.g,
|
|
@@ -95,11 +101,13 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
95
101
|
private barStats: {[Key: string]: {'name': string, 'count': number, 'selectedCount': number}[]} = {};
|
|
96
102
|
tableCanvas: HTMLCanvasElement | undefined;
|
|
97
103
|
private registered: {[Key: string]: DG.GridCell} = {};
|
|
104
|
+
protected corrViz: CorrelationAnalysisVisualizer | undefined;
|
|
98
105
|
|
|
99
106
|
constructor() {
|
|
100
107
|
super();
|
|
101
108
|
this.dataEmptyAA = this.string('dataEmptyAA', '-');
|
|
102
109
|
this.initialized = false;
|
|
110
|
+
this.corrViz = undefined;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
init() {
|
|
@@ -249,9 +257,17 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
249
257
|
});
|
|
250
258
|
}
|
|
251
259
|
this.max = df.filter.trueCount;
|
|
260
|
+
this.corrViz = new CorrelationAnalysisVisualizer(df, this.aminoColumnNames);
|
|
252
261
|
}
|
|
253
262
|
|
|
254
|
-
renderBarToCanvas(
|
|
263
|
+
renderBarToCanvas(
|
|
264
|
+
g: CanvasRenderingContext2D,
|
|
265
|
+
cell: DG.GridCell,
|
|
266
|
+
x: number,
|
|
267
|
+
y: number,
|
|
268
|
+
w: number,
|
|
269
|
+
h: number,
|
|
270
|
+
) {
|
|
255
271
|
const margin = 0.2;
|
|
256
272
|
const innerMargin = 0.02;
|
|
257
273
|
const selectLineRatio = 0.1;
|
|
@@ -268,6 +284,14 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
268
284
|
g.fillText(name,
|
|
269
285
|
x + (w - colNameSize)/2,
|
|
270
286
|
y + h + h * margin / 4);
|
|
287
|
+
this.higlightCorrelatedPositionHeader(
|
|
288
|
+
g,
|
|
289
|
+
name,
|
|
290
|
+
x + (w - colNameSize)/2,
|
|
291
|
+
y + h + h * margin / 4,
|
|
292
|
+
colNameSize,
|
|
293
|
+
margin,
|
|
294
|
+
);
|
|
271
295
|
const barData = this.barStats[name]? this.barStats[name]: this.barStats[name];
|
|
272
296
|
let sum = 0;
|
|
273
297
|
barData.forEach((obj) => {
|
|
@@ -287,8 +311,7 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
287
311
|
if (h * margin / 2 <= sBarHeight - gapSize && h * margin / 2 <= w) {
|
|
288
312
|
g.fillStyle = 'rgb(0,0,0)';
|
|
289
313
|
g.font = `${h * margin / 2}px`;
|
|
290
|
-
|
|
291
|
-
const [_c, aar, _p] = cp.getColorAAPivot(obj['name']);
|
|
314
|
+
const [, aar] = cp.getColorAAPivot(obj['name']);
|
|
292
315
|
g.fillText(aar,
|
|
293
316
|
x + w / 2 - h * margin / 8,
|
|
294
317
|
y + h * (this.max - sum + curSum) / this.max + gapSize / 2 + (sBarHeight - gapSize)/2 - h * margin / 8);
|
|
@@ -398,11 +421,63 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
398
421
|
return;
|
|
399
422
|
}
|
|
400
423
|
this.dataFrame!.selection.handleClick((i) => {
|
|
401
|
-
//let selected = true;
|
|
402
424
|
// @ts-ignore
|
|
403
425
|
return this.highlighted!['aaName'] === (this.dataFrame.getCol(this.highlighted!['colName']).get(i));
|
|
404
|
-
|
|
405
|
-
//&& (scope.dataFrame.selection.get(i) === selected);
|
|
406
426
|
}, event);
|
|
407
427
|
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Highlights column header if the corresponding position is correlated with any other.
|
|
431
|
+
*
|
|
432
|
+
* @protected
|
|
433
|
+
* @param {CanvasRenderingContext2D} g A context to draw on.
|
|
434
|
+
* @param {string} name The name of the column.
|
|
435
|
+
* @param {number} x X coordinate to draw at.
|
|
436
|
+
* @param {number} y Y coordinate to draw at.
|
|
437
|
+
* @param {number} width The width to take to draw the highlighting.
|
|
438
|
+
* @param {number} margin The margin to take into account.
|
|
439
|
+
* @memberof StackedBarChart
|
|
440
|
+
*/
|
|
441
|
+
protected higlightCorrelatedPositionHeader(
|
|
442
|
+
g: CanvasRenderingContext2D,
|
|
443
|
+
name: string,
|
|
444
|
+
x: number,
|
|
445
|
+
y: number,
|
|
446
|
+
width: number,
|
|
447
|
+
margin: number,
|
|
448
|
+
) {
|
|
449
|
+
if (this.corrViz?.isPositionCorrelating(name)) {
|
|
450
|
+
const height = width/name.length; //TODO: measure height more precisely.s
|
|
451
|
+
g.fillRect(x, y + height + margin * 20, width, 2);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Formats HTML elements with the correlation analysis to add to the tooltip.
|
|
457
|
+
*
|
|
458
|
+
* @param {string} name A column name to consider.
|
|
459
|
+
* @param {string[]} positions Optional list of columns containing positions.
|
|
460
|
+
* @return {HTMLElement[]} The list of elements. Is empty if the position is not correlating.
|
|
461
|
+
* @memberof StackedBarChart
|
|
462
|
+
*/
|
|
463
|
+
getTooltipElements(name: string, positions: string[]): HTMLElement[] {
|
|
464
|
+
const pos1 = parseInt(name);
|
|
465
|
+
|
|
466
|
+
if (this.corrViz?.isPositionCorrelating(name)) {
|
|
467
|
+
const padLen = Math.round(Math.log10(positions.length))+1;
|
|
468
|
+
|
|
469
|
+
const elements: HTMLElement[] = [];
|
|
470
|
+
elements.push(ui.divText(name, {style: {fontWeight: 'bold', fontSize: 10}}));
|
|
471
|
+
elements.push(ui.divText('Found correlations with:\n'));
|
|
472
|
+
|
|
473
|
+
for (const [pos2, weight] of Object.entries(this.corrViz.path[pos1])) {
|
|
474
|
+
const w = (weight as number);
|
|
475
|
+
const style = {style: {color: w > 0 ? 'red' : 'blue'}};
|
|
476
|
+
elements.push(ui.divText(`${pos2.padStart(padLen, '0')}: R = ${w.toFixed(2)}\n`, style));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return elements;
|
|
480
|
+
}
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
408
483
|
}
|