@datagrok/peptides 0.0.1 → 0.4.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/.eslintrc.json +1 -1
- package/detectors.js +2 -2
- package/package.json +19 -12
- package/scripts/smiles-to-3D.py +13 -0
- package/src/{peptide-sar-viewer/describe.ts → describe.ts} +104 -78
- package/src/package.ts +79 -137
- package/src/peptides.ts +76 -0
- package/src/utils/cell-renderer.ts +77 -101
- package/src/utils/chem-palette.ts +80 -53
- package/src/utils/correlation-analysis.ts +126 -0
- package/src/utils/molecular-measure.ts +175 -0
- package/src/utils/peptide-similarity-space.ts +242 -0
- package/src/utils/split-aligned.ts +65 -0
- package/src/{peptide-logo-viewer → viewers}/logo-viewer.ts +6 -4
- package/src/viewers/model.ts +76 -0
- package/src/{peptide-sar-viewer → viewers}/sar-viewer.ts +67 -23
- package/src/{stacked-barchart → viewers}/stacked-barchart-viewer.ts +29 -31
- package/src/widgets/analyze-peptides.ts +87 -0
- package/src/widgets/manual-alignment.ts +36 -0
- package/src/widgets/peptide-molecule.ts +42 -0
- package/src/workers/dimensionality-reducer.ts +29 -0
- package/tsconfig.json +12 -13
- package/webpack.config.js +4 -4
- package/src/split-aligned.ts +0 -42
- package/src/utils/misc.ts +0 -101
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
|
|
3
|
+
import {describe} from '../describe';
|
|
4
|
+
import {Subject} from 'rxjs';
|
|
5
|
+
|
|
6
|
+
class SARViewerModel {
|
|
7
|
+
private viewerGrid: Subject<DG.Grid> = new Subject<DG.Grid>();
|
|
8
|
+
private viewerVGrid: Subject<DG.Grid> = new Subject<DG.Grid>();
|
|
9
|
+
private statsDf: Subject<DG.DataFrame> = new Subject<DG.DataFrame>();
|
|
10
|
+
private groupMapping: Subject<{[key: string]: string}> = new Subject<{[key: string]: string}>();
|
|
11
|
+
public viewerGrid$;
|
|
12
|
+
public viewerVGrid$;
|
|
13
|
+
public statsDf$;
|
|
14
|
+
public groupMapping$;
|
|
15
|
+
private dataFrame: DG.DataFrame | null;
|
|
16
|
+
private activityColumn: string | null;
|
|
17
|
+
private activityScaling: string | null;
|
|
18
|
+
private sourceGrid: DG.Grid | null;
|
|
19
|
+
private twoColorMode: boolean | null;
|
|
20
|
+
private initialBitset: DG.BitSet | null;
|
|
21
|
+
private isUpdating = false;
|
|
22
|
+
grouping: boolean;
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
this.dataFrame = null;
|
|
26
|
+
this.activityColumn = null;
|
|
27
|
+
this.activityScaling = null;
|
|
28
|
+
this.sourceGrid = null;
|
|
29
|
+
this.twoColorMode = null;
|
|
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();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async updateData(
|
|
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
|
+
) {
|
|
47
|
+
this.dataFrame = df;
|
|
48
|
+
this.activityColumn = activityCol;
|
|
49
|
+
this.activityScaling = activityScaling;
|
|
50
|
+
this.sourceGrid = sourceGrid;
|
|
51
|
+
this.twoColorMode = twoColorMode;
|
|
52
|
+
this.initialBitset = initialBitset;
|
|
53
|
+
this.grouping = grouping;
|
|
54
|
+
await this.updateDefault();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async updateDefault() {
|
|
58
|
+
if (
|
|
59
|
+
this.dataFrame && this.activityColumn && this.activityScaling &&
|
|
60
|
+
this.sourceGrid && this.twoColorMode !== null && !this.isUpdating
|
|
61
|
+
) {
|
|
62
|
+
this.isUpdating = true;
|
|
63
|
+
const [viewerGrid, viewerVGrid, statsDf, groupMapping] = await describe(
|
|
64
|
+
this.dataFrame, this.activityColumn, this.activityScaling,
|
|
65
|
+
this.sourceGrid, this.twoColorMode, this.initialBitset, this.grouping,
|
|
66
|
+
);
|
|
67
|
+
this.viewerGrid.next(viewerGrid);
|
|
68
|
+
this.viewerVGrid.next(viewerVGrid);
|
|
69
|
+
this.statsDf.next(statsDf);
|
|
70
|
+
this.groupMapping.next(groupMapping);
|
|
71
|
+
this.isUpdating = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const model = new SARViewerModel();
|
|
@@ -4,12 +4,11 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {model} from './model';
|
|
8
8
|
|
|
9
|
-
export class
|
|
9
|
+
export class SARViewer extends DG.JsViewer {
|
|
10
10
|
protected viewerGrid: DG.Grid | null;
|
|
11
11
|
protected sourceGrid: DG.Grid | null;
|
|
12
|
-
protected progress: DG.TaskBarProgressIndicator;
|
|
13
12
|
protected activityColumnColumnName: string;
|
|
14
13
|
protected activityScalingMethod: string;
|
|
15
14
|
protected bidirectionalAnalysis: boolean;
|
|
@@ -21,16 +20,19 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
21
20
|
protected _initialBitset: DG.BitSet | null;
|
|
22
21
|
protected viewerVGrid: DG.Grid | null;
|
|
23
22
|
protected currentBitset: DG.BitSet | null;
|
|
23
|
+
grouping: boolean;
|
|
24
|
+
groupMapping: {[key: string]: string} | null;
|
|
25
|
+
// private df: DG.DataFrame | null;
|
|
24
26
|
// protected pValueThreshold: number;
|
|
25
27
|
// protected amountOfBestAARs: number;
|
|
26
28
|
// duplicatesHandingMethod: string;
|
|
27
29
|
constructor() {
|
|
28
30
|
super();
|
|
29
|
-
this.progress = DG.TaskBarProgressIndicator.create('Loading SAR viewer');
|
|
30
31
|
|
|
31
32
|
this.viewerGrid = null;
|
|
32
33
|
this.viewerVGrid = null;
|
|
33
34
|
this.statsDf = null;
|
|
35
|
+
this.groupMapping = null;
|
|
34
36
|
this.initialized = false;
|
|
35
37
|
this.aminoAcidResidue = 'AAR';
|
|
36
38
|
this._initialBitset = null;
|
|
@@ -42,6 +44,7 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
42
44
|
this.activityScalingMethod = this.string('activityScalingMethod', 'none', {choices: ['none', 'lg', '-lg']});
|
|
43
45
|
this.filterMode = this.bool('filterMode', false);
|
|
44
46
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
47
|
+
this.grouping = this.bool('grouping', false);
|
|
45
48
|
// this.pValueThreshold = this.float('pValueThreshold', 0.1);
|
|
46
49
|
// this.amountOfBestAARs = this.int('amountOfBestAAR', 1);
|
|
47
50
|
// this.duplicatesHandingMethod = this.string('duplicatesHandlingMethod', 'median', {choices: ['median']});
|
|
@@ -52,10 +55,18 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
52
55
|
init() {
|
|
53
56
|
this._initialBitset = this.dataFrame!.filter.clone();
|
|
54
57
|
this.initialized = true;
|
|
58
|
+
this.subs.push(model.statsDf$.subscribe((data) => this.statsDf = data));
|
|
59
|
+
this.subs.push(model.viewerGrid$.subscribe((data) => {
|
|
60
|
+
this.viewerGrid = data;
|
|
61
|
+
this.render();
|
|
62
|
+
}));
|
|
63
|
+
this.subs.push(model.viewerVGrid$.subscribe((data) => this.viewerVGrid = data));
|
|
64
|
+
this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
onTableAttached() {
|
|
58
68
|
this.sourceGrid = this.view.grid;
|
|
69
|
+
this.sourceGrid?.dataFrame?.setTag('dataType', 'peptides');
|
|
59
70
|
this.render();
|
|
60
71
|
}
|
|
61
72
|
|
|
@@ -73,7 +84,7 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
73
84
|
|
|
74
85
|
if (property.name === 'activityScalingMethod' && typeof this.dataFrame !== 'undefined') {
|
|
75
86
|
const minActivity = DG.Stats.fromColumn(
|
|
76
|
-
this.dataFrame
|
|
87
|
+
this.dataFrame!.col(this.activityColumnColumnName)!,
|
|
77
88
|
this._initialBitset,
|
|
78
89
|
).min;
|
|
79
90
|
if (minActivity && minActivity <= 0 && this.activityScalingMethod !== 'none') {
|
|
@@ -103,12 +114,15 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
103
114
|
const otherLabel = 'Other';
|
|
104
115
|
const aarLabel = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
105
116
|
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
let splitCol = this.dataFrame.col(splitColName);
|
|
118
|
+
if (!splitCol) {
|
|
119
|
+
splitCol = this.dataFrame.columns.addNew(splitColName, 'string');
|
|
108
120
|
}
|
|
109
121
|
|
|
110
|
-
const isChosen = (i: number) => this.dataFrame!.get(currentPosition, i) === currentAAR;
|
|
111
|
-
|
|
122
|
+
const isChosen = (i: number) => this.groupMapping![this.dataFrame!.get(currentPosition, i)] === currentAAR;
|
|
123
|
+
splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
|
|
124
|
+
|
|
125
|
+
//TODO: use column.compact
|
|
112
126
|
|
|
113
127
|
// if (this.filterMode) {
|
|
114
128
|
// this.dataFrame.selection.setAll(false, false);
|
|
@@ -118,7 +132,8 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
118
132
|
// this.dataFrame.selection.init(isChosen).and(this._initialBitset!, false);
|
|
119
133
|
// }
|
|
120
134
|
this.currentBitset = DG.BitSet.create(this.dataFrame.rowCount, isChosen).and(this._initialBitset!);
|
|
121
|
-
// (this.filterMode ? this.dataFrame.selection.setAll(false) :
|
|
135
|
+
// (this.filterMode ? this.dataFrame.selection.setAll(false) :
|
|
136
|
+
// this.dataFrame.filter.copyFrom(this._initialBitset!)).fireChanged();
|
|
122
137
|
this.sourceFilteringFunc();
|
|
123
138
|
|
|
124
139
|
|
|
@@ -143,19 +158,21 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
143
158
|
|
|
144
159
|
private accordionFunc(accordion: DG.Accordion) {
|
|
145
160
|
if (accordion.context instanceof DG.RowGroup) {
|
|
146
|
-
const originalDf: DG.DataFrame = accordion.context.dataFrame;
|
|
161
|
+
const originalDf: DG.DataFrame = DG.toJs(accordion.context.dataFrame);
|
|
162
|
+
const viewerDf = this.viewerGrid?.dataFrame;
|
|
147
163
|
|
|
148
164
|
if (
|
|
149
165
|
originalDf.getTag('dataType') === 'peptides' &&
|
|
150
166
|
originalDf.col('~splitCol') &&
|
|
151
167
|
this.viewerGrid &&
|
|
152
|
-
|
|
168
|
+
viewerDf &&
|
|
169
|
+
viewerDf.currentCol !== null
|
|
153
170
|
) {
|
|
154
|
-
const currentAAR: string =
|
|
171
|
+
const currentAAR: string = viewerDf.get(
|
|
155
172
|
this.aminoAcidResidue,
|
|
156
|
-
|
|
173
|
+
viewerDf.currentRowIdx,
|
|
157
174
|
);
|
|
158
|
-
const currentPosition =
|
|
175
|
+
const currentPosition = viewerDf.currentCol.name;
|
|
159
176
|
|
|
160
177
|
const labelStr = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
|
|
161
178
|
const currentColor = DG.Color.toHtml(DG.Color.orange);
|
|
@@ -187,7 +204,8 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
187
204
|
for (const colName of new Set(['Count', 'pValue', 'Mean difference'])) {
|
|
188
205
|
const query = `${this.aminoAcidResidue} = ${currentAAR} and Position = ${currentPosition}`;
|
|
189
206
|
const textNum = this.statsDf?.groupBy([colName]).where(query).aggregate().get(colName, 0);
|
|
190
|
-
const text = textNum === 0 ? '<0.01' : `${colName === 'Count' ? textNum : textNum.toFixed(2)}`;
|
|
207
|
+
// const text = textNum === 0 ? '<0.01' : `${colName === 'Count' ? textNum : textNum.toFixed(2)}`;
|
|
208
|
+
const text = colName === 'Count' ? `${textNum}` : textNum < 0.01 ? '<0.01' : textNum.toFixed(2);
|
|
191
209
|
tableMap[colName === 'pValue' ? 'p-value' : colName] = text;
|
|
192
210
|
}
|
|
193
211
|
elements.push(ui.tableFromMap(tableMap));
|
|
@@ -249,33 +267,59 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
249
267
|
//TODO: optimize. Don't calculate everything again if only view changes
|
|
250
268
|
if (computeData) {
|
|
251
269
|
if (typeof this.dataFrame !== 'undefined' && this.activityColumnColumnName && this.sourceGrid) {
|
|
252
|
-
[this.viewerGrid, this.viewerVGrid, this.statsDf] = await describe(
|
|
253
|
-
|
|
270
|
+
// [this.viewerGrid, this.viewerVGrid, this.statsDf] = await describe(
|
|
271
|
+
// this.dataFrame,
|
|
272
|
+
// this.activityColumnColumnName,
|
|
273
|
+
// this.activityScalingMethod,
|
|
274
|
+
// this.sourceGrid,
|
|
275
|
+
// this.bidirectionalAnalysis,
|
|
276
|
+
// this._initialBitset,
|
|
277
|
+
// );
|
|
278
|
+
await model?.updateData(
|
|
279
|
+
this.dataFrame!,
|
|
254
280
|
this.activityColumnColumnName,
|
|
255
281
|
this.activityScalingMethod,
|
|
256
282
|
this.sourceGrid,
|
|
257
283
|
this.bidirectionalAnalysis,
|
|
258
284
|
this._initialBitset,
|
|
285
|
+
this.grouping,
|
|
259
286
|
);
|
|
260
287
|
|
|
261
288
|
if (this.viewerGrid !== null && this.viewerVGrid !== null) {
|
|
262
289
|
$(this.root).empty();
|
|
263
|
-
this.root.appendChild(
|
|
290
|
+
this.root.appendChild(this.viewerGrid.root);
|
|
264
291
|
this.viewerGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
|
|
265
292
|
this.applyBitset();
|
|
266
293
|
this.syncGridsFunc(false);
|
|
267
294
|
});
|
|
268
295
|
this.viewerVGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => this.syncGridsFunc(true));
|
|
269
|
-
this.dataFrame
|
|
270
|
-
|
|
296
|
+
this.dataFrame!.onRowsFiltering.subscribe((_) => this.sourceFilteringFunc());
|
|
271
297
|
grok.events.onAccordionConstructed.subscribe((accordion: DG.Accordion) => this.accordionFunc(accordion));
|
|
272
298
|
}
|
|
273
299
|
}
|
|
274
300
|
}
|
|
275
301
|
//fixes viewers not rendering immediately after analyze.
|
|
276
|
-
this.viewerVGrid?.invalidate();
|
|
277
302
|
this.viewerGrid?.invalidate();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
278
305
|
|
|
279
|
-
|
|
306
|
+
export class SARViewerVertical extends DG.JsViewer {
|
|
307
|
+
viewerVGrid: DG.Grid | null;
|
|
308
|
+
constructor() {
|
|
309
|
+
super();
|
|
310
|
+
|
|
311
|
+
this.viewerVGrid = null;
|
|
312
|
+
this.subs.push(model.viewerVGrid$.subscribe((data) => {
|
|
313
|
+
this.viewerVGrid = data;
|
|
314
|
+
this.render();
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
render() {
|
|
319
|
+
if (this.viewerVGrid) {
|
|
320
|
+
$(this.root).empty();
|
|
321
|
+
this.root.appendChild(this.viewerVGrid.root);
|
|
322
|
+
}
|
|
323
|
+
this.viewerVGrid?.invalidate();
|
|
280
324
|
}
|
|
281
325
|
}
|
|
@@ -2,13 +2,14 @@ import * as DG from 'datagrok-api/dg';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import {scaleBand, scaleLinear} from 'd3';
|
|
4
4
|
import {ChemPalette} from '../utils/chem-palette';
|
|
5
|
-
//@ts-ignore: I should be able to install it somehow
|
|
6
5
|
import * as rxjs from 'rxjs';
|
|
7
6
|
const cp = new ChemPalette('grok');
|
|
8
7
|
|
|
8
|
+
//TODO: the function should not accept promise. Await the parameters where it is used
|
|
9
9
|
export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
10
10
|
viewer.then((viewer) => {
|
|
11
|
-
const barchart = viewer as StackedBarChart;
|
|
11
|
+
const barchart = viewer as StackedBarChart; //TODO: accept specifically StackedBarChart object
|
|
12
|
+
// The following event makes the barchart interactive
|
|
12
13
|
rxjs.fromEvent(grid.overlay, 'mousemove').subscribe((mm:any) => {
|
|
13
14
|
mm = mm as MouseEvent;
|
|
14
15
|
const cell = grid.hitTest(mm.offsetX, mm.offsetY);
|
|
@@ -73,9 +74,9 @@ export function addViewerToHeader(grid: DG.Grid, viewer: Promise<DG.Widget>) {
|
|
|
73
74
|
export class StackedBarChart extends DG.JsViewer {
|
|
74
75
|
public dataEmptyAA: string;
|
|
75
76
|
public initialized: boolean;
|
|
76
|
-
highlighted:{'colName':string, 'aaName':string}|null = null;
|
|
77
|
+
highlighted: {'colName' : string, 'aaName' : string} | null = null;
|
|
77
78
|
private ord: { [Key: string]: number; } = {};
|
|
78
|
-
private margin: {
|
|
79
|
+
private margin: {top: number; left: number; bottom: number; right: number} = {
|
|
79
80
|
top: 10,
|
|
80
81
|
right: 10,
|
|
81
82
|
bottom: 50,
|
|
@@ -83,18 +84,17 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
83
84
|
};
|
|
84
85
|
private yScale: any;
|
|
85
86
|
private xScale: any;
|
|
86
|
-
private data: {
|
|
87
|
+
private data: {'name': string, 'data': {'name': string, 'count': number, 'selectedCount': number}[]}[] = [];
|
|
87
88
|
private selectionMode: boolean = false;
|
|
88
89
|
public aminoColumnNames: string[] = [];
|
|
89
|
-
// @ts-ignore
|
|
90
90
|
|
|
91
|
-
private aminoColumnIndices: {
|
|
92
|
-
private aggregatedTables: {
|
|
93
|
-
private aggregatedTablesUnselected: {
|
|
91
|
+
private aminoColumnIndices: {[Key: string]: number} = {};
|
|
92
|
+
private aggregatedTables: {[Key: string]: DG.DataFrame} = {};
|
|
93
|
+
private aggregatedTablesUnselected: {[Key: string]: DG.DataFrame} = {};
|
|
94
94
|
private max = 0;
|
|
95
|
-
private barStats: {
|
|
95
|
+
private barStats: {[Key: string]: {'name': string, 'count': number, 'selectedCount': number}[]} = {};
|
|
96
96
|
tableCanvas: HTMLCanvasElement | undefined;
|
|
97
|
-
private registered: {
|
|
97
|
+
private registered: {[Key: string]: DG.GridCell} = {};
|
|
98
98
|
|
|
99
99
|
constructor() {
|
|
100
100
|
super();
|
|
@@ -103,22 +103,22 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
init() {
|
|
106
|
-
const groups: [string
|
|
107
|
-
[
|
|
108
|
-
[
|
|
109
|
-
[
|
|
110
|
-
[
|
|
111
|
-
[
|
|
112
|
-
[
|
|
106
|
+
const groups: {[key: string]: string[]} = {
|
|
107
|
+
'yellow': ['C', 'U'],
|
|
108
|
+
'red': ['G', 'P'],
|
|
109
|
+
'all_green': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
|
|
110
|
+
'light_blue': ['R', 'H', 'K'],
|
|
111
|
+
'dark_blue': ['D', 'E'],
|
|
112
|
+
'orange': ['S', 'T', 'N', 'Q'],
|
|
113
|
+
};
|
|
113
114
|
|
|
114
115
|
let i = 0;
|
|
115
|
-
|
|
116
|
+
for (const value of Object.values(groups)) {
|
|
116
117
|
i++;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.ord[item[0][obj]] = i;
|
|
118
|
+
for (const obj of value) {
|
|
119
|
+
this.ord[obj] = i;
|
|
120
120
|
}
|
|
121
|
-
}
|
|
121
|
+
}
|
|
122
122
|
this.yScale = scaleLinear();
|
|
123
123
|
this.xScale = scaleBand();
|
|
124
124
|
this.data = [];
|
|
@@ -152,8 +152,8 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
152
152
|
{
|
|
153
153
|
// @ts-ignore
|
|
154
154
|
if (df.getCol(name).semType === 'aminoAcids' &&
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
!df.getCol(name).categories.includes('COOH') &&
|
|
156
|
+
!df.getCol(name).categories.includes('NH2')) {
|
|
157
157
|
this.aminoColumnIndices[name] = this.aminoColumnNames.length + 1;
|
|
158
158
|
this.aminoColumnNames.push(name);
|
|
159
159
|
}
|
|
@@ -170,7 +170,7 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
170
170
|
resbuf[i] = buf1[i] & buf2[i];
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
//TODO: optimize it, why store so many tables?
|
|
174
174
|
const mask = DG.BitSet.fromBytes(resbuf.buffer, df.rowCount);
|
|
175
175
|
if (mask.trueCount !== df.filter.trueCount) {
|
|
176
176
|
this.selectionMode = true;
|
|
@@ -214,10 +214,9 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
214
214
|
this.barStats = {};
|
|
215
215
|
for (const [name, df] of Object.entries(this.aggregatedTables)) {
|
|
216
216
|
const colObj: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
{'name': name, 'data': []};
|
|
217
|
+
'name': string,
|
|
218
|
+
'data': { 'name': string, 'count': number, 'selectedCount': number }[],
|
|
219
|
+
} = {'name': name, 'data': []};
|
|
221
220
|
this.barStats[colObj['name']] = colObj['data'];
|
|
222
221
|
this.data.push(colObj);
|
|
223
222
|
for (let i = 0; i < df.rowCount; i++) {
|
|
@@ -263,7 +262,6 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
263
262
|
g.fillStyle = 'black';
|
|
264
263
|
g.textBaseline = 'top';
|
|
265
264
|
g.font = `${h * margin / 2}px`;
|
|
266
|
-
// eslint-disable-next-line no-unused-vars
|
|
267
265
|
|
|
268
266
|
const name = cell.tableColumn!.name;
|
|
269
267
|
const colNameSize = g.measureText(name).width;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
import {Peptides} from '../peptides';
|
|
5
|
+
|
|
6
|
+
export async function analyzePeptidesWidget(
|
|
7
|
+
col: DG.Column, view: DG.TableView, tableGrid: DG.Grid, currentDf: DG.DataFrame,
|
|
8
|
+
): Promise<DG.Widget> {
|
|
9
|
+
let tempCol = null;
|
|
10
|
+
for (const column of currentDf.columns.numerical) {
|
|
11
|
+
tempCol = column.type === DG.TYPE.FLOAT ? column : null;
|
|
12
|
+
}
|
|
13
|
+
const defaultColumn: DG.Column = currentDf.col('activity') || currentDf.col('IC50') || tempCol;
|
|
14
|
+
const histogramHost = ui.div([]);
|
|
15
|
+
|
|
16
|
+
let hist: DG.Viewer;
|
|
17
|
+
|
|
18
|
+
const activityScalingMethod = ui.choiceInput(
|
|
19
|
+
'Activity scaling',
|
|
20
|
+
'none',
|
|
21
|
+
['none', 'lg', '-lg'],
|
|
22
|
+
async (currentMethod: string) => {
|
|
23
|
+
const currentActivityCol = activityColumnChoice.value.name;
|
|
24
|
+
const tempDf = currentDf.clone(currentDf.filter, [currentActivityCol]);
|
|
25
|
+
//TODO: merge with scaling in describe
|
|
26
|
+
switch (currentMethod) {
|
|
27
|
+
case 'lg':
|
|
28
|
+
await tempDf.columns.addNewCalculated('scaledActivity', 'Log10(${' + currentActivityCol + '})');
|
|
29
|
+
break;
|
|
30
|
+
case '-lg':
|
|
31
|
+
await tempDf.columns.addNewCalculated('scaledActivity', '-1*Log10(${' + currentActivityCol + '})');
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
await tempDf.columns.addNewCalculated('scaledActivity', '${' + currentActivityCol + '}');
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
hist = tempDf.plot.histogram({
|
|
38
|
+
filteringEnabled: false,
|
|
39
|
+
valueColumnName: 'scaledActivity',
|
|
40
|
+
legendVisibility: 'Never',
|
|
41
|
+
showXAxis: true,
|
|
42
|
+
showColumnSelector: false,
|
|
43
|
+
showRangeSlider: false,
|
|
44
|
+
// bins: b,
|
|
45
|
+
});
|
|
46
|
+
histogramHost.lastChild?.remove();
|
|
47
|
+
histogramHost.appendChild(hist.root);
|
|
48
|
+
});
|
|
49
|
+
activityScalingMethod.setTooltip('Function to apply for each value in activity column');
|
|
50
|
+
|
|
51
|
+
const activityScalingMethodState = function(_: any) {
|
|
52
|
+
activityScalingMethod.enabled =
|
|
53
|
+
activityColumnChoice.value && DG.Stats.fromColumn(activityColumnChoice.value, currentDf.filter).min > 0;
|
|
54
|
+
activityScalingMethod.fireChanged();
|
|
55
|
+
};
|
|
56
|
+
const activityColumnChoice = ui.columnInput(
|
|
57
|
+
'Activity column',
|
|
58
|
+
currentDf,
|
|
59
|
+
defaultColumn,
|
|
60
|
+
activityScalingMethodState,
|
|
61
|
+
);
|
|
62
|
+
activityColumnChoice.fireChanged();
|
|
63
|
+
activityScalingMethod.fireChanged();
|
|
64
|
+
|
|
65
|
+
const startBtn = ui.button('Launch SAR', async () => {
|
|
66
|
+
if (activityColumnChoice.value.type === DG.TYPE.FLOAT) {
|
|
67
|
+
const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
|
|
68
|
+
const options: {[key: string]: string} = {
|
|
69
|
+
'activityColumnColumnName': activityColumnChoice.value.name,
|
|
70
|
+
'activityScalingMethod': activityScalingMethod.value,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const peptides = new Peptides();
|
|
74
|
+
await peptides.init(tableGrid, view, currentDf, options, col, activityColumnChoice.value.name);
|
|
75
|
+
|
|
76
|
+
progress.close();
|
|
77
|
+
} else {
|
|
78
|
+
grok.shell.error('The activity column must be of floating point number type!');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const viewer = await currentDf.plot.fromType('peptide-logo-viewer');
|
|
83
|
+
|
|
84
|
+
return new DG.Widget(
|
|
85
|
+
ui.divV([viewer.root, ui.inputs([activityColumnChoice, activityScalingMethod]), startBtn, histogramHost]),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as DG from 'datagrok-api/dg';
|
|
3
|
+
|
|
4
|
+
import $ from 'cash-dom';
|
|
5
|
+
import {model} from '../viewers/model';
|
|
6
|
+
import {splitAlignedPeptides} from '../utils/split-aligned';
|
|
7
|
+
|
|
8
|
+
export function manualAlignmentWidget(alignedSequenceCol: DG.Column, currentDf: DG.DataFrame) {
|
|
9
|
+
const sequenceInput = ui.textInput('', alignedSequenceCol.get(currentDf.currentRowIdx));
|
|
10
|
+
(sequenceInput.input as HTMLElement).style.height = '50px';
|
|
11
|
+
(sequenceInput.input as HTMLElement).style.overflow = 'hidden';
|
|
12
|
+
|
|
13
|
+
const applyChangesBtn = ui.button('Apply', async () => {
|
|
14
|
+
const newSequence = sequenceInput.value;
|
|
15
|
+
const affectedRowIndex = currentDf.currentRowIdx;
|
|
16
|
+
const [splitSequence] = splitAlignedPeptides(DG.Column.fromStrings('splitSequence', [newSequence]), false);
|
|
17
|
+
|
|
18
|
+
alignedSequenceCol.set(affectedRowIndex, newSequence);
|
|
19
|
+
for (const part of splitSequence.columns) {
|
|
20
|
+
if (currentDf.col(part.name) !== null) {
|
|
21
|
+
currentDf.set(part.name, affectedRowIndex, part.get(0));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await model.updateDefault();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const resetBtn = ui.button(
|
|
29
|
+
ui.iconFA('redo'),
|
|
30
|
+
() => sequenceInput.value = alignedSequenceCol.get(currentDf.currentRowIdx),
|
|
31
|
+
'Reset',
|
|
32
|
+
);
|
|
33
|
+
$(resetBtn).addClass('dt-snippet-editor-icon dt-reset-icon');
|
|
34
|
+
|
|
35
|
+
return new DG.Widget(ui.divV([resetBtn, sequenceInput.root, applyChangesBtn], 'dt-textarea-box'));
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
import {ChemPalette} from '../utils/chem-palette';
|
|
5
|
+
|
|
6
|
+
export async function peptideMoleculeWidget(pep: string): Promise<DG.Widget> {
|
|
7
|
+
const pi = DG.TaskBarProgressIndicator.create('Creating NGL view');
|
|
8
|
+
|
|
9
|
+
const split = pep.split('-');
|
|
10
|
+
const mols = [];
|
|
11
|
+
for (let i = 1; i < split.length - 1; i++) {
|
|
12
|
+
if (split[i] in ChemPalette.AASmiles) {
|
|
13
|
+
const aar = ChemPalette.AASmiles[split[i]];
|
|
14
|
+
mols[i] = aar.substr(0, aar.length - 1);
|
|
15
|
+
} else if (!split[i] || split[i] == '-') {
|
|
16
|
+
mols[i] = '';
|
|
17
|
+
} else {
|
|
18
|
+
return new DG.Widget(ui.divH([]));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const smiles = mols.join('') + 'O';
|
|
22
|
+
let molfileStr = (await grok.functions.call('Peptides:SmiTo3D', {smiles}));
|
|
23
|
+
|
|
24
|
+
molfileStr = molfileStr.replaceAll('\\n', '\n'); ;
|
|
25
|
+
const stringBlob = new Blob([molfileStr], {type: 'text/plain'});
|
|
26
|
+
const nglHost = ui.div([], {classes: 'd4-ngl-viewer', id: 'ngl-3d-host'});
|
|
27
|
+
|
|
28
|
+
//@ts-ignore
|
|
29
|
+
const stage = new NGL.Stage(nglHost, {backgroundColor: 'white'});
|
|
30
|
+
//@ts-ignore
|
|
31
|
+
stage.loadFile(stringBlob, {ext: 'sdf'}).then(function(comp: NGL.StructureComponent) {
|
|
32
|
+
stage.setSize(300, 300);
|
|
33
|
+
comp.addRepresentation('ball+stick');
|
|
34
|
+
comp.autoView();
|
|
35
|
+
});
|
|
36
|
+
const sketch = grok.chem.svgMol(smiles);
|
|
37
|
+
const panel = ui.divH([sketch]);
|
|
38
|
+
|
|
39
|
+
pi.close();
|
|
40
|
+
|
|
41
|
+
return new DG.Widget(ui.div([panel, nglHost]));
|
|
42
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {DimensionalityReducer} from '@datagrok-libraries/utils/src/reduce-dimensionality';
|
|
2
|
+
import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Worker thread receiving data function.
|
|
6
|
+
*
|
|
7
|
+
* @param {any[]} columnData Samples to process.
|
|
8
|
+
* @param {string} method Embedding method.
|
|
9
|
+
* @param {string} measure Distance metric.
|
|
10
|
+
* @param {number} cyclesCount Number of cycles to repeat.
|
|
11
|
+
* @return {Coordinates} Embedding.
|
|
12
|
+
*/
|
|
13
|
+
function onMessage(columnData: [], method: string, measure: string, cyclesCount: number): Coordinates {
|
|
14
|
+
const reducer = new DimensionalityReducer(
|
|
15
|
+
columnData,
|
|
16
|
+
method,
|
|
17
|
+
measure,
|
|
18
|
+
{cycles: cyclesCount},
|
|
19
|
+
);
|
|
20
|
+
return reducer.transform(true);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
self.onmessage = ({data: {columnData, method, measure, cyclesCount}}) => {
|
|
24
|
+
const embedding = onMessage(columnData, method, measure, cyclesCount);
|
|
25
|
+
self.postMessage({
|
|
26
|
+
embedding: embedding,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|