@datagrok/peptides 1.25.1 → 1.26.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.
- package/CHANGELOG.md +10 -0
- package/dist/216.js +1 -1
- package/dist/216.js.map +1 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +1 -1
- package/src/model.ts +78 -70
- package/src/package-api.ts +7 -0
- package/src/package.g.ts +9 -0
- package/src/package.ts +12 -0
- package/src/tests/viewers.ts +22 -18
- package/src/tests/widgets.ts +1 -1
- package/src/utils/algorithms.ts +7 -3
- package/src/utils/cell-renderer.ts +4 -1
- package/src/utils/misc.ts +16 -20
- package/src/utils/statistics.ts +34 -10
- package/src/utils/tooltips.ts +2 -0
- package/src/viewers/logo-summary.ts +27 -11
- package/src/viewers/mutation-cliffs-viewer.ts +378 -0
- package/src/viewers/position-statistics-viewer.ts +2 -2
- package/src/widgets/distribution.ts +16 -11
- package/src/widgets/peptides.ts +1 -1
- package/src/workers/mutation-cliffs-worker.ts +6 -2
- package/test-console-output-1.log +132 -446
- package/test-record-1.mp4 +0 -0
package/package.json
CHANGED
package/src/model.ts
CHANGED
|
@@ -511,53 +511,54 @@ export class PeptidesModel {
|
|
|
511
511
|
descritionsHost,
|
|
512
512
|
], 'css-gap-small'));
|
|
513
513
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
514
|
+
// ACTIONS PANE temporarily disabled
|
|
515
|
+
// if (filterAndSelectionBs.anyTrue) {
|
|
516
|
+
// acc.addPane('Actions', () => {
|
|
517
|
+
// try {
|
|
518
|
+
// const newView = ui.label('New view');
|
|
519
|
+
// $(newView).addClass('d4-link-action');
|
|
520
|
+
// newView.onclick = (): string => trueModel.createNewView();
|
|
521
|
+
// newView.onmouseover = (ev): void =>
|
|
522
|
+
// ui.tooltip.show('Creates a new view from current selection', ev.clientX + 5, ev.clientY + 5);
|
|
523
|
+
// if (trueLSTViewer === null)
|
|
524
|
+
// return ui.divV([newView]);
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
// const newCluster = ui.label('New cluster');
|
|
528
|
+
// $(newCluster).addClass('d4-link-action');
|
|
529
|
+
// newCluster.onclick = (): void => {
|
|
530
|
+
// if (trueLSTViewer === null)
|
|
531
|
+
// throw new Error('Logo summary table viewer is not found');
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
// trueLSTViewer.clusterFromSelection();
|
|
535
|
+
// };
|
|
536
|
+
// newCluster.onmouseover = (ev): void =>
|
|
537
|
+
// ui.tooltip.show('Creates a new cluster from selection', ev.clientX + 5, ev.clientY + 5);
|
|
538
|
+
|
|
539
|
+
// const removeCluster = ui.label('Remove cluster');
|
|
540
|
+
// $(removeCluster).addClass('d4-link-action');
|
|
541
|
+
// removeCluster.onclick = (): void => {
|
|
542
|
+
// const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
543
|
+
// if (lstViewer === null)
|
|
544
|
+
// throw new Error('Logo summary table viewer is not found');
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
// lstViewer.removeCluster();
|
|
548
|
+
// };
|
|
549
|
+
// removeCluster.onmouseover = (ev): void =>
|
|
550
|
+
// ui.tooltip.show('Removes currently selected custom cluster', ev.clientX + 5, ev.clientY + 5);
|
|
551
|
+
// removeCluster.style.visibility = trueLSTViewer.clusterSelection[CLUSTER_TYPE.CUSTOM].length === 0 ? 'hidden' :
|
|
552
|
+
// 'visible';
|
|
553
|
+
|
|
554
|
+
// return ui.divV([newView, newCluster, removeCluster]);
|
|
555
|
+
// } catch (e) {
|
|
556
|
+
// const errorDiv = ui.divText('Error in Actions');
|
|
557
|
+
// ui.tooltip.bind(errorDiv, String(e));
|
|
558
|
+
// return errorDiv;
|
|
559
|
+
// }
|
|
560
|
+
// }, true);
|
|
561
|
+
// }
|
|
561
562
|
|
|
562
563
|
// Get the source of the bitset change and find viewers that share the same parameters as source
|
|
563
564
|
let requestSource: SARViewer | LogoSummaryTable | PeptidesSettings | null = trueModel.settings;
|
|
@@ -587,27 +588,27 @@ export class PeptidesModel {
|
|
|
587
588
|
v !== null && areParametersEqual(requestSource!, v) && (v !== trueModel.settings || trueModel.isInitialized);
|
|
588
589
|
const panelDataSources = viewers.filter(notEmpty);
|
|
589
590
|
panelDataSources.push(requestSource);
|
|
590
|
-
const combinedBitset: DG.BitSet | null = DG.BitSet.create(trueModel.df.rowCount);
|
|
591
|
-
for (const panelDataSource of panelDataSources) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}
|
|
591
|
+
//const combinedBitset: DG.BitSet | null = DG.BitSet.create(trueModel.df.rowCount);
|
|
592
|
+
// for (const panelDataSource of panelDataSources) {
|
|
593
|
+
// const bitset =
|
|
594
|
+
// (panelDataSource === this.settings) ? getSelectionBitset(this.webLogoSelection, this.monomerPositionStats!) :
|
|
595
|
+
// (panelDataSource instanceof LogoSummaryTable) ?
|
|
596
|
+
// getSelectionBitset(panelDataSource.clusterSelection, panelDataSource.clusterStats) :
|
|
597
|
+
// (panelDataSource instanceof SARViewer) ?
|
|
598
|
+
// getSelectionBitset(panelDataSource.mutationCliffsSelection,
|
|
599
|
+
// mutationCliffsToMaskInfo(panelDataSource.mutationCliffs ?? new Map(), trueModel.df.rowCount)) :
|
|
600
|
+
// null;
|
|
601
|
+
// if (bitset !== null)
|
|
602
|
+
// combinedBitset.or(bitset);
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
// if (panelDataSource instanceof MonomerPosition) {
|
|
606
|
+
// const invariantMapSelectionBitset = getSelectionBitset(panelDataSource.invariantMapSelection,
|
|
607
|
+
// panelDataSource.monomerPositionStats);
|
|
608
|
+
// if (invariantMapSelectionBitset !== null)
|
|
609
|
+
// combinedBitset.or(invariantMapSelectionBitset);
|
|
610
|
+
// }
|
|
611
|
+
// }
|
|
611
612
|
|
|
612
613
|
const sarViewer = requestSource as any as SARViewer | LogoSummaryTable;
|
|
613
614
|
if (requestSource !== trueModel.settings && !(sarViewer instanceof LogoSummaryTable) &&
|
|
@@ -630,7 +631,6 @@ export class PeptidesModel {
|
|
|
630
631
|
acc.addPane('Distribution', () => {
|
|
631
632
|
try {
|
|
632
633
|
return getDistributionWidget(trueModel.df, {
|
|
633
|
-
peptideSelection: combinedBitset,
|
|
634
634
|
columns: isModelSource ? trueModel.settings!.columns ?? {} :
|
|
635
635
|
(requestSource as PeptideViewer).getAggregationColumns(),
|
|
636
636
|
activityCol: isModelSource ? trueModel.getScaledActivityColumn()! :
|
|
@@ -869,7 +869,8 @@ export class PeptidesModel {
|
|
|
869
869
|
}
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
-
|
|
872
|
+
const res = DG.BitSet.fromBytes(combinedSelection.buffer.buffer as ArrayBuffer, combinedSelection.length);
|
|
873
|
+
return this.df.filter.anyFalse ? res.and(this.df.filter) : res;
|
|
873
874
|
}
|
|
874
875
|
|
|
875
876
|
|
|
@@ -1402,6 +1403,13 @@ export class PeptidesModel {
|
|
|
1402
1403
|
await DG.delay(500);
|
|
1403
1404
|
this._mclViewer = this.findViewer(VIEWER_TYPE.MCL) as MCLViewer;
|
|
1404
1405
|
await this._mclViewer.initPromise;
|
|
1406
|
+
// after waiting for init promise, it can be resolved by just removing the viewer, so check again
|
|
1407
|
+
this._mclViewer = this.findViewer(VIEWER_TYPE.MCL) as MCLViewer | null;
|
|
1408
|
+
if (!this._mclViewer || !this._mclViewer.isDetached) {
|
|
1409
|
+
mclAdditionSub?.unsubscribe();
|
|
1410
|
+
columnAddedSub?.unsubscribe();
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1405
1413
|
const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
1406
1414
|
if (lstViewer) { // beware, this is accessing private things
|
|
1407
1415
|
lstViewer._clusterStats = null;
|
package/src/package-api.ts
CHANGED
|
@@ -48,6 +48,13 @@ export namespace funcs {
|
|
|
48
48
|
return await grok.functions.call('Peptides:MostPotentResidues', {});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
Mutation Cliffs Line Chart
|
|
53
|
+
*/
|
|
54
|
+
export async function mutationCliffs(): Promise<any> {
|
|
55
|
+
return await grok.functions.call('Peptides:MutationCliffs', {});
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
export async function logoSummaryTable(): Promise<any> {
|
|
52
59
|
return await grok.functions.call('Peptides:LogoSummaryTable', {});
|
|
53
60
|
}
|
package/src/package.g.ts
CHANGED
|
@@ -48,6 +48,15 @@ export function mostPotentResidues() : any {
|
|
|
48
48
|
return PackageFunctions.mostPotentResidues();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
//name: Sequence Mutation Cliffs
|
|
52
|
+
//description: Mutation Cliffs Line Chart
|
|
53
|
+
//tags: viewer
|
|
54
|
+
//output: viewer result
|
|
55
|
+
//meta.icon: files/icons/sequence-statistics-viewer.svg
|
|
56
|
+
export function mutationCliffs() : any {
|
|
57
|
+
return PackageFunctions.mutationCliffs();
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
//name: Logo Summary Table
|
|
52
61
|
//tags: viewer
|
|
53
62
|
//output: viewer result
|
package/src/package.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {ClusterMaxActivityViewer} from './viewers/cluster-max-activity-viewer';
|
|
|
18
18
|
import {LSTPieChartRenderer} from './utils/cell-renderer';
|
|
19
19
|
import {PeptideUtils} from './peptideUtils';
|
|
20
20
|
import {SequencePositionStatsViewer} from './viewers/position-statistics-viewer';
|
|
21
|
+
import {MutationCliffsViewer} from './viewers/mutation-cliffs-viewer';
|
|
21
22
|
|
|
22
23
|
let monomerWorks: MonomerWorks | null = null;
|
|
23
24
|
let treeHelper: ITreeHelper;
|
|
@@ -193,6 +194,17 @@ export class PackageFunctions {
|
|
|
193
194
|
return new MostPotentResidues();
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
@grok.decorators.func({
|
|
198
|
+
meta: {icon: 'files/icons/sequence-statistics-viewer.svg'},
|
|
199
|
+
tags: ['viewer'],
|
|
200
|
+
name: 'Sequence Mutation Cliffs',
|
|
201
|
+
description: 'Mutation Cliffs Line Chart',
|
|
202
|
+
outputs: [{type: 'viewer', name: 'result'}],
|
|
203
|
+
})
|
|
204
|
+
static mutationCliffs(): DG.Viewer {
|
|
205
|
+
return new MutationCliffsViewer();
|
|
206
|
+
}
|
|
207
|
+
|
|
196
208
|
|
|
197
209
|
@grok.decorators.func({
|
|
198
210
|
meta: {icon: 'files/icons/logo-summary-viewer.svg'},
|
package/src/tests/viewers.ts
CHANGED
|
@@ -14,24 +14,28 @@ import {TEST_COLUMN_NAMES} from './utils';
|
|
|
14
14
|
import {showTooltip} from '../utils/tooltips';
|
|
15
15
|
import {PeptideUtils} from '../peptideUtils';
|
|
16
16
|
|
|
17
|
-
category('Viewers: Basic', () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
17
|
+
// category('Viewers: Basic', () => {
|
|
18
|
+
// let df: DG.DataFrame;
|
|
19
|
+
|
|
20
|
+
// before(async () => {
|
|
21
|
+
// await PeptideUtils.loadComponents();
|
|
22
|
+
|
|
23
|
+
// df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
|
|
24
|
+
// const clusterCol = df.col('cluster')!.convertTo(DG.COLUMN_TYPE.STRING);
|
|
25
|
+
// clusterCol!.name = 'cluster';
|
|
26
|
+
// df.columns.remove('cluster');
|
|
27
|
+
// df.columns.add(clusterCol!);
|
|
28
|
+
// await delay(500);
|
|
29
|
+
// });
|
|
30
|
+
|
|
31
|
+
// const viewers = DG.Func.find({package: 'Peptides', tags: ['viewer']}).map((f) => f.friendlyName);
|
|
32
|
+
// for (const v of viewers) {
|
|
33
|
+
// test(v, async () => {
|
|
34
|
+
// await testViewer(v, df.clone(), {detectSemanticTypes: true, arbitraryDfTest: false, readOnly: true});
|
|
35
|
+
// await delay(1000);
|
|
36
|
+
// });
|
|
37
|
+
// }
|
|
38
|
+
// });
|
|
35
39
|
|
|
36
40
|
category('Viewers: Monomer-Position', () => {
|
|
37
41
|
let df: DG.DataFrame;
|
package/src/tests/widgets.ts
CHANGED
|
@@ -98,7 +98,7 @@ category('Widgets: Distribution panel', () => {
|
|
|
98
98
|
await delay(1000);
|
|
99
99
|
const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
|
|
100
100
|
getDistributionWidget(model.df, {
|
|
101
|
-
|
|
101
|
+
columns: model.settings!.columns!,
|
|
102
102
|
activityCol: scaledActivityCol, clusterSelection: lstViewer!.clusterSelection, clusterColName: clusterCol.name,
|
|
103
103
|
monomerPositionSelection: model.webLogoSelection,
|
|
104
104
|
});
|
package/src/utils/algorithms.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {ParallelMutationCliffs} from './parallel-mutation-cliffs';
|
|
|
6
6
|
import {CLUSTER_TYPE} from '../viewers/logo-summary';
|
|
7
7
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
8
8
|
import {
|
|
9
|
+
bitSetToBitArray,
|
|
9
10
|
ClusterStats,
|
|
10
11
|
ClusterTypeStats,
|
|
11
12
|
getStats,
|
|
@@ -18,7 +19,8 @@ import {
|
|
|
18
19
|
export type MutationCliffsOptions = {
|
|
19
20
|
maxMutations?: number,
|
|
20
21
|
minActivityDelta?: number,
|
|
21
|
-
filter?: Uint32Array
|
|
22
|
+
filter?: Uint32Array,
|
|
23
|
+
singlePosition?: {position: number}
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -120,6 +122,7 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
|
|
|
120
122
|
// if (options.aggValue)
|
|
121
123
|
// options.aggValue.col = options.aggValue.col.clone(filter);
|
|
122
124
|
// }
|
|
125
|
+
const filterBitArray = options.isFiltered ? bitSetToBitArray(filter) : undefined;
|
|
123
126
|
|
|
124
127
|
for (const posCol of positionColumns) {
|
|
125
128
|
if (!options.columns.includes(posCol.name))
|
|
@@ -144,7 +147,7 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
|
|
|
144
147
|
const bitArray = BitArray.fromValues(boolArray);
|
|
145
148
|
if (bitArray.allFalse)
|
|
146
149
|
continue;
|
|
147
|
-
const stats = getStats(activityColData, bitArray, options.aggValue);
|
|
150
|
+
const stats = getStats(activityColData, bitArray, filterBitArray, options.aggValue);
|
|
148
151
|
currentPositionObject[monomer] = stats;
|
|
149
152
|
getSummaryStats(currentPositionObject.general, stats);
|
|
150
153
|
}
|
|
@@ -245,6 +248,7 @@ export function calculateClusterStatistics(df: DG.DataFrame, clustersColumnName:
|
|
|
245
248
|
const origClustStats: ClusterStats = {};
|
|
246
249
|
const customClustStats: ClusterStats = {};
|
|
247
250
|
|
|
251
|
+
const filterMask = df.filter.anyFalse ? bitSetToBitArray(df.filter) : undefined;
|
|
248
252
|
for (const clustType of Object.values(CLUSTER_TYPE)) {
|
|
249
253
|
const masks = clustType === CLUSTER_TYPE.ORIGINAL ? origClustMasks : customClustMasks;
|
|
250
254
|
const clustNames = clustType === CLUSTER_TYPE.ORIGINAL ? origClustColCat : customClustColNamesList;
|
|
@@ -253,7 +257,7 @@ export function calculateClusterStatistics(df: DG.DataFrame, clustersColumnName:
|
|
|
253
257
|
const mask = masks[maskIdx];
|
|
254
258
|
resultStats[clustNames[maskIdx]] = mask.allTrue || mask.allFalse ?
|
|
255
259
|
{count: mask.length, meanDifference: 0, ratio: 1.0, pValue: null, mask: mask, mean: activityCol.stats.avg} :
|
|
256
|
-
getStats(activityColData, mask);
|
|
260
|
+
getStats(activityColData, mask, filterMask);
|
|
257
261
|
}
|
|
258
262
|
}
|
|
259
263
|
|
|
@@ -410,7 +410,10 @@ export function setWebLogoRenderer(grid: DG.Grid, monomerPositionStats: MonomerP
|
|
|
410
410
|
return;
|
|
411
411
|
}
|
|
412
412
|
tooltipOptions.monomerPosition = monomerPosition;
|
|
413
|
-
|
|
413
|
+
const isDfFiltered = df.filter.anyFalse;
|
|
414
|
+
const actionDf = isDfFiltered ? df.clone(df.filter) : df;
|
|
415
|
+
const actionActivityCol = actionDf.getCol(activityCol.name) as DG.Column<number>;
|
|
416
|
+
requestWebLogoAction(ev, monomerPosition, actionDf, actionActivityCol, options, tooltipOptions);
|
|
414
417
|
if (!options.isSelectionTable && options.highlightCallback != null)
|
|
415
418
|
options.highlightCallback(monomerPosition, df, monomerPositionStats);
|
|
416
419
|
} else if (options.unhighlightCallback != null)
|
package/src/utils/misc.ts
CHANGED
|
@@ -131,39 +131,35 @@ export function getDistributionPanel(hist: DG.Viewer<DG.IHistogramSettings>, sta
|
|
|
131
131
|
* Creates a table to plot activity distribution.
|
|
132
132
|
* @param activityCol - Activity column.
|
|
133
133
|
* @param selection - Selection bitset.
|
|
134
|
-
* @param [peptideSelection] - Peptide selection bitset.
|
|
135
134
|
* @return - Dataframe with activity distribution.
|
|
136
135
|
*/
|
|
137
|
-
export function getDistributionTable(activityCol: DG.Column<number>, selection: DG.BitSet
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
export function getDistributionTable(activityCol: DG.Column<number>, selection: DG.BitSet): DG.DataFrame {
|
|
137
|
+
if (!activityCol.dataFrame)
|
|
138
|
+
DG.DataFrame.fromColumns([activityCol]); // to make sure that activityCol has a parent dataframe
|
|
139
|
+
const filter = activityCol.dataFrame!.filter;
|
|
140
|
+
const selectionAndFilter = selection.clone().and(filter);
|
|
140
141
|
const rowCount = activityCol.length;
|
|
141
142
|
const activityColData = activityCol.getRawData();
|
|
142
|
-
const
|
|
143
|
-
|
|
143
|
+
const filteredRowCount = filter.trueCount;
|
|
144
|
+
const activityData = new Float32Array(filteredRowCount + selectionAndFilter.trueCount);
|
|
144
145
|
const categories: string[] = new Array(activityData.length);
|
|
145
146
|
|
|
146
|
-
for (let i = 0, j = 0, k = 0; i < rowCount; ++i) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
for (let i = 0, /*J is counting for filtered data*/j = 0, /*selected data*/k = 0; i < rowCount; ++i) {
|
|
148
|
+
if (!filter.get(i))
|
|
149
|
+
continue;
|
|
150
|
+
const isSelected = selectionAndFilter.get(i);
|
|
151
|
+
activityData[j] = activityColData[i];
|
|
152
|
+
categories[j] = isSelected ? SPLIT_CATEGORY.SELECTION : SPLIT_CATEGORY.ALL;
|
|
153
|
+
j++;
|
|
150
154
|
if (isSelected) {
|
|
151
|
-
activityData[
|
|
152
|
-
categories[
|
|
153
|
-
++j;
|
|
154
|
-
}
|
|
155
|
-
if (selectionMismatch && peptideSelection?.get(i)) {
|
|
156
|
-
activityData[rowCount + selection.trueCount + k] = activityColData[i];
|
|
157
|
-
categories[rowCount + selection.trueCount + k] = SPLIT_CATEGORY.PEPTIDES_SELECTION;
|
|
155
|
+
activityData[filteredRowCount + k] = activityColData[i];
|
|
156
|
+
categories[filteredRowCount + k] = SPLIT_CATEGORY.ALL;
|
|
158
157
|
++k;
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
const splitCol = DG.Column.fromStrings(C.COLUMNS_NAMES.SPLIT_COL, categories);
|
|
163
162
|
const categoryOrder = [SPLIT_CATEGORY.ALL, SPLIT_CATEGORY.SELECTION];
|
|
164
|
-
if (selectionMismatch)
|
|
165
|
-
categoryOrder.push(SPLIT_CATEGORY.PEPTIDES_SELECTION);
|
|
166
|
-
|
|
167
163
|
|
|
168
164
|
splitCol.setCategoryOrder(categoryOrder);
|
|
169
165
|
splitCol.meta.colors.setCategorical();
|
package/src/utils/statistics.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as DG from 'datagrok-api/dg';
|
|
2
3
|
import {tTest} from '@datagrok-libraries/statistics/src/tests';
|
|
3
4
|
import {RawData} from './types';
|
|
@@ -37,24 +38,42 @@ export type SummaryStats = {
|
|
|
37
38
|
export const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
|
|
38
39
|
export type AggregationColumns = { [col: string]: DG.AggregationType };
|
|
39
40
|
|
|
41
|
+
export function bitSetToBitArray(bitSet: DG.BitSet): BitArray {
|
|
42
|
+
return BitArray.fromUint32Array(
|
|
43
|
+
bitSet.length,
|
|
44
|
+
new Uint32Array(bitSet.getBuffer().buffer as ArrayBuffer),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function bitArrayToBitSet(bitArray: BitArray): DG.BitSet {
|
|
49
|
+
return DG.BitSet.fromBytes(
|
|
50
|
+
bitArray.buffer.buffer as ArrayBuffer,
|
|
51
|
+
bitArray.length,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
/**
|
|
41
56
|
* Returns statistics for the given activity data and bit array.
|
|
42
57
|
* @param data - Activity data to calculate statistics for.
|
|
43
58
|
* @param bitArray - Bit array to use for the calculation.
|
|
44
59
|
* @return - Statistics for the given data and bit array.
|
|
45
60
|
*/
|
|
46
|
-
export function getStats(data: RawData | number[], bitArray: BitArray,
|
|
61
|
+
export function getStats(data: RawData | number[], bitArray: BitArray, filter?: BitArray,
|
|
47
62
|
aggData?: {col: DG.Column, type: DG.AGG}): StatsItem {
|
|
48
|
-
if
|
|
63
|
+
// if column length is less than 16, raw data will be still of length 16
|
|
64
|
+
if ((data.length !== bitArray.length && data.length !== 16) || (filter && data.length !== filter.length && data.length !== 16))
|
|
49
65
|
throw new Error('PeptidesError: Data and bit array have different lengths');
|
|
66
|
+
if (filter && filter.allTrue)
|
|
67
|
+
filter = undefined; // no need to filter if all true
|
|
50
68
|
|
|
51
|
-
const
|
|
52
|
-
const
|
|
69
|
+
const filterAndMask = filter ? bitArray.clone().and(filter) : bitArray;
|
|
70
|
+
const selected = new Float32Array(filterAndMask.trueCount());
|
|
71
|
+
const rest = new Float32Array(filter ? (filter.trueCount() - filterAndMask.trueCount()) : filterAndMask.falseCount());
|
|
53
72
|
let aggValue: number | undefined;
|
|
54
73
|
if (aggData) {
|
|
55
74
|
try {
|
|
56
75
|
aggValue = DG.DataFrame.fromColumns([aggData.col])
|
|
57
|
-
.clone(
|
|
76
|
+
.clone(bitArrayToBitSet(filterAndMask)).col(aggData.col.name)
|
|
58
77
|
?.aggregate(aggData.type);
|
|
59
78
|
} catch (e) {
|
|
60
79
|
console.error(e);
|
|
@@ -63,35 +82,40 @@ export function getStats(data: RawData | number[], bitArray: BitArray,
|
|
|
63
82
|
let selectedIndex = 0;
|
|
64
83
|
let restIndex = 0;
|
|
65
84
|
for (let i = 0; i < bitArray.length; ++i) {
|
|
85
|
+
if (filter && !filter.getBit(i))
|
|
86
|
+
continue;
|
|
66
87
|
if (bitArray.getBit(i))
|
|
67
88
|
selected[selectedIndex++] = data[i];
|
|
68
89
|
else
|
|
69
90
|
rest[restIndex++] = data[i];
|
|
70
91
|
}
|
|
92
|
+
const totalAnalyzed = selected.length + rest.length;
|
|
71
93
|
|
|
72
94
|
const selectedMean = selected.reduce((a, b) => a + b, 0) / Math.max(selected.length, 1);
|
|
73
95
|
if (selected.length < 2 || rest.length < 2) {
|
|
74
96
|
const restMean = rest.reduce((a, b) => a + b, 0) / Math.max(rest.length, 1);
|
|
97
|
+
|
|
75
98
|
return {
|
|
76
99
|
count: selected.length,
|
|
77
100
|
pValue: null,
|
|
78
101
|
mean: selectedMean,
|
|
79
102
|
meanDifference: selectedMean - restMean,
|
|
80
|
-
ratio: selected.length /
|
|
81
|
-
mask:
|
|
103
|
+
ratio: selected.length / totalAnalyzed,
|
|
104
|
+
mask: filterAndMask,
|
|
82
105
|
aggValue,
|
|
83
106
|
};
|
|
84
107
|
}
|
|
85
108
|
|
|
86
109
|
const testResult = tTest(selected, rest);
|
|
110
|
+
|
|
87
111
|
const currentMeanDiff = testResult['Mean difference']!;
|
|
88
112
|
return {
|
|
89
113
|
count: selected.length,
|
|
90
114
|
pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'],
|
|
91
115
|
mean: selectedMean,
|
|
92
116
|
meanDifference: currentMeanDiff,
|
|
93
|
-
ratio: selected.length /
|
|
94
|
-
mask:
|
|
117
|
+
ratio: selected.length / totalAnalyzed,
|
|
118
|
+
mask: filterAndMask,
|
|
95
119
|
aggValue,
|
|
96
120
|
};
|
|
97
121
|
}
|
|
@@ -129,7 +153,7 @@ export function getAggregatedColumnValues(df: DG.DataFrame, columns: [string, DG
|
|
|
129
153
|
options.filterDf ??= false;
|
|
130
154
|
options.fractionDigits ??= 3;
|
|
131
155
|
|
|
132
|
-
const filteredDf = options.filterDf && df.filter.anyFalse ? df.clone(df.filter) : df;
|
|
156
|
+
const filteredDf = options.filterDf && df.filter.anyFalse && (!options.mask || options.mask.length === df.filter.trueCount) ? df.clone(df.filter) : df;
|
|
133
157
|
|
|
134
158
|
const colResults: StringDictionary = {};
|
|
135
159
|
for (const [colName, aggFn] of columns) {
|
package/src/utils/tooltips.ts
CHANGED
|
@@ -60,6 +60,7 @@ export function showTooltipAt(df: DG.DataFrame, activityCol: DG.Column<number>,
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer as ArrayBuffer, activityCol.length);
|
|
63
|
+
mask.and(df.filter);
|
|
63
64
|
const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
|
|
64
65
|
const tableMap = getStatsTableMap(stats);
|
|
65
66
|
if (options.fromViewer) {
|
|
@@ -85,6 +86,7 @@ export function showTooltipAt(df: DG.DataFrame, activityCol: DG.Column<number>,
|
|
|
85
86
|
if (!stats)
|
|
86
87
|
return null;
|
|
87
88
|
const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer as ArrayBuffer, activityCol.length);
|
|
89
|
+
mask.and(df.filter);
|
|
88
90
|
const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
|
|
89
91
|
const tableMap = getStatsTableMap(stats, {countName: 'Unique count'});
|
|
90
92
|
if (options.fromViewer) {
|
|
@@ -11,6 +11,7 @@ import * as CR from '../utils/cell-renderer';
|
|
|
11
11
|
import {HorizontalAlignments, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
12
12
|
import {
|
|
13
13
|
AggregationColumns,
|
|
14
|
+
bitSetToBitArray,
|
|
14
15
|
ClusterTypeStats,
|
|
15
16
|
getAggregatedColumnValues,
|
|
16
17
|
getAggregatedValue,
|
|
@@ -315,15 +316,30 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
|
|
|
315
316
|
onTableAttached(): void {
|
|
316
317
|
super.onTableAttached();
|
|
317
318
|
if (isApplicableDataframe(this.dataFrame)) {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
319
|
+
const potentialActivityColName = wu(this.dataFrame.columns.numerical).filter((col) => col.name?.includes('activ')).take(1).toArray()[0]?.name;
|
|
320
|
+
const activityColName = potentialActivityColName ??
|
|
321
|
+
wu(this.dataFrame.columns.numerical).take(1).toArray()?.[0]?.name;
|
|
322
|
+
const seqCol = this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
323
|
+
// const clusterCol =
|
|
324
|
+
if (seqCol) {
|
|
325
|
+
this.getProperty(`${LST_PROPERTIES.SEQUENCE}${COLUMN_NAME}`)
|
|
326
|
+
?.set(this, seqCol.name);
|
|
327
|
+
}
|
|
328
|
+
if (activityColName) {
|
|
329
|
+
this.getProperty(`${LST_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
|
|
330
|
+
?.set(this, activityColName);
|
|
331
|
+
this.getProperty(`${LST_PROPERTIES.WEB_LOGO_AGGREGATION_COLUMN_NAME}`)?.set(this, activityColName);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const potentialClusterCol = wu(this.dataFrame.columns)
|
|
335
|
+
.filter((col) => (col.isCategorical) && col.name?.toLowerCase().includes('cluster') && col.name != seqCol?.name && col.type !== DG.COLUMN_TYPE.BOOL)?.take(1)?.toArray()?.[0];
|
|
336
|
+
const clusterCol = potentialClusterCol ??
|
|
337
|
+
wu(this.dataFrame.columns.categorical)
|
|
338
|
+
.filter((col) => col.name != seqCol?.name && col.categories.length <= 50 && col.type !== DG.COLUMN_TYPE.BOOL)?.take(1)?.toArray()?.[0];
|
|
339
|
+
if (clusterCol) {
|
|
340
|
+
this.getProperty(`${LST_PROPERTIES.CLUSTERS}${COLUMN_NAME}`)
|
|
341
|
+
?.set(this, clusterCol.name);
|
|
342
|
+
}
|
|
327
343
|
} else {
|
|
328
344
|
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
329
345
|
grok.log.error(msg);
|
|
@@ -914,8 +930,8 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
|
|
|
914
930
|
const viewerDfColsLength = viewerDfCols.length;
|
|
915
931
|
const newClusterVals = new Array(viewerDfCols.length);
|
|
916
932
|
const activityScaledCol = this.getScaledActivityColumn();
|
|
917
|
-
const bitArray =
|
|
918
|
-
const stats = getStats(activityScaledCol.getRawData(), bitArray);
|
|
933
|
+
const bitArray = bitSetToBitArray(currentSelection);
|
|
934
|
+
const stats = getStats(activityScaledCol.getRawData(), bitArray, bitSetToBitArray(this.dataFrame.filter));
|
|
919
935
|
|
|
920
936
|
this.bitsets.push(currentSelection.clone());
|
|
921
937
|
|