@datagrok/peptides 0.8.15 → 1.0.2
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/dist/package-test.js +8397 -4112
- package/dist/package.js +9609 -4868
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +306 -215
- package/files/aligned.csv +648 -648
- package/files/aligned_2.csv +541 -10275
- package/files/aligned_3.csv +335 -0
- package/helm/JSDraw/Pistoia.HELM.js +27 -0
- package/package.json +20 -14
- package/package.png +0 -0
- package/src/__jest__/remote.test.ts +31 -13
- package/src/model.ts +540 -646
- package/src/package-test.ts +3 -4
- package/src/package.ts +13 -131
- package/src/tests/core.ts +56 -20
- package/src/tests/peptide-space-test.ts +65 -45
- package/src/tests/utils.ts +15 -59
- package/src/utils/cell-renderer.ts +140 -262
- package/src/utils/constants.ts +8 -4
- package/src/utils/filtering-statistics.ts +21 -53
- package/src/utils/misc.ts +103 -16
- package/src/utils/multiple-sequence-alignment.ts +1 -1
- package/src/utils/peptide-similarity-space.ts +2 -2
- package/src/utils/types.ts +8 -7
- package/src/viewers/peptide-space-viewer.ts +62 -35
- package/src/viewers/sar-viewer.ts +33 -20
- package/src/widgets/analyze-peptides.ts +26 -10
- package/src/widgets/distribution.ts +171 -42
- package/src/widgets/manual-alignment.ts +4 -4
- package/src/widgets/subst-table.ts +40 -21
- package/src/workers/dimensionality-reducer.ts +1 -6
- package/{test-Peptides-e702a345ac13-2a8c8b59.html → test-Peptides-4f0c8bae6479-74cbfe68.html} +22 -20
- package/detectors.js +0 -9
- package/src/monomer-library.ts +0 -193
- package/src/tests/msa-tests.ts +0 -27
- package/src/utils/chem-palette.ts +0 -280
- package/src/viewers/stacked-barchart-viewer.ts +0 -362
- package/src/widgets/multiple-sequence-alignment.ts +0 -9
- package/src/widgets/peptide-molecule.ts +0 -82
package/src/utils/misc.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
|
|
3
2
|
import * as C from './constants';
|
|
3
|
+
import * as type from './types';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
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
|
+
import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
8
10
|
|
|
9
|
-
export function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
export function getPalleteByType(paletteType: string): SeqPalette {
|
|
12
|
+
switch (paletteType) {
|
|
13
|
+
case 'PT':
|
|
14
|
+
return AminoacidsPalettes.GrokGroups;
|
|
15
|
+
case 'NT':
|
|
16
|
+
case 'DNA':
|
|
17
|
+
case 'RNA':
|
|
18
|
+
return NucleotidesPalettes.Chromatogram;
|
|
19
|
+
// other
|
|
20
|
+
default:
|
|
21
|
+
return UnknownSeqPalettes.Color;
|
|
20
22
|
}
|
|
21
|
-
return separator as string ?? '';
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export function getTypedArrayConstructor(
|
|
@@ -27,3 +28,89 @@ export function getTypedArrayConstructor(
|
|
|
27
28
|
maxNum < 65536 ? Uint16Array :
|
|
28
29
|
Uint32Array;
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
export function getSeparator(col: DG.Column<string>): string {
|
|
33
|
+
return col.getTag(C.TAGS.SEPARATOR) ?? '';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function splitAlignedPeptides(peptideColumn: DG.Column<string>): DG.DataFrame {
|
|
37
|
+
const splitter = WebLogo.getSplitterForColumn(peptideColumn);
|
|
38
|
+
const colLen = peptideColumn.length;
|
|
39
|
+
const resultDf = DG.DataFrame.create(colLen);
|
|
40
|
+
let monomerList = splitter(peptideColumn.get(0)!);
|
|
41
|
+
const columnList: DG.Column<string>[] = [];
|
|
42
|
+
|
|
43
|
+
// create columns and fill the first row for faster values filling in the next loop
|
|
44
|
+
for (let i = 0; i < monomerList.length; i++) {
|
|
45
|
+
const col = resultDf.columns.addNewString((i + 1).toString());
|
|
46
|
+
col.set(0, monomerList[i] || '-', false);
|
|
47
|
+
columnList.push(col);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (let rowIndex = 1; rowIndex < colLen; rowIndex++) {
|
|
51
|
+
monomerList = splitter(peptideColumn.get(rowIndex)!);
|
|
52
|
+
monomerList.forEach((monomer, colIndex) => columnList[colIndex].set(rowIndex, monomer || '-', false));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return resultDf;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function scaleActivity(
|
|
59
|
+
activityScaling: string, df: DG.DataFrame, originalActivityName?: string, cloneBitset = false,
|
|
60
|
+
): [DG.DataFrame, string] {
|
|
61
|
+
let currentActivityColName = originalActivityName ?? C.COLUMNS_NAMES.ACTIVITY;
|
|
62
|
+
const flag = df.columns.names().includes(currentActivityColName) &&
|
|
63
|
+
currentActivityColName === originalActivityName;
|
|
64
|
+
currentActivityColName = flag ? currentActivityColName : C.COLUMNS_NAMES.ACTIVITY;
|
|
65
|
+
const tempDf = df.clone(cloneBitset ? df.filter : null, [currentActivityColName]);
|
|
66
|
+
|
|
67
|
+
let formula = (v: number) => v;
|
|
68
|
+
let newColName = 'activity';
|
|
69
|
+
switch (activityScaling) {
|
|
70
|
+
case 'none':
|
|
71
|
+
break;
|
|
72
|
+
case 'lg':
|
|
73
|
+
formula = (v: number) => Math.log10(v);
|
|
74
|
+
newColName = `Log10(${newColName})`;
|
|
75
|
+
break;
|
|
76
|
+
case '-lg':
|
|
77
|
+
formula = (v: number) => -Math.log10(v);
|
|
78
|
+
newColName = `-Log10(${newColName})`;
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
throw new Error(`ScalingError: method \`${activityScaling}\` is not available.`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const asCol = tempDf.columns.addNewFloat(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
85
|
+
const activityCol = df.getCol(currentActivityColName);
|
|
86
|
+
asCol.init((i) => formula(activityCol.get(i)));
|
|
87
|
+
df.tags['scaling'] = activityScaling;
|
|
88
|
+
|
|
89
|
+
return [tempDf, newColName];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function calculateBarsData(columns: DG.Column<string>[], selection: DG.BitSet): type.MonomerDfStats {
|
|
93
|
+
const dfStats: type.MonomerDfStats = {};
|
|
94
|
+
const columnsLen = columns.length;
|
|
95
|
+
|
|
96
|
+
for (let colIndex = 0; colIndex < columnsLen; colIndex++) {
|
|
97
|
+
const col = columns[colIndex];
|
|
98
|
+
dfStats[col.name] = calculateBarData(col, selection);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return dfStats;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function calculateBarData(col: DG.Column<string>, selection: DG.BitSet): type.MonomerColStats {
|
|
105
|
+
const colLen = col.length;
|
|
106
|
+
const colStats: type.MonomerColStats = {};
|
|
107
|
+
col.categories.forEach((monomer) => colStats[monomer] = {count: 0, selected: 0});
|
|
108
|
+
|
|
109
|
+
for (let rowIndex = 0; rowIndex < colLen; rowIndex++) {
|
|
110
|
+
const monomerStats = colStats[col.get(rowIndex)!];
|
|
111
|
+
monomerStats.count += 1;
|
|
112
|
+
monomerStats.selected += +selection.get(rowIndex);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return colStats;
|
|
116
|
+
}
|
|
@@ -87,7 +87,7 @@ export async function runKalign(col: DG.Column, isAligned = false) : Promise<DG.
|
|
|
87
87
|
|
|
88
88
|
const aligned = _fastaToStrings(buf).slice(0, sequences.length);
|
|
89
89
|
const alignedCol = DG.Column.fromStrings(`(${col.name})msa`, _stringsToAligned(aligned));
|
|
90
|
-
alignedCol.semType = C.SEM_TYPES.
|
|
90
|
+
alignedCol.semType = C.SEM_TYPES.MACROMOLECULE;
|
|
91
91
|
return alignedCol;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -7,7 +7,7 @@ import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encod
|
|
|
7
7
|
import {DimensionalityReducer} from '@datagrok-libraries/ml/src/reduce-dimensionality';
|
|
8
8
|
import {
|
|
9
9
|
createDimensinalityReducingWorker,
|
|
10
|
-
IReduceDimensionalityResult
|
|
10
|
+
IReduceDimensionalityResult,
|
|
11
11
|
} from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
|
|
12
12
|
import {Measure, StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
13
13
|
import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
|
|
@@ -44,7 +44,7 @@ export async function createPeptideSimilaritySpaceViewer(
|
|
|
44
44
|
const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
|
|
45
45
|
|
|
46
46
|
const axesNames = ['~X', '~Y', '~MW'];
|
|
47
|
-
col ??= table.columns.bySemType(C.SEM_TYPES.
|
|
47
|
+
col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
48
48
|
const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
|
|
49
49
|
|
|
50
50
|
const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
|
package/src/utils/types.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
2
|
|
|
3
|
-
export type SubstitutionCases = {[aar: string]: number[][][]};
|
|
4
|
-
export type SubstitutionTooltips = { [aar: string]: {}[][]; };
|
|
5
3
|
export type DataFrameDict = {[key: string]: DG.DataFrame};
|
|
6
4
|
|
|
7
|
-
export namespace BarChart {
|
|
8
|
-
export type BarPart = {colName : string, aaName : string};
|
|
9
|
-
export type BarStatsObject = {name: string, count: number, selectedCount: number};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
5
|
export type UTypedArray = Uint8Array | Uint16Array | Uint32Array;
|
|
13
6
|
//AAR: (Position: (index: indexList))
|
|
14
7
|
export type SubstitutionsInfo = Map<string, Map<string, Map<number, number[] | UTypedArray>>>;
|
|
8
|
+
export type SelectionObject = {[postiton: string]: string[]};
|
|
9
|
+
|
|
10
|
+
export type HELMMonomer = {at: {[key: string]: string}, id: string, m: string, na: string, n: string, rs: number};
|
|
11
|
+
|
|
12
|
+
export type MonomerColStats = {[monomer: string]: {count: number, selected: number}};
|
|
13
|
+
export type MonomerDfStats = {[position: string]: MonomerColStats};
|
|
14
|
+
|
|
15
|
+
export type BarCoordinates = {[monomer: string]: DG.Rect};
|
|
@@ -6,19 +6,21 @@ import $ from 'cash-dom';
|
|
|
6
6
|
|
|
7
7
|
import {getSequenceMolecularWeight} from '../utils/molecular-measure';
|
|
8
8
|
import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encoder';
|
|
9
|
-
import {createDimensinalityReducingWorker, IReduceDimensionalityResult
|
|
9
|
+
import {createDimensinalityReducingWorker, IReduceDimensionalityResult,
|
|
10
10
|
} from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
|
|
11
11
|
import {StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
12
|
-
import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
|
|
13
12
|
import * as C from '../utils/constants';
|
|
13
|
+
import {PeptidesModel} from '../model';
|
|
14
14
|
|
|
15
15
|
export class PeptideSpaceViewer extends DG.JsViewer {
|
|
16
16
|
method: string;
|
|
17
17
|
measure: string;
|
|
18
18
|
cyclesCount: number;
|
|
19
|
-
// controller: PeptidesController | null = null;
|
|
20
19
|
customProperties = new Set(['method', 'measure', 'cyclesCount']);
|
|
21
20
|
isEmbeddingCreating: boolean = false;
|
|
21
|
+
model!: PeptidesModel;
|
|
22
|
+
//FIXME: even if the property stays the same, for some reason it's still triggering prop change
|
|
23
|
+
prevProps!: {'method': string, 'measure': string, 'cyclesCount': number};
|
|
22
24
|
|
|
23
25
|
constructor() {
|
|
24
26
|
super();
|
|
@@ -28,34 +30,74 @@ export class PeptideSpaceViewer extends DG.JsViewer {
|
|
|
28
30
|
const measureChoices = ['Levenshtein', 'Jaro-Winkler'];
|
|
29
31
|
this.measure = this.addProperty('measure', DG.TYPE.STRING, 'Levenshtein', {choices: measureChoices});
|
|
30
32
|
this.cyclesCount = this.addProperty('cyclesCount', DG.TYPE.INT, 100);
|
|
33
|
+
this.updatePrevProperties();
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
updatePrevProperties(): void {
|
|
37
|
+
this.prevProps = {'method': this.method, 'measure': this.measure, 'cyclesCount': this.cyclesCount};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async onTableAttached(): Promise<void> {
|
|
41
|
+
super.onTableAttached();
|
|
42
|
+
|
|
43
|
+
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
44
|
+
|
|
45
|
+
await this.render(!this.dataFrame.temp[C.EMBEDDING_STATUS]);
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
async onPropertyChanged(property: DG.Property | null): Promise<void> {
|
|
39
49
|
super.onPropertyChanged(property);
|
|
50
|
+
if (this.prevProps[property?.name as 'method' | 'measure' | 'cyclesCount' ?? ''] == property?.get(this))
|
|
51
|
+
return;
|
|
40
52
|
|
|
53
|
+
if (this.model)
|
|
54
|
+
await this.render(this.customProperties.has(property?.name ?? '') || !this.dataFrame.temp[C.EMBEDDING_STATUS]);
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
this.updatePrevProperties();
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
async render(computeData=false): Promise<void> {
|
|
46
|
-
if (computeData && !this.isEmbeddingCreating) {
|
|
60
|
+
if (computeData && !this.isEmbeddingCreating && !this.model.isChangingEdfBitset) {
|
|
47
61
|
this.isEmbeddingCreating = true;
|
|
48
62
|
$(this.root).empty();
|
|
49
63
|
const viewerHost = ui.waitBox(async () => {
|
|
50
|
-
|
|
64
|
+
const aligendSeqCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
65
|
+
const edf = await computeWeights(this.dataFrame, this.method, this.measure, this.cyclesCount, aligendSeqCol);
|
|
66
|
+
this.dataFrame.temp[C.EMBEDDING_STATUS] = true;
|
|
67
|
+
this.model.edf = edf;
|
|
68
|
+
|
|
69
|
+
if (edf === null)
|
|
70
|
+
return ui.label('Could not compute embeddings');
|
|
71
|
+
|
|
72
|
+
const edfSelection = edf.selection;
|
|
73
|
+
edfSelection.copyFrom(this.dataFrame.selection);
|
|
74
|
+
edfSelection.onChanged.subscribe(() => {
|
|
75
|
+
if (!this.model.isChangingEdfBitset)
|
|
76
|
+
this.model.fireBitsetChanged(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const colorCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
80
|
+
edf.columns.add(colorCol);
|
|
51
81
|
|
|
52
|
-
const colorColName = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!.name;
|
|
53
82
|
const viewerOptions = {
|
|
54
|
-
x: '~X', y: '~Y', color:
|
|
83
|
+
x: '~X', y: '~Y', color: colorCol.name ?? '~MW', size: '~MW', title: 'Peptide Space',
|
|
55
84
|
showYSelector: false, showXSelector: false, showColorSelector: false, showSizeSelector: false,
|
|
56
85
|
zoomAndFilter: 'no action', axesFollowFilter: false,
|
|
57
86
|
};
|
|
58
|
-
const
|
|
87
|
+
const scatterPlot = edf.plot.scatter(viewerOptions);
|
|
88
|
+
const viewerRoot = scatterPlot.root;
|
|
89
|
+
|
|
90
|
+
viewerRoot.addEventListener('mousemove', (ev) => {
|
|
91
|
+
const idx = scatterPlot.hitTest(ev.offsetX, ev.offsetY);
|
|
92
|
+
if (idx != -1) {
|
|
93
|
+
const table = ui.tableFromMap({
|
|
94
|
+
'Activity': colorCol.get(idx),
|
|
95
|
+
'Sequence': aligendSeqCol.get(idx),
|
|
96
|
+
'Row ID': idx,
|
|
97
|
+
});
|
|
98
|
+
ui.tooltip.show(table, ev.clientX, ev.clientY);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
59
101
|
viewerRoot.style.width = 'auto';
|
|
60
102
|
this.isEmbeddingCreating = false;
|
|
61
103
|
viewerHost.style.paddingLeft = 'unset';
|
|
@@ -67,22 +109,22 @@ export class PeptideSpaceViewer extends DG.JsViewer {
|
|
|
67
109
|
}
|
|
68
110
|
}
|
|
69
111
|
|
|
112
|
+
//Do not accept table, only column
|
|
70
113
|
export async function computeWeights(
|
|
71
114
|
table: DG.DataFrame, method: string, measure: string, cyclesCount: number, col?: DG.Column,
|
|
72
|
-
): Promise<
|
|
115
|
+
): Promise<DG.DataFrame | null> {
|
|
73
116
|
const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
|
|
117
|
+
let edf: DG.DataFrame | null = null;
|
|
74
118
|
try {
|
|
75
119
|
const axesNames = ['~X', '~Y', '~MW'];
|
|
76
|
-
col ??= table.columns.bySemType(C.SEM_TYPES.
|
|
77
|
-
const columnData = col.toList()
|
|
78
|
-
.map((v) => AlignedSequenceEncoder.clean(v));
|
|
120
|
+
col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
121
|
+
const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
|
|
79
122
|
|
|
80
123
|
const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
|
|
81
124
|
{data: columnData, metric: measure as StringMetrics}, method, {cycles: cyclesCount});
|
|
82
125
|
const embcols = reduceDimRes.embedding;
|
|
83
126
|
|
|
84
|
-
const columns = Array.from(
|
|
85
|
-
embcols as Coordinates, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
|
|
127
|
+
const columns = Array.from(embcols, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
|
|
86
128
|
|
|
87
129
|
function _getMW(sequences: string[]): Float32Array {
|
|
88
130
|
const mw: Float32Array = new Float32Array(sequences.length);
|
|
@@ -94,27 +136,12 @@ export async function computeWeights(
|
|
|
94
136
|
|
|
95
137
|
columns.push(DG.Column.fromFloat32Array('~MW', _getMW(columnData)));
|
|
96
138
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Add new axes.
|
|
100
|
-
for (const axis of axesNames) {
|
|
101
|
-
const col = table.col(axis);
|
|
102
|
-
const newCol = edf.getCol(axis);
|
|
103
|
-
|
|
104
|
-
// if (col != null) {
|
|
105
|
-
// for (let i = 0; i < newCol.length; ++i) {
|
|
106
|
-
// const v = newCol.get(i);
|
|
107
|
-
// table.set(axis, i, v);
|
|
108
|
-
// }
|
|
109
|
-
// } else
|
|
110
|
-
// table.columns.insert(newCol);
|
|
111
|
-
const columnList = table.columns;
|
|
112
|
-
col !== null ? columnList.replace(col, newCol) : columnList.insert(newCol);
|
|
113
|
-
}
|
|
139
|
+
edf = DG.DataFrame.fromColumns(columns);
|
|
114
140
|
} catch (error) {
|
|
115
141
|
grok.shell.error('Could not compute embeddings. See console for details.');
|
|
116
142
|
console.error(error);
|
|
117
143
|
} finally {
|
|
118
144
|
pi.close();
|
|
145
|
+
return edf;
|
|
119
146
|
}
|
|
120
147
|
}
|
|
@@ -17,7 +17,7 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
17
17
|
bidirectionalAnalysis: boolean;
|
|
18
18
|
showSubstitution: boolean;
|
|
19
19
|
maxSubstitutions: number;
|
|
20
|
-
|
|
20
|
+
minActivityDelta: number;
|
|
21
21
|
_titleHost = ui.divText('SAR Viewer', {id: 'pep-viewer-title'});
|
|
22
22
|
initialized = false;
|
|
23
23
|
isPropertyChanging: boolean = false;
|
|
@@ -28,18 +28,27 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
28
28
|
this.scaling = this.string('scaling', 'none', {choices: ['none', 'lg', '-lg']});
|
|
29
29
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
30
30
|
this.showSubstitution = this.bool('showSubstitution', true);
|
|
31
|
-
this.maxSubstitutions = this.int('maxSubstitutions',
|
|
32
|
-
this.
|
|
31
|
+
this.maxSubstitutions = this.int('maxSubstitutions', 2);
|
|
32
|
+
this.minActivityDelta = this.float('minActivityDelta', 1);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async onTableAttached(): Promise<void> {
|
|
36
36
|
super.onTableAttached();
|
|
37
|
-
this.dataFrame.temp[this.tempName] ??= this;
|
|
38
37
|
this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
|
|
39
38
|
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
40
39
|
// this.model.init(this.dataFrame);
|
|
41
|
-
await this.requestDataUpdate();
|
|
42
40
|
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
41
|
+
// await this.requestDataUpdate();
|
|
42
|
+
|
|
43
|
+
this.initProperties();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
initProperties() {
|
|
47
|
+
const props = this.model.usedProperties;
|
|
48
|
+
IS_PROPERTY_CHANGING = true;
|
|
49
|
+
for (const [propName, propVal] of Object.entries(props))
|
|
50
|
+
this.props.set(propName, propVal as any as object);
|
|
51
|
+
IS_PROPERTY_CHANGING = false;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
|
|
@@ -56,12 +65,12 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
56
65
|
this.viewerGrid?.invalidate();
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
async requestDataUpdate(): Promise<void> {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
68
|
+
// async requestDataUpdate(): Promise<void> {
|
|
69
|
+
// await this.model.updateData(this.scaling, this.sourceGrid, this.bidirectionalAnalysis,
|
|
70
|
+
// this.minActivityDelta, this.maxSubstitutions, this.showSubstitution);
|
|
71
|
+
// }
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
onPropertyChanged(property: DG.Property): void {
|
|
65
74
|
super.onPropertyChanged(property);
|
|
66
75
|
this.dataFrame.tags[property.name] = `${property.get(this)}`;
|
|
67
76
|
if (!this.initialized || IS_PROPERTY_CHANGING)
|
|
@@ -71,7 +80,6 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
71
80
|
|
|
72
81
|
if (propName === 'scaling' && typeof this.dataFrame !== 'undefined') {
|
|
73
82
|
const activityCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
74
|
-
// const minActivity = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY).stats.min;
|
|
75
83
|
const minActivity = activityCol.stats.min;
|
|
76
84
|
if (minActivity && minActivity <= 0 && this.scaling !== 'none') {
|
|
77
85
|
grok.shell.warning(`Could not apply ${this.scaling}: ` +
|
|
@@ -84,7 +92,8 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
84
92
|
if (!this.showSubstitution && ['maxSubstitutions', 'activityLimit'].includes(propName))
|
|
85
93
|
return;
|
|
86
94
|
|
|
87
|
-
await this.requestDataUpdate();
|
|
95
|
+
// await this.requestDataUpdate();
|
|
96
|
+
this.model.updateDefault();
|
|
88
97
|
this.render(true);
|
|
89
98
|
}
|
|
90
99
|
}
|
|
@@ -95,7 +104,6 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
95
104
|
export class SARViewer extends SARViewerBase {
|
|
96
105
|
_titleHost = ui.divText('Monomer-Positions', {id: 'pep-viewer-title'});
|
|
97
106
|
_name = 'Structure-Activity Relationship';
|
|
98
|
-
tempName = 'sarViewer';
|
|
99
107
|
|
|
100
108
|
constructor() {super();}
|
|
101
109
|
|
|
@@ -103,13 +111,16 @@ export class SARViewer extends SARViewerBase {
|
|
|
103
111
|
|
|
104
112
|
async onTableAttached(): Promise<void> {
|
|
105
113
|
await super.onTableAttached();
|
|
106
|
-
this.
|
|
114
|
+
this.model.sarViewer ??= this;
|
|
115
|
+
// this.dataFrame.temp['sarViewer'] = this;
|
|
107
116
|
|
|
108
117
|
this.subs.push(this.model.onSARGridChanged.subscribe((data) => {
|
|
109
118
|
this.viewerGrid = data;
|
|
110
119
|
this.render();
|
|
111
120
|
}));
|
|
112
121
|
|
|
122
|
+
this.model.updateDefault();
|
|
123
|
+
this.viewerGrid = this.model._sarGrid;
|
|
113
124
|
this.initialized = true;
|
|
114
125
|
this.render();
|
|
115
126
|
}
|
|
@@ -117,11 +128,11 @@ export class SARViewer extends SARViewerBase {
|
|
|
117
128
|
isInitialized(): DG.Grid {return this.model?._sarGrid;}
|
|
118
129
|
|
|
119
130
|
//1. debouncing in rxjs; 2. flags?
|
|
120
|
-
|
|
131
|
+
onPropertyChanged(property: DG.Property): void {
|
|
121
132
|
if (!this.isInitialized() || IS_PROPERTY_CHANGING)
|
|
122
133
|
return;
|
|
123
134
|
|
|
124
|
-
|
|
135
|
+
super.onPropertyChanged(property);
|
|
125
136
|
IS_PROPERTY_CHANGING = true;
|
|
126
137
|
this.model.syncProperties(true);
|
|
127
138
|
IS_PROPERTY_CHANGING = false;
|
|
@@ -132,7 +143,6 @@ export class SARViewer extends SARViewerBase {
|
|
|
132
143
|
export class SARViewerVertical extends SARViewerBase {
|
|
133
144
|
_name = 'Sequence-Activity relationship';
|
|
134
145
|
_titleHost = ui.divText('Most Potent Residues', {id: 'pep-viewer-title'});
|
|
135
|
-
tempName = 'sarViewerVertical';
|
|
136
146
|
|
|
137
147
|
constructor() {
|
|
138
148
|
super();
|
|
@@ -142,24 +152,27 @@ export class SARViewerVertical extends SARViewerBase {
|
|
|
142
152
|
|
|
143
153
|
async onTableAttached(): Promise<void> {
|
|
144
154
|
await super.onTableAttached();
|
|
145
|
-
this.
|
|
155
|
+
this.model.sarViewerVertical ??= this;
|
|
146
156
|
|
|
147
157
|
this.subs.push(this.model.onSARVGridChanged.subscribe((data) => {
|
|
148
158
|
this.viewerGrid = data;
|
|
149
159
|
this.render();
|
|
150
160
|
}));
|
|
151
161
|
|
|
162
|
+
this.model.updateDefault();
|
|
163
|
+
this.viewerGrid = this.model._sarVGrid;
|
|
164
|
+
|
|
152
165
|
this.initialized = true;
|
|
153
166
|
this.render();
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
isInitialized(): DG.Grid {return this.model?._sarVGrid;}
|
|
157
170
|
|
|
158
|
-
|
|
171
|
+
onPropertyChanged(property: DG.Property): void {
|
|
159
172
|
if (!this.isInitialized() || IS_PROPERTY_CHANGING)
|
|
160
173
|
return;
|
|
161
174
|
|
|
162
|
-
|
|
175
|
+
super.onPropertyChanged(property);
|
|
163
176
|
IS_PROPERTY_CHANGING = true;
|
|
164
177
|
this.model.syncProperties(false);
|
|
165
178
|
IS_PROPERTY_CHANGING = false;
|
|
@@ -1,11 +1,15 @@
|
|
|
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
|
+
|
|
5
|
+
import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
6
|
+
|
|
4
7
|
import '../styles.css';
|
|
5
8
|
import * as C from '../utils/constants';
|
|
6
|
-
import {getSeparator} from '../utils/misc';
|
|
7
9
|
import {PeptidesModel} from '../model';
|
|
8
10
|
import {_package} from '../package';
|
|
11
|
+
import $ from 'cash-dom';
|
|
12
|
+
import {scaleActivity} from '../utils/misc';
|
|
9
13
|
|
|
10
14
|
/** Peptide analysis widget.
|
|
11
15
|
*
|
|
@@ -13,11 +17,12 @@ import {_package} from '../package';
|
|
|
13
17
|
* @param {DG.Column} col Aligned sequence column
|
|
14
18
|
* @return {Promise<DG.Widget>} Widget containing peptide analysis */
|
|
15
19
|
export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Column): Promise<DG.Widget> {
|
|
20
|
+
const funcs = DG.Func.find({package: 'Bio', name: 'webLogoViewer'});
|
|
21
|
+
if (funcs.length == 0)
|
|
22
|
+
return new DG.Widget(ui.label('Bio package is missing or out of date. Please install the latest version.'));
|
|
16
23
|
let tempCol = null;
|
|
17
24
|
let scaledDf: DG.DataFrame;
|
|
18
25
|
let newScaledColName: string;
|
|
19
|
-
const separator = getSeparator(col);
|
|
20
|
-
col.tags[C.TAGS.SEPARATOR] ??= separator;
|
|
21
26
|
|
|
22
27
|
for (const column of currentDf.columns.numerical)
|
|
23
28
|
tempCol = column.type === DG.TYPE.FLOAT ? column : null;
|
|
@@ -30,8 +35,7 @@ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Col
|
|
|
30
35
|
async (currentMethod: string): Promise<void> => {
|
|
31
36
|
const currentActivityCol = activityColumnChoice.value?.name;
|
|
32
37
|
|
|
33
|
-
[scaledDf, newScaledColName] =
|
|
34
|
-
currentMethod, currentDf, currentActivityCol, true);
|
|
38
|
+
[scaledDf, newScaledColName] = scaleActivity(currentMethod, currentDf, currentActivityCol, true);
|
|
35
39
|
|
|
36
40
|
const hist = scaledDf.plot.histogram({
|
|
37
41
|
filteringEnabled: false,
|
|
@@ -63,12 +67,14 @@ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Col
|
|
|
63
67
|
});
|
|
64
68
|
startBtn.style.alignSelf = 'center';
|
|
65
69
|
|
|
66
|
-
const viewer = await currentDf.plot.fromType('WebLogo');
|
|
70
|
+
const viewer = await currentDf.plot.fromType('WebLogo') as WebLogo;
|
|
67
71
|
viewer.root.style.setProperty('height', '130px');
|
|
72
|
+
const logoHost = ui.div();
|
|
73
|
+
$(logoHost).empty().append(viewer.root);
|
|
68
74
|
|
|
69
75
|
return new DG.Widget(
|
|
70
76
|
ui.divV([
|
|
71
|
-
|
|
77
|
+
logoHost,
|
|
72
78
|
ui.splitH([
|
|
73
79
|
ui.splitV([ui.inputs(inputsList), startBtn]),
|
|
74
80
|
histogramHost,
|
|
@@ -95,10 +101,20 @@ export async function startAnalysis(
|
|
|
95
101
|
activityScaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
|
|
96
102
|
newDf.columns.add(activityScaledCol);
|
|
97
103
|
newDf.name = 'Peptides analysis';
|
|
98
|
-
newDf.
|
|
99
|
-
newDf.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
104
|
+
newDf.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newScaledColName;
|
|
105
|
+
// newDf.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
106
|
+
|
|
107
|
+
const alignedSeqColUnits = alignedSeqCol.getTag(DG.TAGS.UNITS);
|
|
108
|
+
let monomerType = 'HELM_AA';
|
|
109
|
+
if (alignedSeqColUnits == 'HELM') {
|
|
110
|
+
const sampleSeq = alignedSeqCol.get(0)!;
|
|
111
|
+
monomerType = sampleSeq.startsWith('PEPTIDE') ? 'HELM_AA' : 'HELM_BASE';
|
|
112
|
+
} else {
|
|
113
|
+
monomerType = alignedSeqColUnits.split(':')[2] == 'PT' ? 'HELM_AA' : 'HELM_BASE';
|
|
114
|
+
}
|
|
115
|
+
newDf.setTag('monomerType', monomerType);
|
|
100
116
|
|
|
101
|
-
model = await PeptidesModel.getInstance(newDf
|
|
117
|
+
model = await PeptidesModel.getInstance(newDf);
|
|
102
118
|
} else
|
|
103
119
|
grok.shell.error('The activity column must be of floating point number type!');
|
|
104
120
|
progress.close();
|