@datagrok/peptides 1.4.0 → 1.5.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.
@@ -3,6 +3,7 @@ import {runTests, tests, TestContext} from '@datagrok-libraries/utils/src/test';
3
3
 
4
4
  import './tests/core';
5
5
  import './tests/peptide-space-test';
6
+ import './tests/algorithms';
6
7
 
7
8
  export const _package = new DG.Package();
8
9
  export {tests};
package/src/package.ts CHANGED
@@ -2,12 +2,11 @@
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
- import * as C from './utils/constants';
6
5
 
7
6
  import {analyzePeptidesUI} from './widgets/peptides';
8
7
  import {PeptideSimilaritySpaceWidget} from './utils/peptide-similarity-space';
9
8
  import {manualAlignmentWidget} from './widgets/manual-alignment';
10
- import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-viewer';
9
+ import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
11
10
 
12
11
  import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
13
12
  import {LogoSummary} from './viewers/logo-summary';
@@ -19,7 +18,7 @@ export const _package = new DG.Package();
19
18
  let currentTable: DG.DataFrame;
20
19
  let alignedSequenceColumn: DG.Column;
21
20
 
22
- export function getMonomerWorks() {
21
+ export function getMonomerWorks(): MonomerWorks | null {
23
22
  return monomerWorks;
24
23
  };
25
24
 
@@ -41,7 +40,7 @@ export async function Peptides(): Promise<void> {
41
40
  const wikiLink = ui.link('wiki', 'https://github.com/datagrok-ai/public/blob/master/help/domains/bio/peptides.md');
42
41
  const textLink = ui.inlineText(['For more details, see our ', wikiLink, '.']);
43
42
  if (monomerWorks == null) {
44
- let lib = await grok.functions.call('Bio:getBioLib');
43
+ const lib = await grok.functions.call('Bio:getBioLib');
45
44
  monomerWorks = new MonomerWorks(lib);
46
45
  }
47
46
  const appDescription = ui.info(
@@ -82,10 +81,13 @@ export async function Peptides(): Promise<void> {
82
81
 
83
82
  //top-menu: Bio | Peptides...
84
83
  //name: Bio Peptides
85
- export async function peptidesDialog(): Promise<DG.Dialog> {
86
- const analyzeObject = await analyzePeptidesUI(grok.shell.t);
87
- const dialog = ui.dialog('Analyze Peptides').add(analyzeObject.host).onOK(analyzeObject.callback);
88
- dialog.show();
84
+ export function peptidesDialog(): DG.Dialog {
85
+ const analyzeObject = analyzePeptidesUI(grok.shell.t);
86
+ const dialog = ui.dialog('Analyze Peptides').add(analyzeObject.host).onOK(async () => {
87
+ const startSuccess = analyzeObject.callback();
88
+ if (!startSuccess)
89
+ dialog.show();
90
+ });
89
91
  return dialog.show();
90
92
  }
91
93
 
@@ -93,9 +95,9 @@ export async function peptidesDialog(): Promise<DG.Dialog> {
93
95
  //tags: panel, widgets
94
96
  //input: column col {semType: Macromolecule}
95
97
  //output: widget result
96
- export async function peptidesPanel(col: DG.Column): Promise<DG.Widget> {
98
+ export function peptidesPanel(col: DG.Column): DG.Widget {
97
99
  [currentTable, alignedSequenceColumn] = getOrDefine(col.dataFrame, col);
98
- const analyzeObject = await analyzePeptidesUI(currentTable, alignedSequenceColumn);
100
+ const analyzeObject = analyzePeptidesUI(currentTable, alignedSequenceColumn);
99
101
  return new DG.Widget(analyzeObject.host);
100
102
  }
101
103
 
@@ -103,8 +105,8 @@ export async function peptidesPanel(col: DG.Column): Promise<DG.Widget> {
103
105
  //description: Peptides SAR Viewer
104
106
  //tags: viewer
105
107
  //output: viewer result
106
- export function sar(): MutationCliffsViewer {
107
- return new MutationCliffsViewer();
108
+ export function sar(): MonomerPosition {
109
+ return new MonomerPosition();
108
110
  }
109
111
 
110
112
  //name: peptide-sar-viewer-vertical
@@ -174,8 +176,7 @@ export function getPeptidesStructure(col: DG.Column): DG.Widget {
174
176
 
175
177
  function getOrDefine(dataframe?: DG.DataFrame, column?: DG.Column | null): [DG.DataFrame, DG.Column] {
176
178
  dataframe ??= grok.shell.t;
177
- // column ??= dataframe.columns.bySemType(C.SEM_TYPES.MACROMOLECULE);
178
- column ??= dataframe.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
179
+ column ??= dataframe.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!;
179
180
  if (column === null)
180
181
  throw new Error('Table does not contain aligned sequence columns');
181
182
 
@@ -1,29 +1,27 @@
1
- import * as grok from 'datagrok-api/grok';
2
1
  import * as DG from 'datagrok-api/dg';
3
2
 
4
- import {category, test, expect, delay, before} from '@datagrok-libraries/utils/src/test';
3
+ import {category, test, expect, before} from '@datagrok-libraries/utils/src/test';
5
4
 
6
5
  import {_package} from '../package-test';
7
- import {startAnalysis} from '../widgets/peptides';
8
- import {PeptidesModel} from '../model';
9
- import * as C from '../utils/constants';
10
- import {scaleActivity} from '../utils/misc';
11
- import {ALPHABET, TAGS, NOTATION, ALIGNMENT} from '@datagrok-libraries/bio';
12
6
  import {findMutations} from '../utils/algorithms';
13
7
  import * as type from '../utils/types';
14
8
 
15
9
  category('Algorithms', () => {
16
- let activityCol: DG.Column<number>;
17
- let monomerColumns: DG.Column<string>[];
10
+ let activityCol: type.RawData;
11
+ let monomerColumns: type.RawColumn[];
18
12
  let settings: type.PeptidesSettings;
19
13
 
20
14
  before(async () => {
21
- activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]);
15
+ activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]).getRawData();
22
16
  monomerColumns = [
23
- DG.Column.fromList('string', '1', 'ABC'.split('')),
24
- DG.Column.fromList('string', '2', 'ACC'.split('')),
25
- DG.Column.fromList('string', '3', 'ACD'.split('')),
26
- ];
17
+ DG.Column.fromList('string', '1', 'AAA'.split('')),
18
+ DG.Column.fromList('string', '2', 'BCC'.split('')),
19
+ DG.Column.fromList('string', '3', 'CCD'.split('')),
20
+ ].map((col) => ({
21
+ name: col.name,
22
+ rawData: col.getRawData(),
23
+ cat: col.categories,
24
+ }));
27
25
  settings = {maxMutations: 1, minActivityDelta: 2};
28
26
  });
29
27
 
@@ -40,12 +38,23 @@ category('Algorithms', () => {
40
38
 
41
39
  const c3 = c.get('3')!;
42
40
  const d3 = d.get('3')!;
43
- expect(c3.has(2), true);
44
- expect(d3.has(3), true);
41
+ expect(c3.has(1), true);
42
+ expect(d3.has(2), true);
45
43
 
46
- const c32 = c3.get(2)!;
47
- const d33 = d3.get(3)!;
48
- expect(c32[0], 3);
49
- expect(d33[0], 2);
44
+ const c31 = c3.get(1)!;
45
+ const d32 = d3.get(2)!;
46
+ expect(c31[0], 2);
47
+ expect(d32[0], 1);
50
48
  });
49
+
50
+ test('MutationCliffs - Benchmark 5k', async () => {
51
+ const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k.d42'))[0];
52
+ const activityCol: type.RawData = df.getCol('Activity').getRawData();
53
+ const monomerCols: type.RawColumn[] = [];
54
+ for (let i = 1; i < 16; ++i) {
55
+ const col = df.getCol(i.toString());
56
+ monomerCols.push({name: col.name, rawData: col.getRawData(), cat: col.categories});
57
+ }
58
+ DG.time('MutationCliffs', () => findMutations(activityCol, monomerCols));
59
+ }, {skipReason: 'Benchmark'});
51
60
  });
package/src/tests/core.ts CHANGED
@@ -15,14 +15,11 @@ category('Core', () => {
15
15
  let simpleActivityCol: DG.Column<number>;
16
16
  let simpleAlignedSeqCol: DG.Column<string>;
17
17
  let simpleScaledCol: DG.Column<number>;
18
- let scalingFormula: (x: number) => number;
19
- let simpleScaledColName: string;
20
18
 
21
19
  let complexTable: DG.DataFrame;
22
20
  let complexActivityCol: DG.Column<number>;
23
21
  let complexAlignedSeqCol: DG.Column<string>;
24
22
  let complexScaledCol: DG.Column<number>;
25
- let complexScaledColName: string;
26
23
  const alignedSequenceCol = 'AlignedSequence';
27
24
 
28
25
  let model: PeptidesModel | null = null;
@@ -32,7 +29,7 @@ category('Core', () => {
32
29
  simpleTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned.csv'));
33
30
  simpleActivityCol = simpleTable.getCol(simpleActivityColName);
34
31
  simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
35
- simpleAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
32
+ simpleAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
36
33
  simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
37
34
  simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
38
35
  simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
@@ -52,7 +49,7 @@ category('Core', () => {
52
49
  complexTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned_2.csv'));
53
50
  complexActivityCol = complexTable.getCol(complexActivityColName);
54
51
  complexAlignedSeqCol = complexTable.getCol('MSA');
55
- complexAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
52
+ complexAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
56
53
  complexAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.UN);
57
54
  complexAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.SEPARATOR);
58
55
  complexAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
@@ -74,7 +71,7 @@ category('Core', () => {
74
71
  simpleTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned.csv'));
75
72
  simpleActivityCol = simpleTable.getCol(simpleActivityColName);
76
73
  simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
77
- simpleAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
74
+ simpleAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
78
75
  simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
79
76
  simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
80
77
  simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
@@ -45,8 +45,8 @@ export async function _testDimensionalityReducer(
45
45
 
46
46
  const [X, Y] = embcols as Array<Float32Array>;
47
47
 
48
- expect(X.every((v) => v !== null && v !== NaN), true);
49
- expect(Y.every((v) => v !== null && v !== NaN), true);
48
+ expect(X.every((v) => v !== null && !Number.isNaN(v)), true);
49
+ expect(Y.every((v) => v !== null && !Number.isNaN(v)), true);
50
50
  }
51
51
 
52
52
  /**
@@ -64,12 +64,12 @@ export async function _testDimensionalityReducer(
64
64
  export async function _testPeptideSimilaritySpaceViewer(table: DG.DataFrame, alignedSequencesColumn: DG.Column,
65
65
  method: string, measure: string, cyclesCount: number): Promise<void> {
66
66
  const viewer = await createPeptideSimilaritySpaceViewer(
67
- table, method, measure, cyclesCount, undefined, alignedSequencesColumn);
67
+ table, method, measure, cyclesCount, alignedSequencesColumn, undefined);
68
68
  const df = viewer.dataFrame;
69
69
 
70
70
  const axesNames = ['~X', '~Y', '~MW'];
71
71
  const axes = axesNames.map((v) => df.getCol(v).getRawData() as Float32Array);
72
72
 
73
73
  for (const ax of axes)
74
- expect(ax.every((v) => v !== null && v !== NaN), true);
74
+ expect(ax.every((v) => v !== null && !Number.isNaN(v)), true);
75
75
  }
@@ -1,34 +1,32 @@
1
- import * as DG from 'datagrok-api/dg';
2
-
3
1
  import * as C from './constants';
4
2
  import * as type from './types';
5
3
  import {getTypedArrayConstructor} from './misc';
6
4
 
7
- //TODO: move out
8
- export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG.Column<string>[],
5
+ type MutationCliffInfo = {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number};
6
+
7
+ export function findMutations(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
9
8
  settings: type.PeptidesSettings = {}): type.SubstitutionsInfo {
10
- const nCols = monomerColumns.length;
9
+ const nCols = monomerInfoArray.length;
11
10
  if (nCols == 0)
12
11
  throw new Error(`PepAlgorithmError: Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
13
12
 
14
13
  const substitutionsInfo: type.SubstitutionsInfo = new Map();
15
- const nRows = activityCol.length;
14
+ const nRows = activityArray.length;
16
15
  for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
17
16
  for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
18
17
  let substCounter = 0;
19
- const activityValSeq1 = activityCol.get(seq1Idx)!;
20
- const activityValSeq2 = activityCol.get(seq2Idx)!;
18
+ const activityValSeq1 = activityArray[seq1Idx];
19
+ const activityValSeq2 = activityArray[seq2Idx];
21
20
  const delta = activityValSeq1 - activityValSeq2;
22
21
  if (Math.abs(delta) < (settings.minActivityDelta ?? 0))
23
22
  continue;
24
23
 
25
24
  let substCounterFlag = false;
26
- const tempData: { pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number }[] =
27
- [];
28
- for (const currentPosCol of monomerColumns) {
29
- const seq1monomer = currentPosCol.get(seq1Idx)!;
30
- const seq2monomer = currentPosCol.get(seq2Idx)!;
31
- if (seq1monomer == seq2monomer)
25
+ const tempData: MutationCliffInfo[] = [];
26
+ for (const monomerInfo of monomerInfoArray) {
27
+ const seq1category = monomerInfo.rawData[seq1Idx];
28
+ const seq2category = monomerInfo.rawData[seq2Idx];
29
+ if (seq1category == seq2category)
32
30
  continue;
33
31
 
34
32
  substCounter++;
@@ -37,9 +35,9 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
37
35
  break;
38
36
 
39
37
  tempData.push({
40
- pos: currentPosCol.name,
41
- seq1monomer: seq1monomer,
42
- seq2monomer: seq2monomer,
38
+ pos: monomerInfo.name,
39
+ seq1monomer: monomerInfo.cat![seq1category],
40
+ seq2monomer: monomerInfo.cat![seq2category],
43
41
  seq1Idx: seq1Idx,
44
42
  seq2Idx: seq2Idx,
45
43
  });
@@ -49,20 +47,22 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
49
47
  continue;
50
48
 
51
49
  for (const tempDataElement of tempData) {
52
- const position = tempDataElement.pos;
53
-
54
50
  //Working with seq1monomer
55
51
  const seq1monomer = tempDataElement.seq1monomer;
56
52
  if (!substitutionsInfo.has(seq1monomer))
57
53
  substitutionsInfo.set(seq1monomer, new Map());
58
54
 
55
+ const position = tempDataElement.pos;
56
+
59
57
  let positionsMap = substitutionsInfo.get(seq1monomer)!;
60
58
  if (!positionsMap.has(position))
61
59
  positionsMap.set(position, new Map());
62
60
 
63
61
  let indexes = positionsMap.get(position)!;
64
-
65
- !indexes.has(seq1Idx) ? indexes.set(seq1Idx, [seq2Idx]) : (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
62
+ if (indexes.has(seq1Idx))
63
+ (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
64
+ else
65
+ indexes.set(seq1Idx, [seq2Idx]);
66
66
 
67
67
  //Working with seq2monomer
68
68
  const seq2monomer = tempDataElement.seq2monomer;
@@ -74,7 +74,10 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
74
74
  positionsMap.set(position, new Map());
75
75
 
76
76
  indexes = positionsMap.get(position)!;
77
- !indexes.has(seq2Idx) ? indexes.set(seq2Idx, [seq1Idx]) : (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
77
+ if (indexes.has(seq2Idx))
78
+ (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
79
+ else
80
+ indexes.set(seq2Idx, [seq1Idx]);
78
81
  }
79
82
  }
80
83
  }
@@ -11,11 +11,10 @@ function renderCellSelection(canvasContext: CanvasRenderingContext2D, bound: DG.
11
11
  }
12
12
 
13
13
  /** A function that sets amino acid residue cell renderer to the specified column */
14
- export function setAARRenderer(col: DG.Column, alphabet: string, grid: DG.Grid, timeout: number = 500): void {
14
+ export function setAARRenderer(col: DG.Column, alphabet: string): void {
15
15
  col.semType = C.SEM_TYPES.MONOMER;
16
16
  col.setTag('cell.renderer', C.SEM_TYPES.MONOMER);
17
17
  col.tags[C.TAGS.ALPHABET] = alphabet;
18
- // setTimeout(() => grid.columns.byName(col.name)!.width = 60, timeout);
19
18
  }
20
19
 
21
20
  export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
@@ -62,9 +61,10 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
62
61
  if (substitutionsInfo.size > 0) {
63
62
  canvasContext.textBaseline = 'middle';
64
63
  canvasContext.textAlign = 'center';
65
- // canvasContext.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(coef)));
66
64
  canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
67
65
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
66
+ canvasContext.shadowBlur = 5;
67
+ canvasContext.shadowColor = DG.Color.toHtml(DG.Color.white);
68
68
  let substValue = 0;
69
69
  substitutionsInfo.get(currentAAR)?.get(currentPosition)?.forEach((idxs) => substValue += idxs.length);
70
70
  if (substValue && substValue != 0)
@@ -77,7 +77,10 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
77
77
  }
78
78
 
79
79
  export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
80
- currentPosition: string, invariantMapSelection: types.PositionToAARList, cellValue: number, bound: DG.Rect): void {
80
+ currentPosition: string, invariantMapSelection: types.PositionToAARList, cellValue: number, bound: DG.Rect,
81
+ color: number): void {
82
+ canvasContext.fillStyle = DG.Color.toHtml(color);
83
+ canvasContext.fillRect(bound.x, bound.y, bound.width, bound.height);
81
84
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
82
85
  canvasContext.textAlign = 'center';
83
86
  canvasContext.textBaseline = 'middle';
@@ -89,7 +92,7 @@ export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D,
89
92
  renderCellSelection(canvasContext, bound);
90
93
  }
91
94
 
92
- export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: number,
95
+ export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: string, cellRawData: number,
93
96
  clusterSelection: number[], bound: DG.Rect): void {
94
97
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
95
98
  canvasContext.textAlign = 'center';
@@ -97,7 +100,7 @@ export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, c
97
100
  canvasContext.fillStyle = '#000';
98
101
  canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
99
102
 
100
- if (clusterSelection.includes(cellValue))
103
+ if (clusterSelection.includes(cellRawData))
101
104
  renderCellSelection(canvasContext, bound);
102
105
  }
103
106
 
@@ -111,25 +114,26 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
111
114
  drawOptions.marginVertical ??= 5;
112
115
  drawOptions.marginHorizontal ??= 5;
113
116
 
117
+ const pr = window.devicePixelRatio;
114
118
  const totalSpaceBetweenLetters = (statsInfo.orderedIndexes.length - 1) * drawOptions.upperLetterAscent;
115
- const barHeight = bounds.height - 2 * drawOptions.marginVertical - totalSpaceBetweenLetters;
119
+ const barHeight = (bounds.height - 2 * drawOptions.marginVertical - totalSpaceBetweenLetters) * pr;
116
120
  const leftShift = drawOptions.marginHorizontal * 2;
117
- const barWidth = bounds.width - leftShift * 2;
118
- const xStart = bounds.x + leftShift;
119
- const selectionWidth = 4;
120
- const xSelection = bounds.x + 3;
121
- let currentY = bounds.y + drawOptions.marginVertical;
121
+ const barWidth = (bounds.width - leftShift * 2) * pr;
122
+ const xStart = (bounds.x + leftShift) * pr;
123
+ const selectionWidth = 4 * pr;
124
+ const xSelection = (bounds.x + 3) * pr;
125
+ let currentY = (bounds.y + drawOptions.marginVertical) * pr;
122
126
 
123
127
  const monomerBounds: {[monomer: string]: DG.Rect} = {};
124
128
  for (const index of statsInfo.orderedIndexes) {
125
129
  const monomer = statsInfo.monomerCol.get(index)!;
126
130
  const monomerHeight = barHeight * (statsInfo.countCol.get(index)! / rowCount);
127
131
  const selectionHeight = barHeight * ((monomerSelectionStats[monomer] ?? 0) / rowCount);
128
- const currentBound = new DG.Rect(xStart, currentY, barWidth, monomerHeight);
132
+ const currentBound = new DG.Rect(xStart / pr, currentY / pr, barWidth / pr, monomerHeight / pr);
129
133
  monomerBounds[monomer] = currentBound;
130
134
 
131
135
  ctx.resetTransform();
132
- if (monomer !== '-') {
136
+ if (monomer !== '-' && monomer !== '') {
133
137
  const monomerTxt = bio.monomerToShort(monomer, 5);
134
138
  const mTm: TextMetrics = ctx.measureText(monomerTxt);
135
139
 
@@ -142,10 +146,12 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
142
146
  ctx.textBaseline = 'top';
143
147
  ctx.font = drawOptions.fontStyle;
144
148
  // Hacks to scale uppercase characters to target rectangle
145
- ctx.setTransform(barWidth / mTm.width, 0, 0, monomerHeight / drawOptions.upperLetterHeight, xStart, currentY);
149
+ const widthTransform = barWidth / mTm.width;
150
+ const heightTransfrom = monomerHeight / drawOptions.upperLetterHeight;
151
+ ctx.setTransform(widthTransform, 0, 0, heightTransfrom, xStart, currentY);
146
152
  ctx.fillText(monomerTxt, 0, 0);
147
153
  }
148
- currentY += monomerHeight + drawOptions.upperLetterAscent;
154
+ currentY += monomerHeight + drawOptions.upperLetterAscent * pr;
149
155
  }
150
156
 
151
157
  return monomerBounds;
@@ -1,16 +1,13 @@
1
1
  export enum COLUMNS_NAMES {
2
2
  SPLIT_COL = '~split',
3
- ACTIVITY = '~activity',
4
- ACTIVITY_SCALED = 'activity_scaled',
5
- ALIGNED_SEQUENCE = '~aligned_sequence',
3
+ ACTIVITY_SCALED = 'Scaled activity',
6
4
  MONOMER = 'AAR',
7
5
  POSITION = 'Pos',
8
6
  P_VALUE = 'pValue',
9
7
  MEAN_DIFFERENCE = 'Mean difference',
10
8
  COUNT = 'Count',
11
9
  RATIO = 'Ratio',
12
- CLUSTERS = 'clusters',
13
- MACROMOLECULE = 'macromolecule',
10
+ MEMBERS = 'Members',
14
11
  }
15
12
 
16
13
  export enum CATEGORIES {
@@ -25,47 +22,17 @@ export enum TAGS {
25
22
  SELECTION = 'selection',
26
23
  ALPHABET = 'alphabet',
27
24
  FILTER = 'filter',
28
- CLUSTERS = 'clusters',
29
25
  SAR_MODE = 'sarMode',
30
26
  CLUSTER_SELECTION = 'clusterSelection',
31
27
  VISIBLE = 'visible',
28
+ SETTINGS = 'settings',
32
29
  }
33
30
 
34
31
  export enum SEM_TYPES {
35
32
  MONOMER = 'Monomer',
36
- MACROMOLECULE = 'Macromolecule',
37
33
  MACROMOLECULE_DIFFERENCE = 'MacromoleculeDifference',
38
- ACTIVITY = 'activity',
39
- ACTIVITY_SCALED = 'activityScaled',
40
34
  }
41
35
 
42
- export const STATS = 'stats';
43
-
44
36
  export const EMBEDDING_STATUS = 'embeddingStatus';
45
37
 
46
38
  export const PEPTIDES_ANALYSIS = 'isPeptidesAnalysis';
47
-
48
- export enum FLAGS {
49
- CELL_CHANGING = 'isCellChanging',
50
- }
51
-
52
- export const aarGroups = {
53
- 'R': 'PC', 'H': 'PC', 'K': 'PC',
54
- 'D': 'NC', 'E': 'NC',
55
- 'S': 'U', 'T': 'U', 'N': 'U', 'Q': 'U',
56
- 'C': 'SC', 'U': 'SC', 'G': 'SC', 'P': 'SC',
57
- 'A': 'H', 'V': 'H', 'I': 'H', 'L': 'H', 'M': 'H', 'F': 'H', 'Y': 'H', 'W': 'H',
58
- '-': '-',
59
- };
60
-
61
- export const groupDescription: {[key: string]: {'description': string, aminoAcids: string[]}} = {
62
- 'PC': {'description': 'Positive Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['R', 'H', 'K']},
63
- 'NC': {'description': 'Negative Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['D', 'E']},
64
- 'U': {'description': 'Amino Acids with Polar Uncharged Side Chains', 'aminoAcids': ['S', 'T', 'N', 'Q']},
65
- 'SC': {'description': 'Special Cases', 'aminoAcids': ['C', 'U', 'G', 'P']},
66
- 'H': {
67
- 'description': 'Amino Acids with Hydrophobic Side Chain',
68
- 'aminoAcids': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
69
- },
70
- '-': {'description': 'Unknown Amino Acid', 'aminoAcids': ['-']},
71
- };
package/src/utils/misc.ts CHANGED
@@ -2,11 +2,6 @@ import * as DG from 'datagrok-api/dg';
2
2
  import * as C from './constants';
3
3
  import * as type from './types';
4
4
 
5
- import {AminoacidsPalettes} from '@datagrok-libraries/bio/src/aminoacids';
6
- import {NucleotidesPalettes} from '@datagrok-libraries/bio/src/nucleotides';
7
- import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
8
- import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
9
-
10
5
  export function getTypedArrayConstructor(
11
6
  maxNum: number): Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor {
12
7
  return maxNum < 256 ? Uint8Array :
@@ -20,27 +15,23 @@ export function getSeparator(col: DG.Column<string>): string {
20
15
 
21
16
  export function scaleActivity(activityCol: DG.Column<number>, scaling: string = 'none'): DG.Column<number> {
22
17
  let formula = (x: number): number => x;
23
- let newColName = 'activity';
24
18
  switch (scaling) {
25
19
  case 'none':
26
20
  break;
27
21
  case 'lg':
28
22
  formula = (x: number): number => Math.log10(x);
29
- newColName = `Log10(${newColName})`;
30
23
  break;
31
24
  case '-lg':
32
25
  formula = (x: number): number => -Math.log10(x);
33
- newColName = `-Log10(${newColName})`;
34
26
  break;
35
27
  default:
36
28
  throw new Error(`ScalingError: method \`${scaling}\` is not available.`);
37
29
  }
38
- const scaledCol = DG.Column.float(C.COLUMNS_NAMES.ACTIVITY_SCALED, activityCol.length).init((i) => {
39
- const val = activityCol.get(i);
40
- return val ? formula(val) : val;
41
- });
42
- scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
43
- scaledCol.setTag('gridName', newColName);
30
+ const scaledCol: DG.Column<number> = DG.Column.float(C.COLUMNS_NAMES.ACTIVITY_SCALED, activityCol.length)
31
+ .init((i) => {
32
+ const val = activityCol.get(i);
33
+ return val ? formula(val) : val;
34
+ });
44
35
 
45
36
  return scaledCol;
46
37
  }
@@ -53,7 +44,7 @@ export function calculateSelected(df: DG.DataFrame): type.MonomerSelectionStats
53
44
  const monomer = col.get(idx);
54
45
  if (!monomer)
55
46
  continue;
56
-
47
+
57
48
  selectedObj[col.name] ??= {};
58
49
  selectedObj[col.name][monomer] ??= 0;
59
50
  selectedObj[col.name][monomer] += 1;
@@ -67,3 +58,11 @@ export function isGridCellInvalid(gc: DG.GridCell | null): boolean {
67
58
  return !gc || !gc.cell.value || !gc.tableColumn || gc.tableRowIndex == null || gc.tableRowIndex == -1 ||
68
59
  gc.cell.value == DG.INT_NULL || gc.cell.value == DG.FLOAT_NULL;
69
60
  }
61
+
62
+ export function extractMonomerInfo(col: DG.Column<string>): type.RawColumn {
63
+ return {
64
+ name: col.name,
65
+ cat: col.categories,
66
+ rawData: col.getRawData(),
67
+ };
68
+ }
@@ -38,14 +38,11 @@ export function cleanAlignedSequencesColumn(col: DG.Column): Array<string> {
38
38
  * @param {boolean} [zoom=false] Whether to fit view.
39
39
  * @return {Promise<DG.ScatterPlotViewer>} A viewer.
40
40
  */
41
- export async function createPeptideSimilaritySpaceViewer(
42
- table: DG.DataFrame, method: string, measure: string, cyclesCount: number, view?: DG.TableView, col?: DG.Column,
43
- ): Promise<DG.ScatterPlotViewer> {
41
+ export async function createPeptideSimilaritySpaceViewer(table: DG.DataFrame, method: string, measure: string,
42
+ cyclesCount: number, col: DG.Column, activityColName?: string, view?: DG.TableView): Promise<DG.ScatterPlotViewer> {
44
43
  const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
45
44
 
46
45
  const axesNames = ['~X', '~Y', '~MW'];
47
- // col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
48
- col ??= table.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
49
46
  const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
50
47
 
51
48
  const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
@@ -81,7 +78,7 @@ export async function createPeptideSimilaritySpaceViewer(
81
78
  table.columns.insert(newCol);
82
79
  }
83
80
 
84
- const colorColName = table.columns.bySemType(C.SEM_TYPES.ACTIVITY)?.name ?? '~MW';
81
+ const colorColName = activityColName ?? '~MW';
85
82
  const viewerOptions = {
86
83
  x: '~X', y: '~Y', color: colorColName, size: '~MW', title: 'Peptide Space',
87
84
  showYSelector: false, showXSelector: false, showColorSelector: false, showSizeSelector: false,
@@ -137,7 +134,7 @@ export class PeptideSimilaritySpaceWidget {
137
134
  */
138
135
  public async drawViewer(): Promise<DG.Viewer> {
139
136
  const viewer = await createPeptideSimilaritySpaceViewer(
140
- this.currentDf, this.method, this.metrics, this.cycles, undefined, this.alignedSequencesColumn);
137
+ this.currentDf, this.method, this.metrics, this.cycles, this.alignedSequencesColumn, undefined);
141
138
  viewer.root.style.width = 'auto';
142
139
  return viewer;
143
140
  }
@@ -1,6 +1,5 @@
1
- import * as DG from 'datagrok-api/dg';
2
-
3
1
  import {tTest} from '@datagrok-libraries/statistics/src/tests';
2
+ import {RawData} from './types';
4
3
 
5
4
  export type Stats = {
6
5
  count: number,
@@ -9,21 +8,31 @@ export type Stats = {
9
8
  ratio: number,
10
9
  };
11
10
 
12
- type StatsData = Float32Array | Float64Array | Int32Array | Uint32Array | number[];
11
+ export type MaskInfo = {
12
+ trueCount: number,
13
+ falseCount: number,
14
+ mask: boolean[] | Int32Array,
15
+ };
16
+
17
+ export function getStats(data: RawData | number[], maskInfo: MaskInfo): Stats {
18
+ const selected = new Float32Array(maskInfo.trueCount);
19
+ const rest = new Float32Array(maskInfo.falseCount);
13
20
 
14
- export function getStats(data: StatsData, mask: DG.BitSet): Stats {
15
- const selected = new Float32Array(mask.trueCount);
16
- const rest = new Float32Array(mask.falseCount);
17
21
  let selectedIndex = 0;
18
22
  let restIndex = 0;
19
- data.forEach((v, i) => mask.get(i) ? selected[selectedIndex++] = v : rest[restIndex++] = v);
23
+ for (let i = 0; i < data.length; ++i) {
24
+ if (maskInfo.mask[i])
25
+ selected[selectedIndex++] = data[i];
26
+ else
27
+ rest[restIndex++] = data[i];
28
+ }
20
29
 
21
30
  const testResult = tTest(selected, rest);
22
31
  const currentMeanDiff = testResult['Mean difference']!;
23
32
  return {
24
33
  count: selected.length,
25
- pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'],
26
- meanDifference: currentMeanDiff,
34
+ pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'] || 0,
35
+ meanDifference: currentMeanDiff || 0,
27
36
  ratio: selected.length / data.length,
28
37
  };
29
38
  }