@datagrok/peptides 1.11.3 → 1.13.0

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.
@@ -33,8 +33,8 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
33
33
  const posColCategories = posCol.categories;
34
34
  const posColData = posCol.getRawData();
35
35
 
36
- for (const aar of currentCell[pos]) {
37
- const substitutionsMap = substInfo.get(aar)?.get(pos) as Map<number, type.UTypedArray> | undefined;
36
+ for (const monomer of currentCell[pos]) {
37
+ const substitutionsMap = substInfo.get(monomer)?.get(pos) as Map<number, type.UTypedArray> | undefined;
38
38
  if (typeof substitutionsMap === 'undefined')
39
39
  continue;
40
40
 
@@ -16,16 +16,16 @@ import {ALIGNMENT, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/
16
16
  * @param {DG.DataFrame} df Working table
17
17
  * @param {DG.Column} col Aligned sequence column
18
18
  * @return {Promise<DG.Widget>} Widget containing peptide analysis */
19
- export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
20
- { host: HTMLElement, callback: () => Promise<boolean> } {
19
+ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): {host: HTMLElement, callback: () => Promise<boolean>} {
21
20
  const logoHost = ui.div();
22
- // logoHost.style.alignContent = 'center';
23
21
  let seqColInput: DG.InputBase | null = null;
24
22
  if (typeof col === 'undefined') {
25
23
  const sequenceColumns = df.columns.toList().filter((dfCol) => dfCol.semType === DG.SEMTYPE.MACROMOLECULE);
26
24
  const potentialCol = DG.Utils.firstOrNull(sequenceColumns);
27
25
  if (potentialCol === null)
28
26
  throw new Error('Peptides Error: table doesn\'t contain sequence columns');
27
+ else if (potentialCol.stats.missingValueCount !== 0)
28
+ grok.shell.info('Sequences column contains missing values. They will be ignored during analysis');
29
29
 
30
30
  seqColInput = ui.columnInput('Sequence', df, potentialCol, () => {
31
31
  const seqCol = seqColInput!.value;
@@ -38,8 +38,10 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
38
38
  viewer.root.style.setProperty('height', '130px');
39
39
  return viewer.root;
40
40
  }));
41
- //TODO: add when new version of datagrok-api is available
41
+ if (seqCol.stats.missingValueCount !== 0)
42
+ grok.shell.info('Sequences column contains missing values. They will be ignored during analysis');
42
43
  }, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE});
44
+ seqColInput.setTooltip('Macromolecule column in FASTA, HELM or separated format');
43
45
  } else if (!(col.getTag(bioTAGS.aligned) === ALIGNMENT.SEQ_MSA) &&
44
46
  col.getTag(DG.TAGS.UNITS) !== NOTATION.HELM) {
45
47
  return {
@@ -65,10 +67,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
65
67
  }
66
68
 
67
69
  let scaledCol: DG.Column<number>;
68
-
69
- const defaultActivityColumn: DG.Column<number> | null =
70
- df.col('activity') || df.col('IC50') || DG.Utils.firstOrNull(df.columns.numerical);
71
- ;
70
+ const defaultActivityColumn: DG.Column<number> | null = df.col('activity') || df.col('IC50') ||
71
+ DG.Utils.firstOrNull(df.columns.numerical);
72
72
  const histogramHost = ui.div([], {id: 'pep-hist-host'});
73
73
 
74
74
  const activityScalingMethod = ui.choiceInput(
@@ -88,23 +88,27 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
88
88
  histogramHost.lastChild?.remove();
89
89
  histogramHost.appendChild(hist.root);
90
90
  }) as DG.InputBase<C.SCALING_METHODS | null>;
91
- activityScalingMethod.setTooltip('Function to apply for each value in activity column');
91
+ activityScalingMethod.setTooltip('Activity column transformation method');
92
92
 
93
93
  const activityScalingMethodState = (): void => {
94
- activityScalingMethod.enabled = (activityColumnChoice.value ?? false) &&
95
- DG.Stats.fromColumn(activityColumnChoice.value!).min > 0;
96
- activityScalingMethod.fireChanged();
94
+ activityScalingMethod.enabled = (activityColumnChoice.value ?? false) && activityColumnChoice.value!.stats.min > 0;
95
+ activityScalingMethod.value = C.SCALING_METHODS.NONE;
96
+ if (activityColumnChoice.value!.stats.missingValueCount !== 0)
97
+ grok.shell.info('Activity column contains missing values. They will be ignored during analysis');
97
98
  };
98
99
  //TODO: add when new version of datagrok-api is available
99
100
  const activityColumnChoice = ui.columnInput('Activity', df, defaultActivityColumn, activityScalingMethodState,
100
101
  {filter: (col: DG.Column) => col.type === DG.TYPE.INT || col.type === DG.TYPE.FLOAT});
101
- const clustersColumnChoice = ui.columnInput('Clusters', df, null);
102
+ activityColumnChoice.setTooltip('Numerical activity column');
103
+ const clustersColumnChoice = ui.columnInput('Clusters', df, null, null);
104
+ clustersColumnChoice.setTooltip('Optional. Clusters column is used to create Logo Summary Table');
102
105
  clustersColumnChoice.nullable = true;
103
106
  activityColumnChoice.fireChanged();
104
107
  activityScalingMethod.fireChanged();
105
108
 
106
- const targetColumnChoice = ui.columnInput('Target', df, null, null,
107
- {filter: (col: DG.Column) => col.type === DG.TYPE.STRING});
109
+ const targetColumnChoice = ui.columnInput('Target', df, null, null, {filter: (col: DG.Column) => col.type === DG.TYPE.STRING});
110
+ targetColumnChoice.setTooltip('Optional. Target represents a unique binding construct for every peptide in the data. ' +
111
+ 'Target can be used to split mutation cliff analysis for peptides specific to a certain set of targets');
108
112
  targetColumnChoice.nullable = true;
109
113
 
110
114
  const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, targetColumnChoice];
@@ -128,7 +132,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
128
132
  const inputElements: HTMLElement[] = [ui.inputs(inputsList)];
129
133
  $(inputElements[0]).find('label').css('width', 'unset');
130
134
  if (typeof col !== 'undefined') {
131
- const startBtn = ui.button('Launch SAR', startAnalysisCallback);
135
+ const startBtn = ui.button('Launch SAR', startAnalysisCallback, '');
132
136
  startBtn.style.alignSelf = 'center';
133
137
  inputElements.push(startBtn);
134
138
  bottomHeight = '215px';
@@ -198,10 +202,14 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
198
202
  newDf.setTag(C.TAGS.UUID, dfUuid);
199
203
  newDf.setTag('monomerType', monomerType);
200
204
 
205
+ const bitset = DG.BitSet.create(currentDf.rowCount,
206
+ (i) => !activityColumn.isNone(i) && !peptidesCol.isNone(i) && currentDf.filter.get(i));
207
+
201
208
  // Cloning dataframe with applied filter. If filter is not applied, cloning is
202
209
  // needed anyway to allow filtering on the original dataframe
203
- model = PeptidesModel.getInstance(newDf.clone(currentDf.filter));
204
- if (clustersColumn) await model.addLogoSummaryTable();
210
+ model = PeptidesModel.getInstance(newDf.clone(bitset));
211
+ if (clustersColumn)
212
+ await model.addLogoSummaryTable();
205
213
  await model.addMonomerPosition();
206
214
  await model.addMostPotentResidues();
207
215
  } else
@@ -0,0 +1,31 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import * as DG from 'datagrok-api/dg';
3
+
4
+ import {PeptidesModel} from '../model';
5
+
6
+ import wu from 'wu';
7
+
8
+ export function getSelectionWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
9
+ const compBitset = model.getCompoundBitset();
10
+ if (compBitset.trueCount === 0)
11
+ return new DG.Widget(ui.divText('No compounds selected'));
12
+ const newTable = DG.DataFrame.create(table.rowCount);
13
+ newTable.filter.copyFrom(compBitset);
14
+ const sourceGrid = model.analysisView.grid;
15
+ const numericalCols = wu(table.columns.numerical);
16
+ for (let gridColIdx = 1; gridColIdx < sourceGrid.columns.length; gridColIdx++) {
17
+ const gridCol = sourceGrid.columns.byIndex(gridColIdx)!;
18
+ if (!gridCol.visible)
19
+ continue;
20
+ const sourceCol = gridCol.column!;
21
+ const sourceColRawData = sourceCol.getRawData();
22
+ const sourceColCategories = sourceCol.categories;
23
+ const getValue = numericalCols.some((col) => col.name === sourceCol.name) ? (i: number): number => sourceColRawData[i] :
24
+ (i: number): string => sourceColCategories[sourceColRawData[i]];
25
+ const col = newTable.columns.addNewVirtual(gridCol.name, (i) => getValue(i), sourceCol.type as DG.TYPE);
26
+ for (const [tag, value] of sourceCol.tags)
27
+ col.setTag(tag, value);
28
+ }
29
+ const newGrid = newTable.plot.grid();
30
+ return new DG.Widget(ui.box(newGrid.root, {style: {width: '100%'}}));
31
+ }
@@ -62,12 +62,17 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
62
62
  // General pane options
63
63
  const activityCol = ui.columnInput(GENERAL_INPUTS.ACTIVITY, model.df,
64
64
  model.df.getCol(model.settings.activityColumnName!), () => result.activityColumnName = activityCol.value!.name,
65
- {filter: (col: DG.Column) => (col.type === DG.TYPE.FLOAT || col.type === DG.TYPE.INT) && col.name !== C.COLUMNS_NAMES.ACTIVITY_SCALED});
65
+ {filter: (col: DG.Column) => (col.type === DG.TYPE.FLOAT || col.type === DG.TYPE.INT) &&
66
+ col.name !== C.COLUMNS_NAMES.ACTIVITY_SCALED && col.stats.missingValueCount === 0});
67
+ activityCol.setTooltip('Numeric activity column');
66
68
  const activityScaling =
67
69
  ui.choiceInput(GENERAL_INPUTS.ACTIVITY_SCALING, currentScaling, Object.values(C.SCALING_METHODS),
68
70
  () => result.scaling = activityScaling.value as C.SCALING_METHODS) as DG.InputBase<C.SCALING_METHODS>;
71
+ activityScaling.setTooltip('Activity column transformation method');
69
72
  const bidirectionalAnalysis = ui.boolInput(GENERAL_INPUTS.BIDIRECTIONAL_ANALYSIS, currentBidirectional,
70
73
  () => result.isBidirectional = bidirectionalAnalysis.value) as DG.InputBase<boolean>;
74
+ bidirectionalAnalysis.setTooltip('Distinguish between positive and negative mean activity difference in ' +
75
+ 'Monomer-Position and Most Potent Residues viewers');
71
76
 
72
77
  accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling, bidirectionalAnalysis]), true);
73
78
  inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling, bidirectionalAnalysis];
@@ -88,6 +93,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
88
93
  const isDendrogramEnabled = wu(model.analysisView.viewers).some((v) => v.type === VIEWER_TYPE.DENDROGRAM);
89
94
  const dendrogram = ui.boolInput(VIEWER_TYPE.DENDROGRAM, isDendrogramEnabled ?? false,
90
95
  () => result.showDendrogram = dendrogram.value) as DG.InputBase<boolean>;
96
+ dendrogram.setTooltip('Show dendrogram viewer');
91
97
  dendrogram.enabled = getTreeHelperInstance() !== null;
92
98
 
93
99
  accordion.addPane(SETTINGS_PANES.VIEWERS, () => ui.inputs([dendrogram]), true);
@@ -100,6 +106,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
100
106
  result.maxMutations = val;
101
107
  maxMutations.addPostfix(val.toString());
102
108
  }) as DG.InputBase<number>;
109
+ maxMutations.setTooltip('Maximum number of mutations between reference and mutated sequences');
103
110
  maxMutations.addPostfix((settings.maxMutations ?? 1).toString());
104
111
  const minActivityDelta = ui.sliderInput(MUTATION_CLIFFS_INPUTS.MIN_ACTIVITY_DELTA, currentMinActivityDelta, 0,
105
112
  100, () => {
@@ -108,6 +115,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
108
115
  $(minActivityDelta.root).find('label.ui-input-description').remove();
109
116
  minActivityDelta.addPostfix(val);
110
117
  }) as DG.InputBase<number>;
118
+ minActivityDelta.setTooltip('Minimum activity difference between reference and mutated sequences');
111
119
  minActivityDelta.addPostfix((settings.minActivityDelta ?? 0).toString());
112
120
  accordion.addPane(SETTINGS_PANES.MUTATION_CLIFFS, () => ui.inputs([maxMutations, minActivityDelta]), true);
113
121
  inputs[SETTINGS_PANES.MUTATION_CLIFFS] = [maxMutations, minActivityDelta];
@@ -131,6 +139,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
131
139
  delete result.columns;
132
140
  }
133
141
  }) as DG.InputBase<boolean>;
142
+ isIncludedInput.setTooltip('Include aggregated column value in tooltips, Logo Summary Table and Distribution panel');
134
143
 
135
144
  const aggregationInput = ui.choiceInput(COLUMNS_INPUTS.AGGREGATION, (currentColumns)[colName] ?? DG.AGG.AVG,
136
145
  Object.values(DG.STATS), () => {
@@ -143,6 +152,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
143
152
  delete result.columns;
144
153
  }
145
154
  }) as DG.InputBase<DG.AggregationType>;
155
+ aggregationInput.setTooltip('Aggregation method');
146
156
  $(aggregationInput.root).find('label').css('width', 'auto');
147
157
  const inputsRow = ui.inputsRow(col.name, [isIncludedInput, aggregationInput]);
148
158
  includedColumnsInputs.push(...[isIncludedInput, aggregationInput]);
@@ -1,39 +0,0 @@
1
-
2
- import {sequenceChemSimilarity} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
3
- import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
4
- import * as DG from 'datagrok-api/dg';
5
-
6
- export function calculateIdentity(template: ISeqSplitted, splitSeqDf: DG.DataFrame): DG.Column<number> {
7
- const numPositions = splitSeqDf.columns.length;
8
- const positionCols: Uint32Array[] = new Array(numPositions);
9
- const positionEmptyCategories: number[] = new Array(numPositions);
10
- const categoryIndexesTemplate: number[] = new Array(numPositions);
11
-
12
- for (let posIdx = 0; posIdx < numPositions; ++posIdx) {
13
- const posCol = splitSeqDf.columns.byIndex(posIdx);
14
- positionCols[posIdx] = posCol.getRawData() as Uint32Array;
15
- positionEmptyCategories[posIdx] = posCol.categories.indexOf('');
16
- categoryIndexesTemplate[posIdx] = posCol.categories.indexOf(template[posIdx] ?? '');
17
- }
18
-
19
- const identityScoresCol = DG.Column.float('Identity', splitSeqDf.rowCount);
20
- const identityScoresData = identityScoresCol.getRawData();
21
- for (let rowIndex = 0; rowIndex < splitSeqDf.rowCount; ++rowIndex) {
22
- identityScoresData[rowIndex] = 0;
23
- for (let posIdx = 0; posIdx < template.length; ++posIdx) {
24
- const categoryIndex = positionCols[posIdx][rowIndex];
25
- if (categoryIndex === categoryIndexesTemplate[posIdx])
26
- ++identityScoresData[rowIndex];
27
- }
28
- identityScoresData[rowIndex] /= template.length;
29
- }
30
-
31
- return identityScoresCol;
32
- }
33
-
34
-
35
- export async function calculateSimilarity(template: ISeqSplitted, splitSeqDf: DG.DataFrame): Promise<DG.Column<number>> {
36
- const columns = splitSeqDf.columns.toList() as DG.Column<string>[];
37
- const scoresCol = await sequenceChemSimilarity(columns, template);
38
- return scoresCol;
39
- }