@datagrok/peptides 0.3.0 → 0.4.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/.eslintrc.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true
5
+ },
6
+ "extends": [
7
+ "google"
8
+ ],
9
+ "parser": "@typescript-eslint/parser",
10
+ "parserOptions": {
11
+ "ecmaVersion": 12,
12
+ "sourceType": "module"
13
+ },
14
+ "plugins": [
15
+ "@typescript-eslint"
16
+ ],
17
+ "rules": {
18
+ "indent": [
19
+ "error",
20
+ 2
21
+ ],
22
+ "max-len": [
23
+ "error",
24
+ 120
25
+ ],
26
+ "spaced-comment": "off",
27
+ "require-jsdoc": "off"
28
+ }
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "",
5
5
  "dependencies": {
6
6
  "@keckelt/tsne": "^1.0.2",
@@ -13,8 +13,8 @@
13
13
  "logojs-react": "^2.1.1",
14
14
  "rxjs": "^6.5.5",
15
15
  "umap-js": "^1.3.3",
16
- "@datagrok-libraries/utils": ">=0.0.10",
17
- "@datagrok-libraries/statistics": ">=0.1.4",
16
+ "@datagrok-libraries/utils": ">=0.0.11",
17
+ "@datagrok-libraries/statistics": ">=0.1.5",
18
18
  "@types/d3": "^7.0.0",
19
19
  "@types/jquery": "^3.5.6"
20
20
  },
package/src/describe.ts CHANGED
@@ -4,12 +4,49 @@ import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
  import {splitAlignedPeptides} from './utils/split-aligned';
6
6
  import {tTest} from '@datagrok-libraries/statistics/src/tests';
7
- import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests.js';
7
+ import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests';
8
8
  import {ChemPalette} from './utils/chem-palette';
9
9
  import {setAARRenderer} from './utils/cell-renderer';
10
10
 
11
11
  const cp = new ChemPalette('grok');
12
12
 
13
+ const aarGroups = {
14
+ 'R': 'PC',
15
+ 'H': 'PC',
16
+ 'K': 'PC',
17
+ 'D': 'NC',
18
+ 'E': 'NC',
19
+ 'S': 'U',
20
+ 'T': 'U',
21
+ 'N': 'U',
22
+ 'Q': 'U',
23
+ 'C': 'SC',
24
+ 'U': 'SC',
25
+ 'G': 'SC',
26
+ 'P': 'SC',
27
+ 'A': 'H',
28
+ 'V': 'H',
29
+ 'I': 'H',
30
+ 'L': 'H',
31
+ 'M': 'H',
32
+ 'F': 'H',
33
+ 'Y': 'H',
34
+ 'W': 'H',
35
+ '-': '-',
36
+ };
37
+
38
+ const groupDescription: {[key: string]: {'description': string, 'aminoAcids': string[]}} = {
39
+ 'PC': {'description': 'Positive Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['R', 'H', 'K']},
40
+ 'NC': {'description': 'Negative Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['D', 'E']},
41
+ 'U': {'description': 'Amino Acids with Polar Uncharged Side Chains', 'aminoAcids': ['S', 'T', 'N', 'Q']},
42
+ 'SC': {'description': 'Special Cases', 'aminoAcids': ['C', 'U', 'G', 'P']},
43
+ 'H': {
44
+ 'description': 'Amino Acids with Hydrophobic Side Chain',
45
+ 'aminoAcids': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
46
+ },
47
+ '-': {'description': 'Unknown Amino Acid', 'aminoAcids': ['-']},
48
+ };
49
+
13
50
  export async function describe(
14
51
  df: DG.DataFrame,
15
52
  activityColumn: string,
@@ -17,7 +54,8 @@ export async function describe(
17
54
  sourceGrid: DG.Grid,
18
55
  twoColorMode: boolean,
19
56
  initialBitset: DG.BitSet | null,
20
- ): Promise<[DG.Grid, DG.Grid, DG.DataFrame]> {
57
+ grouping: boolean,
58
+ ): Promise<[DG.Grid, DG.Grid, DG.DataFrame, {[key: string]: string}]> {
21
59
  //Split the aligned sequence into separate AARs
22
60
  let splitSeqDf: DG.DataFrame | undefined;
23
61
  let invalidIndexes: number[];
@@ -50,6 +88,7 @@ export async function describe(
50
88
  setAARRenderer(col, sourceGrid);
51
89
  }
52
90
  }
91
+
53
92
  if (sourceGrid) {
54
93
  const colNames:string[] = [];
55
94
  for (let i = 0; i < sourceGrid.columns.length; i++) {
@@ -105,6 +144,17 @@ export async function describe(
105
144
 
106
145
  let matrixDf = splitSeqDf.unpivot([activityColumnScaled], positionColumns, positionColName, aminoAcidResidue);
107
146
 
147
+ //TODO: move to chem palette
148
+ let groupMapping: {[key: string]: string} = {};
149
+ if (grouping) {
150
+ groupMapping = aarGroups;
151
+ const aarCol = matrixDf.getCol(aminoAcidResidue);
152
+ aarCol.init((index) => groupMapping[aarCol.get(index)[0]] ?? '-');
153
+ aarCol.compact();
154
+ } else {
155
+ Object.keys(aarGroups).forEach((value) => groupMapping[value] = value);
156
+ }
157
+
108
158
  //statistics for specific AAR at a specific position
109
159
  matrixDf = matrixDf.groupBy([positionColName, aminoAcidResidue])
110
160
  .add('count', activityColumnScaled, 'Count')
@@ -135,14 +185,14 @@ export async function describe(
135
185
  AAR = matrixDf.get(aminoAcidResidue, i);
136
186
 
137
187
  //@ts-ignore
138
- splitSeqDf.rows.select((row) => row[position] === AAR);
188
+ splitSeqDf.rows.select((row) => groupMapping[row[position]] === AAR);
139
189
  currentActivity = splitSeqDf
140
190
  .clone(splitSeqDf.selection, [activityColumnScaled])
141
191
  .getCol(activityColumnScaled)
142
192
  .toList();
143
193
 
144
194
  //@ts-ignore
145
- splitSeqDf.rows.select((row) => row[position] !== AAR);
195
+ splitSeqDf.rows.select((row) => groupMapping[row[position]] !== AAR);
146
196
  otherActivity = splitSeqDf
147
197
  .clone(splitSeqDf.selection, [activityColumnScaled])
148
198
  .getCol(activityColumnScaled)
@@ -224,17 +274,14 @@ export async function describe(
224
274
  SARVgrid.col('pValue')!.format = 'four digits after comma';
225
275
  SARVgrid.col('pValue')!.name = 'P-Value';
226
276
 
227
- //FIXME: looks inefficient
228
- for (const col of matrixDf.columns) {
229
- if (col.name === aminoAcidResidue) {
230
- setAARRenderer(col, SARgrid);
231
- break;
277
+ if (!grouping) {
278
+ let tempCol = matrixDf.columns.byName(aminoAcidResidue);
279
+ if (tempCol) {
280
+ setAARRenderer(tempCol, SARgrid);
232
281
  }
233
- }
234
- for (const col of sequenceDf.columns) {
235
- if (col.name === aminoAcidResidue) {
236
- setAARRenderer(col, SARVgrid);
237
- break;
282
+ tempCol = sequenceDf.columns.byName(aminoAcidResidue);
283
+ if (tempCol) {
284
+ setAARRenderer(tempCol, SARgrid);
238
285
  }
239
286
  }
240
287
 
@@ -362,7 +409,13 @@ export async function describe(
362
409
  cell.cell.value !== null &&
363
410
  cell.tableRowIndex !== null
364
411
  ) {
365
- cp.showTooltip(cell, x, y);
412
+ if (grouping) {
413
+ const currentGroup = groupDescription[cell.cell.value];
414
+ const divText = ui.divText('Amino Acids in this group: ' + currentGroup['aminoAcids'].join(', '));
415
+ ui.tooltip.show(ui.divV([ui.h3(currentGroup['description']), divText]), x, y);
416
+ } else {
417
+ cp.showTooltip(cell, x, y);
418
+ }
366
419
  }
367
420
  return true;
368
421
  };
@@ -376,9 +429,14 @@ export async function describe(
376
429
  }
377
430
  });
378
431
 
379
- // for (const col of matrixDf.columns.names()) {
380
- // SARgrid.col(col)!.width = SARgrid.props.rowHeight;
381
- // }
432
+ for (const col of matrixDf.columns.names()) {
433
+ SARgrid.col(col)!.width = SARgrid.props.rowHeight;
434
+ }
435
+
436
+ if (grouping) {
437
+ SARgrid.col(aminoAcidResidue)!.name = 'Groups';
438
+ SARVgrid.col(aminoAcidResidue)!.name = 'Groups';
439
+ }
382
440
 
383
- return [SARgrid, SARVgrid, statsDf];
441
+ return [SARgrid, SARVgrid, statsDf, groupMapping];
384
442
  }
package/src/package.ts CHANGED
@@ -197,7 +197,7 @@ export function manualAlignment(monomer: string) {
197
197
  //input: column col {semType: alignedSequence}
198
198
  //output: widget result
199
199
  export async function peptideSpacePanel(col: DG.Column): Promise<DG.Widget> {
200
- const widget = new PeptideSimilaritySpaceWidget(col);
200
+ const widget = new PeptideSimilaritySpaceWidget(col, view ?? grok.shell.v);
201
201
  return await widget.draw();
202
202
  }
203
203
 
@@ -0,0 +1,74 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+ import {createPeptideSimilaritySpaceViewer} from './utils/peptide-similarity-space';
5
+ import {addViewerToHeader} from './viewers/stacked-barchart-viewer';
6
+
7
+ export class Peptides {
8
+ async init(
9
+ tableGrid: DG.Grid,
10
+ view: DG.TableView,
11
+ currentDf: DG.DataFrame,
12
+ options: {[key: string]: string},
13
+ col: DG.Column,
14
+ activityColumnChoice: string
15
+ ) {
16
+ for (let i = 0; i < tableGrid.columns.length; i++) {
17
+ const col = tableGrid.columns.byIndex(i);
18
+ if (col &&
19
+ col.name &&
20
+ col.column?.semType != 'aminoAcids'
21
+ ) {
22
+ //@ts-ignore
23
+ tableGrid.columns.byIndex(i)?.visible = false;
24
+ }
25
+ }
26
+
27
+ const originalDfColumns = (currentDf.columns as DG.ColumnList).names();
28
+
29
+ const sarViewer = view.addViewer('peptide-sar-viewer', options);
30
+ const sarNode = view.dockManager.dock(sarViewer, DG.DOCK_TYPE.DOWN, null, 'SAR Viewer');
31
+
32
+ const sarViewerVertical = view.addViewer('peptide-sar-viewer-vertical');
33
+ const sarVNode = view.dockManager.dock(sarViewerVertical, DG.DOCK_TYPE.RIGHT, sarNode, 'SAR Vertical Viewer');
34
+
35
+ const peptideSpaceViewer = await createPeptideSimilaritySpaceViewer(
36
+ currentDf,
37
+ col,
38
+ 't-SNE',
39
+ 'Levenshtein',
40
+ 100,
41
+ view,
42
+ `${activityColumnChoice}Scaled`,
43
+ );
44
+ const psNode = view.dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.LEFT, sarNode, 'Peptide Space Viewer', 0.3);
45
+
46
+ const StackedBarchartProm = currentDf.plot.fromType('StackedBarChartAA');
47
+ addViewerToHeader(tableGrid, StackedBarchartProm);
48
+
49
+ const hideIcon = ui.iconFA('window-close', () => { //undo?, times?
50
+ const viewers = [];
51
+ for (const viewer of view.viewers) {
52
+ if (viewer.type !== DG.VIEWER.GRID) {
53
+ viewers.push(viewer);
54
+ }
55
+ }
56
+ viewers.forEach((v) => v.close());
57
+
58
+ const cols = (view.dataFrame.columns as DG.ColumnList);
59
+ for (const colName of cols.names()) {
60
+ if (!originalDfColumns.includes(colName)) {
61
+ cols.remove(colName);
62
+ }
63
+ }
64
+
65
+ tableGrid.setOptions({'colHeaderHeight': 20});
66
+ tableGrid.columns.setVisible(originalDfColumns);
67
+
68
+ view.setRibbonPanels(ribbonPanels);
69
+ }, 'Close viewers and restore dataframe');
70
+
71
+ const ribbonPanels = view.getRibbonPanels();
72
+ view.setRibbonPanels([[hideIcon]]);
73
+ }
74
+ }
@@ -23,24 +23,27 @@ export function expandColumn(col:DG.Column,
23
23
  timeout);
24
24
  }
25
25
 
26
- export function setAARRenderer(col:DG.Column, grid:DG.Grid|null = null) {
26
+ export function setAARRenderer(col: DG.Column, grid: DG.Grid | null = null, grouping = false) {
27
27
  col.semType = 'aminoAcids';
28
28
  col.setTag('cell.renderer', 'aminoAcids');
29
- if (grid)
29
+ if (grouping) {
30
+ col.setTag('groups', `${grouping}`);
31
+ }
32
+ if (grid) {
30
33
  expandColumn(col, grid, (ent) => measureAAR(ent));
34
+ }
31
35
  }
32
36
 
33
37
  export function measureAAR(s: string): number {
34
38
  const end = s.lastIndexOf(')');
35
39
  const beg = s.indexOf('(');
36
- return end == beg ? s.length:s.length - (end-beg)+1;
40
+ return end == beg ? s.length : s.length - (end - beg) + 1;
37
41
  }
38
42
 
39
43
  function printLeftCentered(
40
- x: number, y: number, w: number, h: number,
41
- g: CanvasRenderingContext2D, s: string, color = ChemPalette.undefinedColor,
42
- pivot: number = 0, left = false, hideMod = false) {
43
-
44
+ x: number, y: number, w: number, h: number,
45
+ g: CanvasRenderingContext2D, s: string, color = ChemPalette.undefinedColor,
46
+ pivot: number = 0, left = false, hideMod = false) {
44
47
  g.textAlign = 'start';
45
48
  let colorPart = pivot == -1 ? s.substring(0) : s.substring(0, pivot);
46
49
  if (colorPart.length == 1) {
@@ -91,8 +94,7 @@ function printLeftCentered(
91
94
  if (left || textSize.width > w) {
92
95
  draw(indent, indent + colorTextSize.width);
93
96
  return x + colorTextSize.width + g.measureText(grayPart).width;
94
- }
95
- else {
97
+ } else {
96
98
  draw((w - textSize.width) / 2, (w - textSize.width) / 2 + colorTextSize.width);
97
99
  return x + (w - textSize.width) / 2 + colorTextSize.width;
98
100
  }
@@ -100,13 +102,26 @@ function printLeftCentered(
100
102
 
101
103
 
102
104
  export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
105
+ chemPalette: ChemPalette | null;
106
+
107
+ get name() {
108
+ return 'aminoAcidsCR';
109
+ }
103
110
 
104
- get name() { return 'aminoAcidsCR'; }
111
+ get cellType() {
112
+ return 'aminoAcids';
113
+ }
105
114
 
106
- get cellType() { return 'aminoAcids'; }
115
+ constructor() {
116
+ super();
117
+ this.chemPalette = null;
118
+ }
107
119
 
108
120
  render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
109
- gridCell: DG.GridCell, cellStyle: DG.GridCellStyle) {
121
+ gridCell: DG.GridCell, cellStyle: DG.GridCellStyle) {
122
+ if (this.chemPalette === null) {
123
+ this.chemPalette = new ChemPalette('grok', gridCell.tableColumn?.getTag('groups') ? true : false);
124
+ }
110
125
  g.save();
111
126
  g.beginPath();
112
127
  g.rect(x, y, w, h);
@@ -122,44 +137,47 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
122
137
 
123
138
 
124
139
  export class AlignedSequenceCellRenderer extends DG.GridCellRenderer {
140
+ get name() {
141
+ return 'alignedSequenceCR';
142
+ }
125
143
 
126
- get name() { return 'alignedSequenceCR'; }
127
-
128
- get cellType() { return 'alignedSequence'; }
129
-
130
- render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
131
- gridCell: DG.GridCell, cellStyle: DG.GridCellStyle ) {
132
- w = Math.min(gridCell.grid.canvas.width - x, w);
133
- g.save();
134
- g.beginPath();
135
- g.rect(x, y, w, h);
136
- g.clip();
137
- g.font = '14px monospace';
138
- g.textBaseline = 'top';
139
- const s: string = gridCell.cell.value ?? '';
140
-
141
- const subParts = s.split('-');
142
- const [text, simplified] = processSequence(subParts);
143
- const textSize = g.measureText(text.join(''));
144
- x = Math.max(x, x + (w - textSize.width) / 2);
145
-
146
- subParts.forEach((amino: string, index) => {
147
- const [color, pivot] = cp.getColorPivot(amino);
148
- g.fillStyle = ChemPalette.undefinedColor;
149
- if (index + 1 < subParts.length) {
150
- const gap = simplified?'':' ';
151
- amino += `${amino?'':'-'}${gap}`;
152
- }
153
- x = printLeftCentered(x, y, w, h, g, amino, color, pivot, true);
154
- });
144
+ get cellType() {
145
+ return 'alignedSequence';
146
+ }
155
147
 
156
- g.restore();
157
- }
148
+ render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
149
+ gridCell: DG.GridCell, cellStyle: DG.GridCellStyle ) {
150
+ w = Math.min(gridCell.grid.canvas.width - x, w);
151
+ g.save();
152
+ g.beginPath();
153
+ g.rect(x, y, w, h);
154
+ g.clip();
155
+ g.font = '14px monospace';
156
+ g.textBaseline = 'top';
157
+ const s: string = gridCell.cell.value ?? '';
158
+
159
+ //TODO: can this be replaced/merged with splitSequence?
160
+ const subParts = s.split('-');
161
+ const [text, simplified] = processSequence(subParts);
162
+ const textSize = g.measureText(text.join(''));
163
+ x = Math.max(x, x + (w - textSize.width) / 2);
164
+
165
+ subParts.forEach((amino: string, index) => {
166
+ const [color, pivot] = cp.getColorPivot(amino);
167
+ g.fillStyle = ChemPalette.undefinedColor;
168
+ if (index + 1 < subParts.length) {
169
+ const gap = simplified?'':' ';
170
+ amino += `${amino?'':'-'}${gap}`;
171
+ }
172
+ x = printLeftCentered(x, y, w, h, g, amino, color, pivot, true);
173
+ });
174
+
175
+ g.restore();
176
+ }
158
177
  }
159
178
 
160
179
 
161
180
  export function processSequence(subParts:string[]) : [string[], boolean] {
162
-
163
181
  const simplified = !subParts.some((amino, index) =>
164
182
  amino.length > 1 &&
165
183
  index != 0 &&
@@ -174,4 +192,4 @@ export function processSequence(subParts:string[]) : [string[], boolean] {
174
192
  text.push(amino);
175
193
  });
176
194
  return [text, simplified];
177
- }
195
+ }
@@ -6,17 +6,16 @@ import * as DG from 'datagrok-api/dg';
6
6
  export class ChemPalette {
7
7
  cp: {[key: string]: string} = {};
8
8
 
9
- constructor(scheme: string) {
9
+ constructor(scheme: string, grouping = false) {
10
10
  if (scheme == 'grok') {
11
- this.cp = ChemPalette.getDatagrok();
11
+ this.cp = ChemPalette.getDatagrok(grouping);
12
12
  }
13
13
  }
14
14
 
15
15
  showTooltip(cell:DG.GridCell, x:number, y:number) {
16
16
  const s = cell.cell.value as string;
17
17
  let toDisplay = [ui.divText(s)];
18
- // eslint-disable-next-line no-unused-vars
19
- const [_c, aar, _p] = this.getColorAAPivot(s);
18
+ const [, aar] = this.getColorAAPivot(s);
20
19
  if (aar in ChemPalette.AASmiles) {
21
20
  if (s in ChemPalette.AANames) {
22
21
  toDisplay = [ui.divText(ChemPalette.AANames[s])];
@@ -31,8 +30,7 @@ export class ChemPalette {
31
30
  }
32
31
 
33
32
  getColor( c: string) {
34
- // eslint-disable-next-line no-unused-vars
35
- const [color, _] = this.getColorPivot(c);
33
+ const [color] = this.getColorPivot(c);
36
34
  return color;
37
35
  }
38
36
 
@@ -77,8 +75,7 @@ export class ChemPalette {
77
75
  }
78
76
 
79
77
  getColorPivot(c = ''): [string, number] {
80
- // eslint-disable-next-line no-unused-vars
81
- const [color, _, pivot] = this.getColorAAPivot(c);
78
+ const [color,, pivot] = this.getColorAAPivot(c);
82
79
  return [color, pivot];
83
80
  };
84
81
 
@@ -130,14 +127,17 @@ export class ChemPalette {
130
127
  'all_blue': ['K', 'R'],
131
128
  }
132
129
 
133
- static undefinedColor = 'rgb(100,100,100)'
130
+ static undefinedColor = 'rgb(100,100,100)';
134
131
 
135
- static makePalette(dt: {[key: string]: string[]}, simplified = false) {
132
+ static makePalette(dt: {[key: string]: string[]}, simplified = false, grouping = false) {
136
133
  const palette: { [key: string]: string } = {};
134
+ const groups = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
135
+ let currentGroup = 0;
137
136
  for (const [color, monomers] of Object.entries(dt)) {
138
137
  monomers.forEach((monomer, index) => {
139
- palette[monomer] = ChemPalette.colourPalette[color][simplified ? 0 : index];
138
+ palette[grouping ? groups[currentGroup] : monomer] = ChemPalette.colourPalette[color][simplified ? 0 : index];
140
139
  });
140
+ currentGroup++;
141
141
  }
142
142
  return palette;
143
143
  }
@@ -234,8 +234,8 @@ export class ChemPalette {
234
234
  'Val': 'V',
235
235
  }
236
236
 
237
- static getDatagrok() {
238
- return ChemPalette.makePalette(ChemPalette.grokGroups);
237
+ static getDatagrok(grouping = false) {
238
+ return ChemPalette.makePalette(ChemPalette.grokGroups, false, grouping);
239
239
  }
240
240
 
241
241
  static getLesk() {
@@ -4,17 +4,28 @@
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import {AlignedSequenceEncoder} from '@datagrok-libraries/utils/src/sequence-encoder';
7
- import {assert, transposeMatrix, matrix2DataFrame} from '@datagrok-libraries/utils/src/operations';
8
- import {Vector} from '@datagrok-libraries/utils/src/type_declarations';
7
+ import {assert, transposeMatrix} from '@datagrok-libraries/utils/src/operations';
8
+ import {Vector, Matrix} from '@datagrok-libraries/utils/src/type-declarations';
9
+ import {kendallsTau} from '@datagrok-libraries/statistics/src/correlation-coefficient';
9
10
 
10
11
  /**
11
- * Encodes amino acid sequences into a numeric representation.
12
+ * Converts a Matrix into a DataFrame.
12
13
  *
13
14
  * @export
15
+ * @param {Matrix} matrix A matrix.
16
+ * @return {DG.DataFrame} The data frame.
17
+ */
18
+ export function matrix2DataFrame(matrix: Matrix): DG.DataFrame {
19
+ return DG.DataFrame.fromColumns(matrix.map((v, i) => DG.Column.fromFloat32Array(`${i+1}`, v)));
20
+ }
21
+
22
+ /**
23
+ * Encodes amino acid sequences into a numeric representation.
24
+ *
14
25
  * @param {DG.Column} col A column containing the sequences.
15
26
  * @return {DG.DataFrame} The resulting data frame.
16
27
  */
17
- export function calcPositions(col: DG.Column): DG.DataFrame {
28
+ function calcPositions(col: DG.Column): DG.DataFrame {
18
29
  const sequences = col.toList().map((v, _) => AlignedSequenceEncoder.clean(v));
19
30
  const enc = new AlignedSequenceEncoder();
20
31
  const encSeqs = sequences.map((v) => Vector.from(enc.encode(v)));
@@ -25,11 +36,10 @@ export function calcPositions(col: DG.Column): DG.DataFrame {
25
36
  /**
26
37
  * Unfolds a data frame into <category>-<value> format.
27
38
  *
28
- * @export
29
39
  * @param {DG.DataFrame} df A data frame to unfold.
30
40
  * @return {DG.DataFrame} The resulting data frame.
31
41
  */
32
- export function melt(df: DG.DataFrame): DG.DataFrame {
42
+ function melt(df: DG.DataFrame): DG.DataFrame {
33
43
  let keys: string[] = [];
34
44
  const values: Float32Array = new Float32Array(df.columns.length*df.rowCount);
35
45
  let i = 0;
@@ -43,38 +53,45 @@ export function melt(df: DG.DataFrame): DG.DataFrame {
43
53
  return DG.DataFrame.fromColumns([DG.Column.fromStrings('keys', keys), DG.Column.fromFloat32Array('values', values)]);
44
54
  }
45
55
 
46
- /*export async function calcSpearmanRhoMatrixExt(positions: Matrix): Promise<Matrix> {
47
- const Spearman = require('spearman-rho');
48
- const nItems = positions.length;
56
+ /**
57
+ * Calculates Spearman's rho rank correlation coefficient.
58
+ *
59
+ * @param {DG.DataFrame} df A data frame to process.
60
+ * @return {DG.DataFrame} The correlation matrix.
61
+ */
62
+ // eslint-disable-next-line no-unused-vars
63
+ function calcSpearmanRhoMatrix(df: DG.DataFrame): DG.DataFrame {
64
+ const nItems = df.columns.length;
49
65
  const rho = new Array(nItems).fill(0).map((_) => new Float32Array(nItems).fill(0));
50
66
 
51
67
  for (let i = 0; i < nItems; ++i) {
52
68
  for (let j = i+1; j < nItems; ++j) {
53
- rho[i][j] = await(new Spearman(positions[i], positions[j])).calc();
69
+ rho[i][j] = df.columns.byIndex(i).stats.spearmanCorr(df.columns.byIndex(j));
54
70
  rho[j][i] = rho[i][j];
55
71
  }
56
72
  }
57
- return rho;
58
- }*/
73
+ return matrix2DataFrame(rho);
74
+ }
59
75
 
60
76
  /**
61
- * Calculates Spearman's rho rank correlation coefficient.
77
+ * Calculates Kendall's tau rank correlation coefficient.
62
78
  *
63
- * @export
64
79
  * @param {DG.DataFrame} df A data frame to process.
80
+ * @param {number} [alpha=0.05] The significance threshold.
65
81
  * @return {DG.DataFrame} The correlation matrix.
66
82
  */
67
- export function calcSpearmanRhoMatrix(df: DG.DataFrame): DG.DataFrame {
83
+ function calcKendallTauMatrix(df: DG.DataFrame, alpha: number = 0.05): DG.DataFrame {
68
84
  const nItems = df.columns.length;
69
- const rho = new Array(nItems).fill(0).map((_) => new Float32Array(nItems).fill(0));
85
+ const tau = new Array(nItems).fill(0).map((_) => new Float32Array(nItems).fill(0));
70
86
 
71
87
  for (let i = 0; i < nItems; ++i) {
72
88
  for (let j = i+1; j < nItems; ++j) {
73
- rho[i][j] = df.columns.byIndex(i).stats.spearmanCorr(df.columns.byIndex(j));
74
- rho[j][i] = rho[i][j];
89
+ const res = kendallsTau(df.columns.byIndex(i).getRawData(), df.columns.byIndex(j).getRawData());
90
+ tau[i][j] = res.prob < alpha ? res.test : 0;
91
+ tau[j][i] = tau[i][j];
75
92
  }
76
93
  }
77
- return matrix2DataFrame(rho);
94
+ return matrix2DataFrame(tau);
78
95
  }
79
96
 
80
97
  /**
@@ -95,7 +112,7 @@ export function correlationAnalysisPlots(sequencesColumn: DG.Column): [DG.Viewer
95
112
  'correlationType': 'Spearman',
96
113
  });
97
114
 
98
- const rhoDF = calcSpearmanRhoMatrix(posDF);
115
+ const rhoDF = calcKendallTauMatrix(posDF);
99
116
  const meltDF = melt(rhoDF);
100
117
 
101
118
  const bpviewer = DG.Viewer.fromType(
@@ -8,7 +8,7 @@ import {getSequenceMolecularWeight} from './molecular-measure';
8
8
  import {AlignedSequenceEncoder} from '@datagrok-libraries/utils/src/sequence-encoder';
9
9
  import {DimensionalityReducer} from '@datagrok-libraries/utils/src/reduce-dimensionality';
10
10
  import {Measurer} from '@datagrok-libraries/utils/src/string-measure';
11
- import {Coordinates} from '@datagrok-libraries/utils/src/type_declarations';
11
+ import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
12
12
 
13
13
  /**
14
14
  * Creates a worker to perform a dimensionality reduction.
@@ -75,6 +75,7 @@ export async function createPeptideSimilaritySpaceViewer(
75
75
  method: string,
76
76
  measure: string,
77
77
  cyclesCount: number,
78
+ view: DG.TableView | null,
78
79
  activityColumnName?: string | null,
79
80
  zoom: boolean = false,
80
81
  ): Promise<DG.ScatterPlotViewer> {
@@ -117,8 +118,11 @@ export async function createPeptideSimilaritySpaceViewer(
117
118
  table.columns.replace(col, edf.getCol(axis));
118
119
  }
119
120
  }
120
- const viewer = DG.Viewer.scatterPlot(table, {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'});
121
121
 
122
+ // const viewer = DG.Viewer.scatterPlot(table, {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'});
123
+ const viewerOptions = {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'};
124
+ const viewer = view !== null ?
125
+ view.addViewer(DG.VIEWER.SCATTER_PLOT, viewerOptions) : DG.Viewer.scatterPlot(table, viewerOptions);
122
126
  // Fit view if needed.
123
127
  /*if (zoom) {
124
128
  viewer.zoom(
@@ -129,7 +133,7 @@ export async function createPeptideSimilaritySpaceViewer(
129
133
  );
130
134
  }*/
131
135
  pi.close();
132
- return viewer;
136
+ return (viewer as DG.ScatterPlotViewer);
133
137
  }
134
138
 
135
139
  /**
@@ -147,13 +151,14 @@ export class PeptideSimilaritySpaceWidget {
147
151
  protected availableMethods: string[];
148
152
  protected availableMetrics: string[];
149
153
  protected viewer: HTMLElement;
154
+ view: DG.TableView;
150
155
 
151
156
  /**
152
157
  * Creates an instance of PeptideSimilaritySpaceWidget.
153
158
  * @param {DG.Column} alignedSequencesColumn The column to get amino acid sequences from.
154
159
  * @memberof PeptideSimilaritySpaceWidget
155
160
  */
156
- constructor(alignedSequencesColumn: DG.Column) {
161
+ constructor(alignedSequencesColumn: DG.Column, view: DG.TableView) {
157
162
  this.availableMethods = DimensionalityReducer.availableMethods;
158
163
  this.availableMetrics = Measurer.availableMeasures;
159
164
  this.method = this.availableMethods[0];
@@ -161,6 +166,7 @@ export class PeptideSimilaritySpaceWidget {
161
166
  this.currentDf = alignedSequencesColumn.dataFrame;
162
167
  this.alignedSequencesColumn = alignedSequencesColumn;
163
168
  this.viewer = ui.div([]);
169
+ this.view = view;
164
170
  }
165
171
 
166
172
  /**
@@ -177,6 +183,7 @@ export class PeptideSimilaritySpaceWidget {
177
183
  this.metrics,
178
184
  this.cycles,
179
185
  null,
186
+ null,
180
187
  true,
181
188
  );
182
189
  viewer.root.style.width = 'auto';
@@ -45,7 +45,7 @@ export function splitAlignedPeptides(peptideColumn: DG.Column, filter: boolean =
45
45
  columnNames.push('C-terminal');
46
46
 
47
47
  // filter out the columns with the same values
48
-
48
+
49
49
  if (filter) {
50
50
  splitColumns = splitColumns.filter((positionArray, index) => {
51
51
  const isRetained = new Set(positionArray).size > 1;
@@ -1,15 +1,17 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
 
3
3
  import {describe} from '../describe';
4
- import { Subject } from 'rxjs';
4
+ import {Subject} from 'rxjs';
5
5
 
6
6
  class SARViewerModel {
7
7
  private viewerGrid: Subject<DG.Grid> = new Subject<DG.Grid>();
8
8
  private viewerVGrid: Subject<DG.Grid> = new Subject<DG.Grid>();
9
9
  private statsDf: Subject<DG.DataFrame> = new Subject<DG.DataFrame>();
10
- public viewerGrid$ = this.viewerGrid.asObservable();
11
- public viewerVGrid$ = this.viewerVGrid.asObservable();
12
- public statsDf$ = this.statsDf.asObservable();
10
+ private groupMapping: Subject<{[key: string]: string}> = new Subject<{[key: string]: string}>();
11
+ public viewerGrid$;
12
+ public viewerVGrid$;
13
+ public statsDf$;
14
+ public groupMapping$;
13
15
  private dataFrame: DG.DataFrame | null;
14
16
  private activityColumn: string | null;
15
17
  private activityScaling: string | null;
@@ -17,6 +19,7 @@ class SARViewerModel {
17
19
  private twoColorMode: boolean | null;
18
20
  private initialBitset: DG.BitSet | null;
19
21
  private isUpdating = false;
22
+ grouping: boolean;
20
23
 
21
24
  constructor() {
22
25
  this.dataFrame = null;
@@ -25,39 +28,49 @@ class SARViewerModel {
25
28
  this.sourceGrid = null;
26
29
  this.twoColorMode = null;
27
30
  this.initialBitset = null;
31
+ this.grouping = false;
32
+ this.viewerGrid$ = this.viewerGrid.asObservable();
33
+ this.viewerVGrid$ = this.viewerVGrid.asObservable();
34
+ this.statsDf$ = this.statsDf.asObservable();
35
+ this.groupMapping$ = this.groupMapping.asObservable();
28
36
  }
29
37
 
30
38
  async updateData(
31
- df: DG.DataFrame,
32
- activityCol: string,
33
- activityScaling: string,
34
- sourceGrid: DG.Grid,
35
- twoColorMode: boolean,
36
- initialBitset: DG.BitSet | null,
37
- ) {
39
+ df: DG.DataFrame,
40
+ activityCol: string,
41
+ activityScaling: string,
42
+ sourceGrid: DG.Grid,
43
+ twoColorMode: boolean,
44
+ initialBitset: DG.BitSet | null,
45
+ grouping: boolean,
46
+ ) {
38
47
  this.dataFrame = df;
39
48
  this.activityColumn = activityCol;
40
49
  this.activityScaling = activityScaling;
41
50
  this.sourceGrid = sourceGrid;
42
51
  this.twoColorMode = twoColorMode;
43
52
  this.initialBitset = initialBitset;
53
+ this.grouping = grouping;
44
54
  await this.updateDefault();
45
55
  }
46
56
 
47
57
  async updateDefault() {
48
- if (this.dataFrame && this.activityColumn && this.activityScaling && this.sourceGrid && this.twoColorMode !== null && !this.isUpdating) {
58
+ if (
59
+ this.dataFrame && this.activityColumn && this.activityScaling &&
60
+ this.sourceGrid && this.twoColorMode !== null && !this.isUpdating
61
+ ) {
49
62
  this.isUpdating = true;
50
- const [viewerGrid, viewerVGrid, statsDf] = await describe(
63
+ const [viewerGrid, viewerVGrid, statsDf, groupMapping] = await describe(
51
64
  this.dataFrame, this.activityColumn, this.activityScaling,
52
- this.sourceGrid, this.twoColorMode, this.initialBitset
65
+ this.sourceGrid, this.twoColorMode, this.initialBitset, this.grouping,
53
66
  );
54
67
  this.viewerGrid.next(viewerGrid);
55
68
  this.viewerVGrid.next(viewerVGrid);
56
69
  this.statsDf.next(statsDf);
70
+ this.groupMapping.next(groupMapping);
57
71
  this.isUpdating = false;
58
72
  }
59
73
  }
60
74
  }
61
75
 
62
- export let model = new SARViewerModel();
63
-
76
+ export const model = new SARViewerModel();
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import $ from 'cash-dom';
6
6
 
7
- import { model } from './model';
7
+ import {model} from './model';
8
8
 
9
9
  export class SARViewer extends DG.JsViewer {
10
10
  protected viewerGrid: DG.Grid | null;
@@ -20,16 +20,19 @@ export class SARViewer extends DG.JsViewer {
20
20
  protected _initialBitset: DG.BitSet | null;
21
21
  protected viewerVGrid: DG.Grid | null;
22
22
  protected currentBitset: DG.BitSet | null;
23
+ grouping: boolean;
24
+ groupMapping: {[key: string]: string} | null;
23
25
  // private df: DG.DataFrame | null;
24
26
  // protected pValueThreshold: number;
25
27
  // protected amountOfBestAARs: number;
26
28
  // duplicatesHandingMethod: string;
27
29
  constructor() {
28
30
  super();
29
-
31
+
30
32
  this.viewerGrid = null;
31
33
  this.viewerVGrid = null;
32
34
  this.statsDf = null;
35
+ this.groupMapping = null;
33
36
  this.initialized = false;
34
37
  this.aminoAcidResidue = 'AAR';
35
38
  this._initialBitset = null;
@@ -41,6 +44,7 @@ export class SARViewer extends DG.JsViewer {
41
44
  this.activityScalingMethod = this.string('activityScalingMethod', 'none', {choices: ['none', 'lg', '-lg']});
42
45
  this.filterMode = this.bool('filterMode', false);
43
46
  this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
47
+ this.grouping = this.bool('grouping', false);
44
48
  // this.pValueThreshold = this.float('pValueThreshold', 0.1);
45
49
  // this.amountOfBestAARs = this.int('amountOfBestAAR', 1);
46
50
  // this.duplicatesHandingMethod = this.string('duplicatesHandlingMethod', 'median', {choices: ['median']});
@@ -51,12 +55,13 @@ export class SARViewer extends DG.JsViewer {
51
55
  init() {
52
56
  this._initialBitset = this.dataFrame!.filter.clone();
53
57
  this.initialized = true;
54
- this.subs.push(model.statsDf$.subscribe(data => this.statsDf = data));
55
- this.subs.push(model.viewerGrid$.subscribe(data => {
58
+ this.subs.push(model.statsDf$.subscribe((data) => this.statsDf = data));
59
+ this.subs.push(model.viewerGrid$.subscribe((data) => {
56
60
  this.viewerGrid = data;
57
61
  this.render();
58
62
  }));
59
- this.subs.push(model.viewerVGrid$.subscribe(data => this.viewerVGrid = data));
63
+ this.subs.push(model.viewerVGrid$.subscribe((data) => this.viewerVGrid = data));
64
+ this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
60
65
  }
61
66
 
62
67
  onTableAttached() {
@@ -114,7 +119,7 @@ export class SARViewer extends DG.JsViewer {
114
119
  splitCol = this.dataFrame.columns.addNew(splitColName, 'string');
115
120
  }
116
121
 
117
- const isChosen = (i: number) => this.dataFrame!.get(currentPosition, i) === currentAAR;
122
+ const isChosen = (i: number) => this.groupMapping![this.dataFrame!.get(currentPosition, i)] === currentAAR;
118
123
  splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
119
124
 
120
125
  //TODO: use column.compact
@@ -277,6 +282,7 @@ export class SARViewer extends DG.JsViewer {
277
282
  this.sourceGrid,
278
283
  this.bidirectionalAnalysis,
279
284
  this._initialBitset,
285
+ this.grouping,
280
286
  );
281
287
 
282
288
  if (this.viewerGrid !== null && this.viewerVGrid !== null) {
@@ -303,7 +309,7 @@ export class SARViewerVertical extends DG.JsViewer {
303
309
  super();
304
310
 
305
311
  this.viewerVGrid = null;
306
- this.subs.push(model.viewerVGrid$.subscribe(data => {
312
+ this.subs.push(model.viewerVGrid$.subscribe((data) => {
307
313
  this.viewerVGrid = data;
308
314
  this.render();
309
315
  }));
@@ -316,4 +322,4 @@ export class SARViewerVertical extends DG.JsViewer {
316
322
  }
317
323
  this.viewerVGrid?.invalidate();
318
324
  }
319
- }
325
+ }
@@ -115,8 +115,8 @@ export class StackedBarChart extends DG.JsViewer {
115
115
  let i = 0;
116
116
  for (const value of Object.values(groups)) {
117
117
  i++;
118
- for (const obj in value) {
119
- this.ord[value[obj]] = i;
118
+ for (const obj of value) {
119
+ this.ord[obj] = i;
120
120
  }
121
121
  }
122
122
  this.yScale = scaleLinear();
@@ -1,9 +1,7 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
- import {createPeptideSimilaritySpaceViewer} from '../utils/peptide-similarity-space';
5
- import {addViewerToHeader} from '../viewers/stacked-barchart-viewer';
6
- import {model} from '../viewers/model';
4
+ import { Peptides } from '../peptides';
7
5
 
8
6
  export async function analyzePeptidesWidget(
9
7
  col: DG.Column, view: DG.TableView, tableGrid: DG.Grid, currentDf: DG.DataFrame,
@@ -24,6 +22,7 @@ export async function analyzePeptidesWidget(
24
22
  async (currentMethod: string) => {
25
23
  const currentActivityCol = activityColumnChoice.value.name;
26
24
  const tempDf = currentDf.clone(currentDf.filter, [currentActivityCol]);
25
+ //TODO: merge with scaling in describe
27
26
  switch (currentMethod) {
28
27
  case 'lg':
29
28
  await tempDf.columns.addNewCalculated('scaledActivity', 'Log10(${' + currentActivityCol + '})');
@@ -64,45 +63,20 @@ export async function analyzePeptidesWidget(
64
63
  activityScalingMethod.fireChanged();
65
64
 
66
65
  const startBtn = ui.button('Launch SAR', async () => {
67
- const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
68
66
  if (activityColumnChoice.value.type === DG.TYPE.FLOAT) {
67
+ const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
69
68
  const options: {[key: string]: string} = {
70
69
  'activityColumnColumnName': activityColumnChoice.value.name,
71
70
  'activityScalingMethod': activityScalingMethod.value,
72
71
  };
73
- for (let i = 0; i < tableGrid.columns.length; i++) {
74
- const col = tableGrid.columns.byIndex(i);
75
- if (col &&
76
- col.name &&
77
- col.column?.semType != 'aminoAcids'
78
- ) {
79
- //@ts-ignore
80
- tableGrid.columns.byIndex(i)?.visible = false;
81
- }
82
- }
83
72
 
84
- const sarViewer = view.addViewer('peptide-sar-viewer', options);
85
- const sarViewerVertical = view.addViewer('peptide-sar-viewer-vertical');
86
- const peptideSpaceViewer = await createPeptideSimilaritySpaceViewer(
87
- currentDf,
88
- col,
89
- 't-SNE',
90
- 'Levenshtein',
91
- 100,
92
- `${activityColumnChoice}Scaled`,
93
- );
94
- let refNode = view.dockManager.dock(peptideSpaceViewer, 'down');
95
- refNode = view.dockManager.dock(sarViewer, 'right', refNode);
96
- view.dockManager.dock(sarViewerVertical, 'right', refNode);
73
+ let peptides = new Peptides();
74
+ await peptides.init(tableGrid, view, currentDf, options, col, activityColumnChoice.value.name);
97
75
 
98
- const StackedBarchartProm = currentDf.plot.fromType('StackedBarChartAA');
99
- addViewerToHeader(tableGrid, StackedBarchartProm);
100
-
101
- // currentDf.onValuesChanged.subscribe(async () => await model.updateDefault());
76
+ progress.close();
102
77
  } else {
103
78
  grok.shell.error('The activity column must be of floating point number type!');
104
79
  }
105
- progress.close();
106
80
  });
107
81
 
108
82
  const viewer = await currentDf.plot.fromType('peptide-logo-viewer');
@@ -2,8 +2,8 @@ import * as ui from 'datagrok-api/ui';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
 
4
4
  import $ from 'cash-dom';
5
- import { model } from '../viewers/model';
6
- import { splitAlignedPeptides } from '../utils/split-aligned';
5
+ import {model} from '../viewers/model';
6
+ import {splitAlignedPeptides} from '../utils/split-aligned';
7
7
 
8
8
  export function manualAlignmentWidget(alignedSequenceCol: DG.Column, currentDf: DG.DataFrame) {
9
9
  const sequenceInput = ui.textInput('', alignedSequenceCol.get(currentDf.currentRowIdx));
@@ -13,12 +13,13 @@ export function manualAlignmentWidget(alignedSequenceCol: DG.Column, currentDf:
13
13
  const applyChangesBtn = ui.button('Apply', async () => {
14
14
  const newSequence = sequenceInput.value;
15
15
  const affectedRowIndex = currentDf.currentRowIdx;
16
- const [splitSequence,] = splitAlignedPeptides(DG.Column.fromStrings('splitSequence', [newSequence]), false);
16
+ const [splitSequence] = splitAlignedPeptides(DG.Column.fromStrings('splitSequence', [newSequence]), false);
17
17
 
18
18
  alignedSequenceCol.set(affectedRowIndex, newSequence);
19
19
  for (const part of splitSequence.columns) {
20
- if (currentDf.col(part.name) !== null)
20
+ if (currentDf.col(part.name) !== null) {
21
21
  currentDf.set(part.name, affectedRowIndex, part.get(0));
22
+ }
22
23
  }
23
24
 
24
25
  await model.updateDefault();
@@ -39,4 +39,4 @@ export async function peptideMoleculeWidget(pep: string): Promise<DG.Widget> {
39
39
  pi.close();
40
40
 
41
41
  return new DG.Widget(ui.div([panel, nglHost]));
42
- }
42
+ }
@@ -1,5 +1,5 @@
1
1
  import {DimensionalityReducer} from '@datagrok-libraries/utils/src/reduce-dimensionality';
2
- import {Coordinates} from '@datagrok-libraries/utils/src/type_declarations';
2
+ import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
3
3
 
4
4
  /**
5
5
  * Worker thread receiving data function.