@datagrok/peptides 0.2.0 → 0.4.3

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,20 +1,20 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
- "version": "0.2.0",
3
+ "version": "0.4.3",
4
4
  "description": "",
5
5
  "dependencies": {
6
6
  "@keckelt/tsne": "^1.0.2",
7
7
  "cash-dom": "latest",
8
8
  "d3": "latest",
9
- "datagrok-api": ">0.95.4",
9
+ "datagrok-api": ">=0.95.11",
10
10
  "dayjs": "latest",
11
11
  "jaro-winkler-typescript": "^1.0.1",
12
12
  "jstat": "^1.9.5",
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.9",
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
  },
@@ -52,4 +52,4 @@
52
52
  "lint": "eslint \"./src/**/*.ts\"",
53
53
  "lint-fix": "eslint \"./src/**/*.ts\" --fix"
54
54
  }
55
- }
55
+ }
package/src/describe.ts CHANGED
@@ -1,15 +1,50 @@
1
- // eslint-disable-next-line no-unused-vars
2
- import * as grok from 'datagrok-api/grok';
3
1
  import * as ui from 'datagrok-api/ui';
4
2
  import * as DG from 'datagrok-api/dg';
5
3
  import {splitAlignedPeptides} from './utils/split-aligned';
6
4
  import {tTest} from '@datagrok-libraries/statistics/src/tests';
7
- import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests.js';
5
+ import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests';
8
6
  import {ChemPalette} from './utils/chem-palette';
9
7
  import {setAARRenderer} from './utils/cell-renderer';
10
8
 
11
9
  const cp = new ChemPalette('grok');
12
10
 
11
+ const aarGroups = {
12
+ 'R': 'PC',
13
+ 'H': 'PC',
14
+ 'K': 'PC',
15
+ 'D': 'NC',
16
+ 'E': 'NC',
17
+ 'S': 'U',
18
+ 'T': 'U',
19
+ 'N': 'U',
20
+ 'Q': 'U',
21
+ 'C': 'SC',
22
+ 'U': 'SC',
23
+ 'G': 'SC',
24
+ 'P': 'SC',
25
+ 'A': 'H',
26
+ 'V': 'H',
27
+ 'I': 'H',
28
+ 'L': 'H',
29
+ 'M': 'H',
30
+ 'F': 'H',
31
+ 'Y': 'H',
32
+ 'W': 'H',
33
+ '-': '-',
34
+ };
35
+
36
+ const groupDescription: {[key: string]: {'description': string, 'aminoAcids': string[]}} = {
37
+ 'PC': {'description': 'Positive Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['R', 'H', 'K']},
38
+ 'NC': {'description': 'Negative Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['D', 'E']},
39
+ 'U': {'description': 'Amino Acids with Polar Uncharged Side Chains', 'aminoAcids': ['S', 'T', 'N', 'Q']},
40
+ 'SC': {'description': 'Special Cases', 'aminoAcids': ['C', 'U', 'G', 'P']},
41
+ 'H': {
42
+ 'description': 'Amino Acids with Hydrophobic Side Chain',
43
+ 'aminoAcids': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
44
+ },
45
+ '-': {'description': 'Unknown Amino Acid', 'aminoAcids': ['-']},
46
+ };
47
+
13
48
  export async function describe(
14
49
  df: DG.DataFrame,
15
50
  activityColumn: string,
@@ -17,7 +52,8 @@ export async function describe(
17
52
  sourceGrid: DG.Grid,
18
53
  twoColorMode: boolean,
19
54
  initialBitset: DG.BitSet | null,
20
- ): Promise<[DG.Grid, DG.Grid, DG.DataFrame]> {
55
+ grouping: boolean,
56
+ ): Promise<[DG.Grid, DG.Grid, DG.DataFrame, {[key: string]: string}]> {
21
57
  //Split the aligned sequence into separate AARs
22
58
  let splitSeqDf: DG.DataFrame | undefined;
23
59
  let invalidIndexes: number[];
@@ -50,6 +86,7 @@ export async function describe(
50
86
  setAARRenderer(col, sourceGrid);
51
87
  }
52
88
  }
89
+
53
90
  if (sourceGrid) {
54
91
  const colNames:string[] = [];
55
92
  for (let i = 0; i < sourceGrid.columns.length; i++) {
@@ -105,6 +142,17 @@ export async function describe(
105
142
 
106
143
  let matrixDf = splitSeqDf.unpivot([activityColumnScaled], positionColumns, positionColName, aminoAcidResidue);
107
144
 
145
+ //TODO: move to chem palette
146
+ let groupMapping: {[key: string]: string} = {};
147
+ if (grouping) {
148
+ groupMapping = aarGroups;
149
+ const aarCol = matrixDf.getCol(aminoAcidResidue);
150
+ aarCol.init((index) => groupMapping[aarCol.get(index)[0]] ?? '-');
151
+ aarCol.compact();
152
+ } else {
153
+ Object.keys(aarGroups).forEach((value) => groupMapping[value] = value);
154
+ }
155
+
108
156
  //statistics for specific AAR at a specific position
109
157
  matrixDf = matrixDf.groupBy([positionColName, aminoAcidResidue])
110
158
  .add('count', activityColumnScaled, 'Count')
@@ -135,14 +183,14 @@ export async function describe(
135
183
  AAR = matrixDf.get(aminoAcidResidue, i);
136
184
 
137
185
  //@ts-ignore
138
- splitSeqDf.rows.select((row) => row[position] === AAR);
186
+ splitSeqDf.rows.select((row) => groupMapping[row[position]] === AAR);
139
187
  currentActivity = splitSeqDf
140
188
  .clone(splitSeqDf.selection, [activityColumnScaled])
141
189
  .getCol(activityColumnScaled)
142
190
  .toList();
143
191
 
144
192
  //@ts-ignore
145
- splitSeqDf.rows.select((row) => row[position] !== AAR);
193
+ splitSeqDf.rows.select((row) => groupMapping[row[position]] !== AAR);
146
194
  otherActivity = splitSeqDf
147
195
  .clone(splitSeqDf.selection, [activityColumnScaled])
148
196
  .getCol(activityColumnScaled)
@@ -190,11 +238,9 @@ export async function describe(
190
238
  aarList.sort((first, second) => getWeight(second) - getWeight(first));
191
239
 
192
240
  matrixDf.getCol(aminoAcidResidue).setCategoryOrder(aarList);
193
- //const sequenceDf = segregateBestAtAllCateg(statsDf, twoColorMode);
194
241
 
195
242
  // SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
196
243
  // TODO: aquire ALL of the positions
197
-
198
244
  let sequenceDf = statsDf.groupBy(['Mean difference', aminoAcidResidue, positionColName, 'Count', 'Ratio', 'pValue'])
199
245
  .where('pValue <= 0.1')
200
246
  .aggregate();
@@ -224,17 +270,14 @@ export async function describe(
224
270
  SARVgrid.col('pValue')!.format = 'four digits after comma';
225
271
  SARVgrid.col('pValue')!.name = 'P-Value';
226
272
 
227
- //FIXME: looks inefficient
228
- for (const col of matrixDf.columns) {
229
- if (col.name === aminoAcidResidue) {
230
- setAARRenderer(col, SARgrid);
231
- break;
273
+ if (!grouping) {
274
+ let tempCol = matrixDf.columns.byName(aminoAcidResidue);
275
+ if (tempCol) {
276
+ setAARRenderer(tempCol, SARgrid);
232
277
  }
233
- }
234
- for (const col of sequenceDf.columns) {
235
- if (col.name === aminoAcidResidue) {
236
- setAARRenderer(col, SARVgrid);
237
- break;
278
+ tempCol = sequenceDf.columns.byName(aminoAcidResidue);
279
+ if (tempCol) {
280
+ setAARRenderer(tempCol, SARgrid);
238
281
  }
239
282
  }
240
283
 
@@ -251,19 +294,6 @@ export async function describe(
251
294
  return;
252
295
  }
253
296
 
254
- // if (args.cell.isColHeader) {
255
- // if (args.cell.gridColumn.name != aminoAcidResidue) {
256
- // const textSize = args.g.measureText(args.cell.gridColumn.name);
257
- // args.g.fillStyle = '#4b4b4a';
258
- // args.g.fillText(
259
- // args.cell.gridColumn.name,
260
- // args.bounds.x + (args.bounds.width - textSize.width) / 2,
261
- // args.bounds.y + (textSize.actualBoundingBoxAscent + textSize.actualBoundingBoxDescent),
262
- // );
263
- // }
264
- // args.preventDefault();
265
- // }
266
-
267
297
  if (
268
298
  args.cell.isTableCell &&
269
299
  args.cell.tableRowIndex !== null &&
@@ -357,12 +387,18 @@ export async function describe(
357
387
  }
358
388
  if (
359
389
  !cell.isColHeader &&
360
- cell.tableColumn !== null &&
361
- cell.tableColumn.name == aminoAcidResidue &&
362
- cell.cell.value !== null &&
363
- cell.tableRowIndex !== null
390
+ cell.tableColumn !== null &&
391
+ cell.tableColumn.name == aminoAcidResidue &&
392
+ cell.cell.value !== null &&
393
+ cell.tableRowIndex !== null
364
394
  ) {
365
- cp.showTooltip(cell, x, y);
395
+ if (grouping) {
396
+ const currentGroup = groupDescription[cell.cell.value];
397
+ const divText = ui.divText('Amino Acids in this group: ' + currentGroup['aminoAcids'].join(', '));
398
+ ui.tooltip.show(ui.divV([ui.h3(currentGroup['description']), divText]), x, y);
399
+ } else {
400
+ cp.showTooltip(cell, x, y);
401
+ }
366
402
  }
367
403
  return true;
368
404
  };
@@ -376,9 +412,14 @@ export async function describe(
376
412
  }
377
413
  });
378
414
 
379
- // for (const col of matrixDf.columns.names()) {
380
- // SARgrid.col(col)!.width = SARgrid.props.rowHeight;
381
- // }
415
+ for (const col of matrixDf.columns.names()) {
416
+ SARgrid.col(col)!.width = SARgrid.props.rowHeight;
417
+ }
418
+
419
+ if (grouping) {
420
+ SARgrid.col(aminoAcidResidue)!.name = 'Groups';
421
+ SARVgrid.col(aminoAcidResidue)!.name = 'Groups';
422
+ }
382
423
 
383
- return [SARgrid, SARVgrid, statsDf];
424
+ return [SARgrid, SARVgrid, statsDf, groupMapping];
384
425
  }
package/src/package.ts CHANGED
@@ -14,7 +14,8 @@ import {analyzePeptidesWidget} from './widgets/analyze-peptides';
14
14
  import {PeptideSimilaritySpaceWidget} from './utils/peptide-similarity-space';
15
15
  import {manualAlignmentWidget} from './widgets/manual-alignment';
16
16
  import {SARViewer, SARViewerVertical} from './viewers/sar-viewer';
17
- import { peptideMoleculeWidget } from './widgets/peptide-molecule';
17
+ import {peptideMoleculeWidget} from './widgets/peptide-molecule';
18
+ import {correlationAnalysisPlots} from './utils/correlation-analysis';
18
19
 
19
20
  export const _package = new DG.Package();
20
21
  let tableGrid: DG.Grid;
@@ -24,35 +25,13 @@ let view: DG.TableView;
24
25
 
25
26
  async function main(chosenFile: string) {
26
27
  const pi = DG.TaskBarProgressIndicator.create('Loading Peptides');
27
- //let peptides =
28
- // await grok.data.loadTable('https://datagrok.jnj.com/p/ejaeger.il23peptideidp5562/il-23_peptide_idp-5562');
29
28
  const path = _package.webRoot + 'files/' + chosenFile;
30
29
  const peptides = (await grok.data.loadTable(path));
31
30
  peptides.name = 'Peptides';
32
31
  peptides.setTag('dataType', 'peptides');
33
32
  const view = grok.shell.addTableView(peptides);
34
33
  tableGrid = view.grid;
35
- // peptides.onSemanticTypeDetecting.subscribe((_: any) => {
36
- // const regexp = new RegExp(/^([^-^\n]*-){2,49}(\w|\(|\))+$/);
37
- // for (const col of peptides.columns) {
38
- // col.semType = DG.Detector.sampleCategories(col, (s: any) => regexp.test(s.trim())) ? 'alignedSequence' : null;
39
- // if (col.semType == 'alignedSequence') {
40
- // expandColumn(col, tableGrid, (ent)=>{
41
- // const subParts:string[] = ent.split('-');
42
- // // eslint-disable-next-line no-unused-vars
43
- // const [text, _] = processSequence(subParts);
44
- // let textSize = 0;
45
- // text.forEach((aar)=>{
46
- // textSize += aar.length;
47
- // });
48
- // return textSize;
49
- // });
50
- // }
51
- // }
52
- // });
53
-
54
34
  view.name = 'PeptidesView';
55
-
56
35
  grok.shell.windows.showProperties = true;
57
36
 
58
37
  pi.close();
@@ -66,7 +45,6 @@ export function Peptides() {
66
45
 
67
46
  const appDescription = ui.info(
68
47
  [
69
- // ui.divText('\n To start the application :', {style: {'font-weight': 'bolder'}}),
70
48
  ui.list([
71
49
  '- automatic recognition of peptide sequences',
72
50
  '- native integration with tons of Datagrok out-of-the box features (visualization, filtering, clustering, ' +
@@ -196,6 +174,18 @@ export function manualAlignment(monomer: string) {
196
174
  //input: column col {semType: alignedSequence}
197
175
  //output: widget result
198
176
  export async function peptideSpacePanel(col: DG.Column): Promise<DG.Widget> {
199
- const widget = new PeptideSimilaritySpaceWidget(col);
177
+ const widget = new PeptideSimilaritySpaceWidget(col, view ?? grok.shell.v);
200
178
  return await widget.draw();
201
179
  }
180
+
181
+ //name: Correllation analysis
182
+ export async function correlationAnalysis() {
183
+ view = (grok.shell.v as DG.TableView);
184
+
185
+ const df = await grok.data.files.openTable('Demo:TestJobs:Files:DemoFiles/bio/peptides.csv');
186
+ const tview = grok.shell.addTableView(df);
187
+ const [cpviewer, bpviewer] = correlationAnalysisPlots(df.getCol('AlignedSequence'));
188
+
189
+ tview.dockManager.dock(cpviewer, 'right');
190
+ tview.dockManager.dock(bpviewer, 'down');
191
+ }
@@ -0,0 +1,76 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import * as DG from 'datagrok-api/dg';
3
+ import {createPeptideSimilaritySpaceViewer} from './utils/peptide-similarity-space';
4
+ import {addViewerToHeader} from './viewers/stacked-barchart-viewer';
5
+
6
+ export class Peptides {
7
+ async init(
8
+ tableGrid: DG.Grid,
9
+ view: DG.TableView,
10
+ currentDf: DG.DataFrame,
11
+ options: {[key: string]: string},
12
+ col: DG.Column,
13
+ activityColumnChoice: string,
14
+ ) {
15
+ for (let i = 0; i < tableGrid.columns.length; i++) {
16
+ const col = tableGrid.columns.byIndex(i);
17
+ if (col &&
18
+ col.name &&
19
+ col.column?.semType != 'aminoAcids'
20
+ ) {
21
+ //@ts-ignore
22
+ tableGrid.columns.byIndex(i)?.visible = false;
23
+ }
24
+ }
25
+
26
+ const originalDfColumns = (currentDf.columns as DG.ColumnList).names();
27
+
28
+ const sarViewer = view.addViewer('peptide-sar-viewer', options);
29
+ const sarNode = view.dockManager.dock(sarViewer, DG.DOCK_TYPE.DOWN, null, 'SAR Viewer');
30
+
31
+ const sarViewerVertical = view.addViewer('peptide-sar-viewer-vertical');
32
+ view.dockManager.dock(sarViewerVertical, DG.DOCK_TYPE.RIGHT, sarNode, 'SAR Vertical Viewer');
33
+
34
+ const peptideSpaceViewer = await createPeptideSimilaritySpaceViewer(
35
+ currentDf,
36
+ col,
37
+ 't-SNE',
38
+ 'Levenshtein',
39
+ 100,
40
+ view,
41
+ `${activityColumnChoice}Scaled`,
42
+ );
43
+ view.dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.LEFT, sarNode, 'Peptide Space Viewer', 0.3);
44
+
45
+ const StackedBarchartProm = currentDf.plot.fromType('StackedBarChartAA');
46
+ addViewerToHeader(tableGrid, StackedBarchartProm);
47
+
48
+ const hideIcon = ui.iconFA('window-close', () => { //undo?, times?
49
+ const viewers = [];
50
+ for (const viewer of view.viewers) {
51
+ if (viewer.type !== DG.VIEWER.GRID) {
52
+ viewers.push(viewer);
53
+ }
54
+ }
55
+ viewers.forEach((v) => v.close());
56
+
57
+ const cols = (currentDf.columns as DG.ColumnList);
58
+ for (const colName of cols.names()) {
59
+ if (!originalDfColumns.includes(colName)) {
60
+ cols.remove(colName);
61
+ }
62
+ }
63
+
64
+ currentDf.selection.setAll(false);
65
+ currentDf.filter.setAll(true);
66
+
67
+ tableGrid.setOptions({'colHeaderHeight': 20});
68
+ tableGrid.columns.setVisible(originalDfColumns);
69
+
70
+ view.setRibbonPanels(ribbonPanels);
71
+ }, 'Close viewers and restore dataframe');
72
+
73
+ const ribbonPanels = view.getRibbonPanels();
74
+ view.setRibbonPanels([[hideIcon]]);
75
+ }
76
+ }
@@ -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])];
@@ -24,15 +23,19 @@ export class ChemPalette {
24
23
  if (s in ChemPalette.AAFullNames) {
25
24
  toDisplay = [ui.divText(ChemPalette.AANames[ChemPalette.AAFullNames[s]])];
26
25
  }
27
- const sketch = grok.chem.svgMol(ChemPalette.AASmiles[aar]);
26
+ const options = {
27
+ autoCrop: true,
28
+ autoCropMargin: 0,
29
+ suppressChiralText: true,
30
+ };
31
+ const sketch = grok.chem.svgMol(ChemPalette.AASmiles[aar], undefined, undefined, options);
28
32
  toDisplay.push(sketch);
29
33
  }
30
34
  ui.tooltip.show(ui.divV(toDisplay), x, y);
31
35
  }
32
36
 
33
37
  getColor( c: string) {
34
- // eslint-disable-next-line no-unused-vars
35
- const [color, _] = this.getColorPivot(c);
38
+ const [color] = this.getColorPivot(c);
36
39
  return color;
37
40
  }
38
41
 
@@ -77,8 +80,7 @@ export class ChemPalette {
77
80
  }
78
81
 
79
82
  getColorPivot(c = ''): [string, number] {
80
- // eslint-disable-next-line no-unused-vars
81
- const [color, _, pivot] = this.getColorAAPivot(c);
83
+ const [color,, pivot] = this.getColorAAPivot(c);
82
84
  return [color, pivot];
83
85
  };
84
86
 
@@ -105,14 +107,6 @@ export class ChemPalette {
105
107
  'white': ['rgb(230,230,230)'],
106
108
  }
107
109
 
108
- // static grokGroups: [string[], string][] = [
109
- // [['C', 'U'], 'yellow'],
110
- // [['G', 'P'], 'red'],
111
- // [['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'], 'all_green'],
112
- // [['R', 'H', 'K'], 'light_blue'],
113
- // [['D', 'E'], 'dark_blue'],
114
- // [['S', 'T', 'N', 'Q'], 'orange'],
115
- // ];
116
110
  static grokGroups: {[key: string]: string[]} = {
117
111
  'yellow': ['C', 'U'],
118
112
  'red': ['G', 'P'],
@@ -130,14 +124,17 @@ export class ChemPalette {
130
124
  'all_blue': ['K', 'R'],
131
125
  }
132
126
 
133
- static undefinedColor = 'rgb(100,100,100)'
127
+ static undefinedColor = 'rgb(100,100,100)';
134
128
 
135
- static makePalette(dt: {[key: string]: string[]}, simplified = false) {
129
+ static makePalette(dt: {[key: string]: string[]}, simplified = false, grouping = false) {
136
130
  const palette: { [key: string]: string } = {};
131
+ const groups = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
132
+ let currentGroup = 0;
137
133
  for (const [color, monomers] of Object.entries(dt)) {
138
134
  monomers.forEach((monomer, index) => {
139
- palette[monomer] = ChemPalette.colourPalette[color][simplified ? 0 : index];
135
+ palette[grouping ? groups[currentGroup] : monomer] = ChemPalette.colourPalette[color][simplified ? 0 : index];
140
136
  });
137
+ currentGroup++;
141
138
  }
142
139
  return palette;
143
140
  }
@@ -234,8 +231,8 @@ export class ChemPalette {
234
231
  'Val': 'V',
235
232
  }
236
233
 
237
- static getDatagrok() {
238
- return ChemPalette.makePalette(ChemPalette.grokGroups);
234
+ static getDatagrok(grouping = false) {
235
+ return ChemPalette.makePalette(ChemPalette.grokGroups, false, grouping);
239
236
  }
240
237
 
241
238
  static getLesk() {
@@ -0,0 +1,123 @@
1
+ /* Do not change these import lines. Datagrok will import API library in exactly the same manner */
2
+ import * as DG from 'datagrok-api/dg';
3
+
4
+ import {AlignedSequenceEncoder} from '@datagrok-libraries/utils/src/sequence-encoder';
5
+ import {assert, transposeMatrix} from '@datagrok-libraries/utils/src/operations';
6
+ import {Vector, Matrix} from '@datagrok-libraries/utils/src/type-declarations';
7
+ import {kendallsTau} from '@datagrok-libraries/statistics/src/correlation-coefficient';
8
+
9
+ /**
10
+ * Converts a Matrix into a DataFrame.
11
+ *
12
+ * @export
13
+ * @param {Matrix} matrix A matrix.
14
+ * @return {DG.DataFrame} The data frame.
15
+ */
16
+ export function matrix2DataFrame(matrix: Matrix): DG.DataFrame {
17
+ return DG.DataFrame.fromColumns(matrix.map((v, i) => DG.Column.fromFloat32Array(`${i+1}`, v)));
18
+ }
19
+
20
+ /**
21
+ * Encodes amino acid sequences into a numeric representation.
22
+ *
23
+ * @param {DG.Column} col A column containing the sequences.
24
+ * @return {DG.DataFrame} The resulting data frame.
25
+ */
26
+ function calcPositions(col: DG.Column): DG.DataFrame {
27
+ const sequences = col.toList().map((v, _) => AlignedSequenceEncoder.clean(v));
28
+ const enc = new AlignedSequenceEncoder();
29
+ const encSeqs = sequences.map((v) => Vector.from(enc.encode(v)));
30
+ const positions = transposeMatrix(encSeqs);
31
+ return matrix2DataFrame(positions);
32
+ }
33
+
34
+ /**
35
+ * Unfolds a data frame into <category>-<value> format.
36
+ *
37
+ * @param {DG.DataFrame} df A data frame to unfold.
38
+ * @return {DG.DataFrame} The resulting data frame.
39
+ */
40
+ function melt(df: DG.DataFrame): DG.DataFrame {
41
+ let keys: string[] = [];
42
+ const values: Float32Array = new Float32Array(df.columns.length*df.rowCount);
43
+ let i = 0;
44
+
45
+ for (const c of df.columns.toList()) {
46
+ keys = keys.concat(Array<string>(c.length).fill(c.name));
47
+ values.set(c.getRawData(), i);
48
+ i += df.rowCount;
49
+ }
50
+ assert(keys.length == values.length);
51
+ return DG.DataFrame.fromColumns([DG.Column.fromStrings('keys', keys), DG.Column.fromFloat32Array('values', values)]);
52
+ }
53
+
54
+ /**
55
+ * Calculates Spearman's rho rank correlation coefficient.
56
+ *
57
+ * @param {DG.DataFrame} df A data frame to process.
58
+ * @return {DG.DataFrame} The correlation matrix.
59
+ */
60
+ function calcSpearmanRhoMatrix(df: DG.DataFrame): DG.DataFrame {
61
+ const nItems = df.columns.length;
62
+ const rho = new Array(nItems).fill(0).map((_) => new Float32Array(nItems).fill(0));
63
+
64
+ for (let i = 0; i < nItems; ++i) {
65
+ for (let j = i+1; j < nItems; ++j) {
66
+ rho[i][j] = df.columns.byIndex(i).stats.spearmanCorr(df.columns.byIndex(j));
67
+ rho[j][i] = rho[i][j];
68
+ }
69
+ }
70
+ return matrix2DataFrame(rho);
71
+ }
72
+
73
+ /**
74
+ * Calculates Kendall's tau rank correlation coefficient.
75
+ *
76
+ * @param {DG.DataFrame} df A data frame to process.
77
+ * @param {number} [alpha=0.05] The significance threshold.
78
+ * @return {DG.DataFrame} The correlation matrix.
79
+ */
80
+ function calcKendallTauMatrix(df: DG.DataFrame, alpha: number = 0.05): DG.DataFrame {
81
+ const nItems = df.columns.length;
82
+ const tau = new Array(nItems).fill(0).map((_) => new Float32Array(nItems).fill(0));
83
+
84
+ for (let i = 0; i < nItems; ++i) {
85
+ for (let j = i+1; j < nItems; ++j) {
86
+ const res = kendallsTau(df.columns.byIndex(i).getRawData(), df.columns.byIndex(j).getRawData());
87
+ tau[i][j] = res.prob < alpha ? res.test : 0;
88
+ tau[j][i] = tau[i][j];
89
+ }
90
+ }
91
+ return matrix2DataFrame(tau);
92
+ }
93
+
94
+ /**
95
+ * Creates acorrelation plot and a box plot to perform correlation analysis.
96
+ *
97
+ * @export
98
+ * @param {DG.Column} sequencesColumn A column containing amino acid sequences.
99
+ * @return {[DG.Viewer, DG.Viewer]} These two plots.
100
+ */
101
+ export function correlationAnalysisPlots(sequencesColumn: DG.Column): [DG.Viewer, DG.Viewer] {
102
+ const posDF = calcPositions(sequencesColumn);
103
+ const cpviewer = DG.Viewer.fromType(
104
+ DG.VIEWER.CORR_PLOT,
105
+ posDF,
106
+ {
107
+ 'xColumnNames': posDF.columns.names(),
108
+ 'yColumnNames': posDF.columns.names(),
109
+ 'correlationType': 'Spearman',
110
+ });
111
+
112
+ const rhoDF = calcKendallTauMatrix(posDF);
113
+ const meltDF = melt(rhoDF);
114
+
115
+ const bpviewer = DG.Viewer.fromType(
116
+ DG.VIEWER.BOX_PLOT,
117
+ meltDF, {
118
+ 'categoryColumnName': 'keys',
119
+ 'valueColumnName': 'values',
120
+ 'statistics': ['min', 'max', 'avg', 'med'],
121
+ });
122
+ return [cpviewer, bpviewer];
123
+ }
@@ -1,6 +1,4 @@
1
1
  /* Do not change these import lines. Datagrok will import API library in exactly the same manner */
2
- // eslint-disable-next-line no-unused-vars
3
- import * as grok from 'datagrok-api/grok';
4
2
  import * as ui from 'datagrok-api/ui';
5
3
  import * as DG from 'datagrok-api/dg';
6
4
 
@@ -8,17 +6,8 @@ import {getSequenceMolecularWeight} from './molecular-measure';
8
6
  import {AlignedSequenceEncoder} from '@datagrok-libraries/utils/src/sequence-encoder';
9
7
  import {DimensionalityReducer} from '@datagrok-libraries/utils/src/reduce-dimensionality';
10
8
  import {Measurer} from '@datagrok-libraries/utils/src/string-measure';
11
- import {Coordinates} from '@datagrok-libraries/utils/src/type_declarations';
9
+ import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
12
10
 
13
- /**
14
- * Creates a worker to perform a dimensionality reduction.
15
- *
16
- * @param {any[]} columnData Samples to process.
17
- * @param {string} method Embedding method.
18
- * @param {string} measure Distance metric.
19
- * @param {number} cyclesCount Number of cycles to repeat.
20
- * @return {Promise<unknown>} Promise.
21
- */
22
11
  function createDimensinalityReducingWorker(
23
12
  columnData: any[],
24
13
  method: string,
@@ -65,6 +54,7 @@ function inferActivityColumnsName(table: DG.DataFrame): string | null {
65
54
  * @param {string} method Embedding method to apply.
66
55
  * @param {string} measure Distance metric.
67
56
  * @param {number} cyclesCount Number of cycles to repeat.
57
+ * @param {(DG.TableView | null)} view View to add scatter plot to
68
58
  * @param {(string | null)} [activityColumnName] Activity containing column to assign it to points radius.
69
59
  * @param {boolean} [zoom=false] Whether to fit view.
70
60
  * @return {Promise<DG.ScatterPlotViewer>} A viewer.
@@ -75,6 +65,7 @@ export async function createPeptideSimilaritySpaceViewer(
75
65
  method: string,
76
66
  measure: string,
77
67
  cyclesCount: number,
68
+ view: DG.TableView | null,
78
69
  activityColumnName?: string | null,
79
70
  zoom: boolean = false,
80
71
  ): Promise<DG.ScatterPlotViewer> {
@@ -117,8 +108,11 @@ export async function createPeptideSimilaritySpaceViewer(
117
108
  table.columns.replace(col, edf.getCol(axis));
118
109
  }
119
110
  }
120
- const viewer = DG.Viewer.scatterPlot(table, {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'});
121
111
 
112
+ // const viewer = DG.Viewer.scatterPlot(table, {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'});
113
+ const viewerOptions = {x: '~X', y: '~Y', color: activityColumnName ?? '~MW', size: '~MW'};
114
+ const viewer = view !== null ?
115
+ view.addViewer(DG.VIEWER.SCATTER_PLOT, viewerOptions) : DG.Viewer.scatterPlot(table, viewerOptions);
122
116
  // Fit view if needed.
123
117
  /*if (zoom) {
124
118
  viewer.zoom(
@@ -129,7 +123,7 @@ export async function createPeptideSimilaritySpaceViewer(
129
123
  );
130
124
  }*/
131
125
  pi.close();
132
- return viewer;
126
+ return (viewer as DG.ScatterPlotViewer);
133
127
  }
134
128
 
135
129
  /**
@@ -147,13 +141,15 @@ export class PeptideSimilaritySpaceWidget {
147
141
  protected availableMethods: string[];
148
142
  protected availableMetrics: string[];
149
143
  protected viewer: HTMLElement;
144
+ view: DG.TableView;
150
145
 
151
146
  /**
152
147
  * Creates an instance of PeptideSimilaritySpaceWidget.
153
148
  * @param {DG.Column} alignedSequencesColumn The column to get amino acid sequences from.
149
+ * @param {DG.TableView} view Current view
154
150
  * @memberof PeptideSimilaritySpaceWidget
155
151
  */
156
- constructor(alignedSequencesColumn: DG.Column) {
152
+ constructor(alignedSequencesColumn: DG.Column, view: DG.TableView) {
157
153
  this.availableMethods = DimensionalityReducer.availableMethods;
158
154
  this.availableMetrics = Measurer.availableMeasures;
159
155
  this.method = this.availableMethods[0];
@@ -161,6 +157,7 @@ export class PeptideSimilaritySpaceWidget {
161
157
  this.currentDf = alignedSequencesColumn.dataFrame;
162
158
  this.alignedSequencesColumn = alignedSequencesColumn;
163
159
  this.viewer = ui.div([]);
160
+ this.view = view;
164
161
  }
165
162
 
166
163
  /**
@@ -177,6 +174,7 @@ export class PeptideSimilaritySpaceWidget {
177
174
  this.metrics,
178
175
  this.cycles,
179
176
  null,
177
+ null,
180
178
  true,
181
179
  );
182
180
  viewer.root.style.width = 'auto';
@@ -45,7 +45,6 @@ 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
-
49
48
  if (filter) {
50
49
  splitColumns = splitColumns.filter((positionArray, index) => {
51
50
  const isRetained = new Set(positionArray).size > 1;
@@ -61,7 +61,6 @@ export class Logo extends DG.JsViewer {
61
61
 
62
62
  init() {
63
63
  this.initialized = true;
64
- // this.reactHost = ui.div([]);
65
64
  console.log('INIT');
66
65
  this.target = this.dataFrame;
67
66
  [this.splitted] = splitAlignedPeptides(this.dataFrame!.columns.bySemType(this.colSemType));
@@ -111,10 +110,6 @@ export class Logo extends DG.JsViewer {
111
110
 
112
111
  if (typeof this.dataFrame !== 'undefined') {
113
112
  this.findLogo();
114
-
115
- // if (this.reactHost !== null) {
116
- // this.root.appendChild(this.reactHost);
117
- // }
118
113
  }
119
114
  }
120
115
 
@@ -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,18 @@ 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
- // private df: DG.DataFrame | null;
23
+ grouping: boolean;
24
+ groupMapping: {[key: string]: string} | null;
24
25
  // protected pValueThreshold: number;
25
26
  // protected amountOfBestAARs: number;
26
27
  // duplicatesHandingMethod: string;
27
28
  constructor() {
28
29
  super();
29
-
30
+
30
31
  this.viewerGrid = null;
31
32
  this.viewerVGrid = null;
32
33
  this.statsDf = null;
34
+ this.groupMapping = null;
33
35
  this.initialized = false;
34
36
  this.aminoAcidResidue = 'AAR';
35
37
  this._initialBitset = null;
@@ -41,6 +43,7 @@ export class SARViewer extends DG.JsViewer {
41
43
  this.activityScalingMethod = this.string('activityScalingMethod', 'none', {choices: ['none', 'lg', '-lg']});
42
44
  this.filterMode = this.bool('filterMode', false);
43
45
  this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
46
+ this.grouping = this.bool('grouping', false);
44
47
  // this.pValueThreshold = this.float('pValueThreshold', 0.1);
45
48
  // this.amountOfBestAARs = this.int('amountOfBestAAR', 1);
46
49
  // this.duplicatesHandingMethod = this.string('duplicatesHandlingMethod', 'median', {choices: ['median']});
@@ -51,12 +54,13 @@ export class SARViewer extends DG.JsViewer {
51
54
  init() {
52
55
  this._initialBitset = this.dataFrame!.filter.clone();
53
56
  this.initialized = true;
54
- this.subs.push(model.statsDf$.subscribe(data => this.statsDf = data));
55
- this.subs.push(model.viewerGrid$.subscribe(data => {
57
+ this.subs.push(model.statsDf$.subscribe((data) => this.statsDf = data));
58
+ this.subs.push(model.viewerGrid$.subscribe((data) => {
56
59
  this.viewerGrid = data;
57
60
  this.render();
58
61
  }));
59
- this.subs.push(model.viewerVGrid$.subscribe(data => this.viewerVGrid = data));
62
+ this.subs.push(model.viewerVGrid$.subscribe((data) => this.viewerVGrid = data));
63
+ this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
60
64
  }
61
65
 
62
66
  onTableAttached() {
@@ -114,25 +118,13 @@ export class SARViewer extends DG.JsViewer {
114
118
  splitCol = this.dataFrame.columns.addNew(splitColName, 'string');
115
119
  }
116
120
 
117
- const isChosen = (i: number) => this.dataFrame!.get(currentPosition, i) === currentAAR;
121
+ const isChosen = (i: number) => this.groupMapping![this.dataFrame!.get(currentPosition, i)] === currentAAR;
118
122
  splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
119
123
 
120
124
  //TODO: use column.compact
121
-
122
- // if (this.filterMode) {
123
- // this.dataFrame.selection.setAll(false, false);
124
- // this.dataFrame.filter.init(isChosen).and(this._initialBitset!, false);
125
- // } else {
126
- // this.dataFrame.filter.copyFrom(this._initialBitset!);
127
- // this.dataFrame.selection.init(isChosen).and(this._initialBitset!, false);
128
- // }
129
125
  this.currentBitset = DG.BitSet.create(this.dataFrame.rowCount, isChosen).and(this._initialBitset!);
130
- // (this.filterMode ? this.dataFrame.selection.setAll(false) :
131
- // this.dataFrame.filter.copyFrom(this._initialBitset!)).fireChanged();
132
126
  this.sourceFilteringFunc();
133
127
 
134
-
135
- // df.getCol(splitColName).setCategoryOrder([otherLabel, aarLabel]);
136
128
  const colorMap: {[index: string]: string | number} = {};
137
129
  colorMap[otherLabel] = DG.Color.blue;
138
130
  colorMap[aarLabel] = DG.Color.orange;
@@ -262,14 +254,6 @@ export class SARViewer extends DG.JsViewer {
262
254
  //TODO: optimize. Don't calculate everything again if only view changes
263
255
  if (computeData) {
264
256
  if (typeof this.dataFrame !== 'undefined' && this.activityColumnColumnName && this.sourceGrid) {
265
- // [this.viewerGrid, this.viewerVGrid, this.statsDf] = await describe(
266
- // this.dataFrame,
267
- // this.activityColumnColumnName,
268
- // this.activityScalingMethod,
269
- // this.sourceGrid,
270
- // this.bidirectionalAnalysis,
271
- // this._initialBitset,
272
- // );
273
257
  await model?.updateData(
274
258
  this.dataFrame!,
275
259
  this.activityColumnColumnName,
@@ -277,6 +261,7 @@ export class SARViewer extends DG.JsViewer {
277
261
  this.sourceGrid,
278
262
  this.bidirectionalAnalysis,
279
263
  this._initialBitset,
264
+ this.grouping,
280
265
  );
281
266
 
282
267
  if (this.viewerGrid !== null && this.viewerVGrid !== null) {
@@ -303,7 +288,7 @@ export class SARViewerVertical extends DG.JsViewer {
303
288
  super();
304
289
 
305
290
  this.viewerVGrid = null;
306
- this.subs.push(model.viewerVGrid$.subscribe(data => {
291
+ this.subs.push(model.viewerVGrid$.subscribe((data) => {
307
292
  this.viewerVGrid = data;
308
293
  this.render();
309
294
  }));
@@ -316,4 +301,4 @@ export class SARViewerVertical extends DG.JsViewer {
316
301
  }
317
302
  this.viewerVGrid?.invalidate();
318
303
  }
319
- }
304
+ }
@@ -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();
@@ -287,8 +287,7 @@ export class StackedBarChart extends DG.JsViewer {
287
287
  if (h * margin / 2 <= sBarHeight - gapSize && h * margin / 2 <= w) {
288
288
  g.fillStyle = 'rgb(0,0,0)';
289
289
  g.font = `${h * margin / 2}px`;
290
- // eslint-disable-next-line no-unused-vars
291
- const [_c, aar, _p] = cp.getColorAAPivot(obj['name']);
290
+ const [, aar] = cp.getColorAAPivot(obj['name']);
292
291
  g.fillText(aar,
293
292
  x + w / 2 - h * margin / 8,
294
293
  y + h * (this.max - sum + curSum) / this.max + gapSize / 2 + (sBarHeight - gapSize)/2 - h * margin / 8);
@@ -398,11 +397,8 @@ export class StackedBarChart extends DG.JsViewer {
398
397
  return;
399
398
  }
400
399
  this.dataFrame!.selection.handleClick((i) => {
401
- //let selected = true;
402
400
  // @ts-ignore
403
401
  return this.highlighted!['aaName'] === (this.dataFrame.getCol(this.highlighted!['colName']).get(i));
404
-
405
- //&& (scope.dataFrame.selection.get(i) === selected);
406
402
  }, event);
407
403
  }
408
404
  }
@@ -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
+ const 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.