@datagrok/peptides 0.4.3 → 0.5.6

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.
@@ -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
- ui.tooltip.show(ui.divV([ui.divText(barchart.highlighted.aaName)]), x, y);
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(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number) {
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) => {
@@ -401,4 +425,59 @@ export class StackedBarChart extends DG.JsViewer {
401
425
  return this.highlighted!['aaName'] === (this.dataFrame.getCol(this.highlighted!['colName']).get(i));
402
426
  }, event);
403
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
+ }
404
483
  }
@@ -3,6 +3,16 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
  import {Peptides} from '../peptides';
5
5
 
6
+ /**
7
+ * Peptide analysis widget.
8
+ *
9
+ * @export
10
+ * @param {DG.Column} col Aligned sequence column.
11
+ * @param {DG.TableView} view Working view.
12
+ * @param {DG.Grid} tableGrid Working table grid.
13
+ * @param {DG.DataFrame} currentDf Working table.
14
+ * @return {Promise<DG.Widget>} Widget containing peptide analysis.
15
+ */
6
16
  export async function analyzePeptidesWidget(
7
17
  col: DG.Column, view: DG.TableView, tableGrid: DG.Grid, currentDf: DG.DataFrame,
8
18
  ): Promise<DG.Widget> {
@@ -16,7 +26,7 @@ export async function analyzePeptidesWidget(
16
26
  let hist: DG.Viewer;
17
27
 
18
28
  const activityScalingMethod = ui.choiceInput(
19
- 'Activity scaling',
29
+ 'Scaling',
20
30
  'none',
21
31
  ['none', 'lg', '-lg'],
22
32
  async (currentMethod: string) => {
@@ -54,7 +64,7 @@ export async function analyzePeptidesWidget(
54
64
  activityScalingMethod.fireChanged();
55
65
  };
56
66
  const activityColumnChoice = ui.columnInput(
57
- 'Activity column',
67
+ 'Activity',
58
68
  currentDf,
59
69
  defaultColumn,
60
70
  activityScalingMethodState,
@@ -5,6 +5,13 @@ import $ from 'cash-dom';
5
5
  import {model} from '../viewers/model';
6
6
  import {splitAlignedPeptides} from '../utils/split-aligned';
7
7
 
8
+ /**
9
+ * Manual sequence alignment widget.
10
+ *
11
+ * @param {DG.Column} alignedSequenceCol Aligned sequence column.
12
+ * @param {DG.DataFrame} currentDf Working table.
13
+ * @returns {DG.Widget} Widget for manual sequence alignment.
14
+ */
8
15
  export function manualAlignmentWidget(alignedSequenceCol: DG.Column, currentDf: DG.DataFrame) {
9
16
  const sequenceInput = ui.textInput('', alignedSequenceCol.get(currentDf.currentRowIdx));
10
17
  (sequenceInput.input as HTMLElement).style.height = '50px';
@@ -3,6 +3,13 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
  import {ChemPalette} from '../utils/chem-palette';
5
5
 
6
+ /**
7
+ * 3D representation widget of peptide molecule.
8
+ *
9
+ * @export
10
+ * @param {string} pep Peptide string.
11
+ * @return {Promise<DG.Widget>} Widget.
12
+ */
6
13
  export async function peptideMoleculeWidget(pep: string): Promise<DG.Widget> {
7
14
  const pi = DG.TaskBarProgressIndicator.create('Creating NGL view');
8
15