@vitessce/statistical-plots 3.4.6 → 3.4.7

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.
Files changed (47) hide show
  1. package/dist/deflate-c8c2f459.js +13 -0
  2. package/dist/index-a1925e78.js +206649 -0
  3. package/dist/index.js +13 -91680
  4. package/dist/jpeg-ffd14ffe.js +840 -0
  5. package/dist/lerc-9d1dd17e.js +2014 -0
  6. package/dist/lzw-3705b408.js +128 -0
  7. package/dist/packbits-6f657116.js +30 -0
  8. package/dist/pako.esm-68f84e2a.js +4022 -0
  9. package/dist/raw-0a76dec9.js +12 -0
  10. package/dist/webimage-fbdf3bdf.js +32 -0
  11. package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -1
  12. package/dist-tsc/CellSetExpressionPlot.js +207 -106
  13. package/dist-tsc/CellSetExpressionPlotOptions.d.ts.map +1 -1
  14. package/dist-tsc/CellSetExpressionPlotOptions.js +14 -4
  15. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
  16. package/dist-tsc/CellSetExpressionPlotSubscriber.js +15 -31
  17. package/dist-tsc/CellSetSizesPlot.d.ts.map +1 -1
  18. package/dist-tsc/CellSetSizesPlotSubscriber.d.ts.map +1 -1
  19. package/dist-tsc/DotPlot.d.ts +28 -0
  20. package/dist-tsc/DotPlot.d.ts.map +1 -0
  21. package/dist-tsc/DotPlot.js +144 -0
  22. package/dist-tsc/DotPlotSubscriber.d.ts +14 -0
  23. package/dist-tsc/DotPlotSubscriber.d.ts.map +1 -0
  24. package/dist-tsc/DotPlotSubscriber.js +54 -0
  25. package/dist-tsc/ExpressionHistogram.d.ts.map +1 -1
  26. package/dist-tsc/ExpressionHistogramSubscriber.d.ts.map +1 -1
  27. package/dist-tsc/dot-plot-hook.d.ts +23 -0
  28. package/dist-tsc/dot-plot-hook.d.ts.map +1 -0
  29. package/dist-tsc/dot-plot-hook.js +69 -0
  30. package/dist-tsc/expr-hooks.d.ts +51 -0
  31. package/dist-tsc/expr-hooks.d.ts.map +1 -0
  32. package/dist-tsc/expr-hooks.js +135 -0
  33. package/dist-tsc/expr-hooks.test.d.ts +2 -0
  34. package/dist-tsc/expr-hooks.test.d.ts.map +1 -0
  35. package/dist-tsc/expr-hooks.test.js +97 -0
  36. package/dist-tsc/index.d.ts +2 -0
  37. package/dist-tsc/index.js +2 -0
  38. package/package.json +10 -7
  39. package/src/CellSetExpressionPlot.js +223 -124
  40. package/src/CellSetExpressionPlotOptions.js +57 -1
  41. package/src/CellSetExpressionPlotSubscriber.js +45 -38
  42. package/src/DotPlot.js +175 -0
  43. package/src/DotPlotSubscriber.js +173 -0
  44. package/src/dot-plot-hook.js +107 -0
  45. package/src/expr-hooks.js +170 -0
  46. package/src/expr-hooks.test.js +116 -0
  47. package/src/index.js +2 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Gene expression dot plot,
3
+ * implemented with the VegaPlot component.
4
+ * @param {object} props
5
+ * @param {object[]} props.data The expression data, an array
6
+ * of objects with properties `value`, `gene`, and `set`.
7
+ * @param {string} props.theme The name of the current Vitessce theme.
8
+ * @param {number} props.width The container width.
9
+ * @param {number} props.height The container height.
10
+ * @param {number} props.marginRight The size of the margin
11
+ * on the right side of the plot, to account for the vega menu button.
12
+ * By default, 90.
13
+ * @param {number} props.marginBottom The size of the margin
14
+ * on the bottom of the plot, to account for long x-axis labels.
15
+ * Default is allowing the component to automatically determine the margin.
16
+ * @param {string|null} props.featureValueTransformName A name
17
+ * for the feature value transformation function.
18
+ */
19
+ export default function DotPlot(props: {
20
+ data: object[];
21
+ theme: string;
22
+ width: number;
23
+ height: number;
24
+ marginRight: number;
25
+ marginBottom: number;
26
+ featureValueTransformName: string | null;
27
+ }): JSX.Element;
28
+ //# sourceMappingURL=DotPlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DotPlot.d.ts","sourceRoot":"","sources":["../src/DotPlot.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,uCAdG;IAAwB,IAAI,EAApB,MAAM,EAAE;IAEM,KAAK,EAAnB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,WAAW,EAAzB,MAAM;IAGQ,YAAY,EAA1B,MAAM;IAGa,yBAAyB,EAA5C,MAAM,GAAC,IAAI;CAErB,eAwJA"}
@@ -0,0 +1,144 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { clamp } from 'lodash-es';
4
+ import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
5
+ import { capitalize, pluralize as plur } from '@vitessce/utils';
6
+ /**
7
+ * Gene expression dot plot,
8
+ * implemented with the VegaPlot component.
9
+ * @param {object} props
10
+ * @param {object[]} props.data The expression data, an array
11
+ * of objects with properties `value`, `gene`, and `set`.
12
+ * @param {string} props.theme The name of the current Vitessce theme.
13
+ * @param {number} props.width The container width.
14
+ * @param {number} props.height The container height.
15
+ * @param {number} props.marginRight The size of the margin
16
+ * on the right side of the plot, to account for the vega menu button.
17
+ * By default, 90.
18
+ * @param {number} props.marginBottom The size of the margin
19
+ * on the bottom of the plot, to account for long x-axis labels.
20
+ * Default is allowing the component to automatically determine the margin.
21
+ * @param {string|null} props.featureValueTransformName A name
22
+ * for the feature value transformation function.
23
+ */
24
+ export default function DotPlot(props) {
25
+ const { isStratified, transpose, data: rawData, theme, width, height, marginRight, marginBottom, obsType, keyLength = 36, featureType, featureValueType, featureValueTransformName, featureValueColormap, cellSetSelection, } = props;
26
+ // Add a property `keyGroup` and `keyFeature` which concatenates the key and the name,
27
+ // which is both unique and can easily be converted
28
+ // back to the name by taking a substring.
29
+ const data = rawData.map(d => ({
30
+ ...d,
31
+ keyGroup: d.groupKey + d.group,
32
+ keyFeature: d.featureKey + d.feature,
33
+ keyGroupSecondary: d.secondaryGroupKey + d.secondaryGroup,
34
+ }));
35
+ // Get the max characters in an axis label for autsizing the bottom margin.
36
+ const maxCharactersForGroup = data.reduce((acc, val) => {
37
+ // eslint-disable-next-line no-param-reassign
38
+ acc = acc === undefined || val.group?.length > acc ? val.group?.length : acc;
39
+ return acc;
40
+ }, 0);
41
+ const maxCharactersForFeature = data.reduce((acc, val) => {
42
+ // eslint-disable-next-line no-param-reassign
43
+ acc = acc === undefined || val.feature.length > acc ? val.feature.length : acc;
44
+ return acc;
45
+ }, 0);
46
+ const maxCharactersForSampleSet = isStratified ? data.reduce((acc, val) => {
47
+ // eslint-disable-next-line no-param-reassign
48
+ acc = acc === undefined || val.secondaryGroup.length > acc ? val.secondaryGroup.length : acc;
49
+ return acc;
50
+ }, 0) : 0;
51
+ // Use a square-root term because the angle of the labels is 45 degrees (see below)
52
+ // so the perpendicular distance to the bottom of the labels is proportional to the
53
+ // square root of the length of the labels along the imaginary hypotenuse.
54
+ // 30 is an estimate of the pixel size of a given character and seems to work well.
55
+ const autoMarginForFeature = marginBottom
56
+ || 30 + Math.sqrt(maxCharactersForFeature / 2) * 30;
57
+ const autoMarginForGroup = marginRight
58
+ || 30 + Math.sqrt(maxCharactersForGroup / 2) * 30;
59
+ const autoMarginForSampleSet = marginRight
60
+ || 30 + Math.sqrt(maxCharactersForSampleSet / 2) * 30;
61
+ const plotWidth = transpose
62
+ ? clamp(width - autoMarginForFeature - 180, 10, Infinity) / (cellSetSelection?.length || 1)
63
+ : clamp(width - autoMarginForGroup - autoMarginForSampleSet - 200, 10, Infinity);
64
+ const plotHeight = transpose
65
+ ? clamp((height - autoMarginForGroup - autoMarginForSampleSet - 50), 10, Infinity)
66
+ : clamp((height - autoMarginForFeature - 80), 10, Infinity) / (cellSetSelection?.length || 1);
67
+ // Get an array of keys for sorting purposes.
68
+ const groupKeys = data.map(d => d.keyGroup);
69
+ const featureKeys = data.map(d => d.keyFeature);
70
+ const groupSecondaryKeys = data.map(d => d.keyGroupSecondary);
71
+ const meanTransform = (featureValueTransformName && featureValueTransformName !== 'None')
72
+ // Mean Log-Transformed Normalized Expression
73
+ ? [`Mean ${featureValueTransformName}-transformed`, `normalized ${featureValueType}`, 'in set']
74
+ // Mean Normalized Expression
75
+ : ['Mean normalized', `${featureValueType} in set`];
76
+ const spec = {
77
+ mark: {
78
+ type: 'circle',
79
+ // The Vega-Lite default opacity is 0.7 for point, tick, circle,
80
+ // or square marks.
81
+ // Reference: https://vega.github.io/vega-lite/docs/mark.html
82
+ opacity: 1.0,
83
+ },
84
+ encoding: {
85
+ [(transpose ? 'y' : 'x')]: {
86
+ field: 'keyFeature',
87
+ type: 'nominal',
88
+ axis: { labelExpr: `substring(datum.label, ${keyLength})` },
89
+ title: capitalize(featureType),
90
+ sort: featureKeys,
91
+ },
92
+ [(transpose ? 'column' : 'row')]: {
93
+ field: 'keyGroup',
94
+ type: 'nominal',
95
+ header: transpose
96
+ ? { labelExpr: `substring(datum.label, ${keyLength})`, labelAngle: -60, labelAlign: 'right', titleOrient: 'bottom', labelOrient: 'bottom' }
97
+ : { labelExpr: `substring(datum.label, ${keyLength})`, labelAngle: 0, labelAlign: 'left' },
98
+ title: `${capitalize(obsType)} Set`,
99
+ sort: groupKeys,
100
+ spacing: 0,
101
+ },
102
+ color: {
103
+ field: 'meanExpInGroup',
104
+ type: 'quantitative',
105
+ title: meanTransform,
106
+ scale: {
107
+ scheme: featureValueColormap,
108
+ },
109
+ legend: {
110
+ direction: 'horizontal',
111
+ tickCount: 2,
112
+ },
113
+ },
114
+ [(transpose ? 'x' : 'y')]: {
115
+ field: 'keyGroupSecondary',
116
+ type: 'nominal',
117
+ axis: { labelExpr: `substring(datum.label, ${keyLength})` },
118
+ title: null, // TODO: use sampleType
119
+ sort: groupSecondaryKeys,
120
+ },
121
+ size: {
122
+ field: 'pctPosInGroup',
123
+ type: 'quantitative',
124
+ title: [`Percentage of ${plur(obsType, 2)}`, 'in set'],
125
+ legend: {
126
+ symbolFillColor: 'white',
127
+ },
128
+ },
129
+ },
130
+ width: plotWidth,
131
+ height: plotHeight,
132
+ config: {
133
+ ...VEGA_THEMES[theme],
134
+ ...(!isStratified ? {
135
+ // Remove the row/column outlines when
136
+ // not stratified by sample set.
137
+ view: {
138
+ stroke: 'transparent',
139
+ },
140
+ } : {}),
141
+ },
142
+ };
143
+ return (_jsx(VegaPlot, { data: data, spec: spec }));
144
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A subscriber component for DotPlot.
3
+ * @param {object} props
4
+ * @param {function} props.removeGridComponent The grid component removal function.
5
+ * @param {object} props.coordinationScopes An object mapping coordination
6
+ * types to coordination scopes.
7
+ * @param {string} props.theme The name of the current Vitessce theme.
8
+ */
9
+ export function DotPlotSubscriber(props: {
10
+ removeGridComponent: Function;
11
+ coordinationScopes: object;
12
+ theme: string;
13
+ }): JSX.Element;
14
+ //# sourceMappingURL=DotPlotSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DotPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/DotPlotSubscriber.js"],"names":[],"mappings":"AAmBA;;;;;;;GAOG;AACH,yCALG;IAAwB,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;CAChB,eAkJA"}
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useFeatureSelection, useObsSetsData, useObsFeatureMatrixIndices, useFeatureLabelsData, useSampleSetsData, useSampleEdgesData, } from '@vitessce/vit-s';
4
+ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
5
+ import { VALUE_TRANSFORM_OPTIONS } from '@vitessce/utils';
6
+ import CellSetExpressionPlotOptions from './CellSetExpressionPlotOptions.js';
7
+ import DotPlot from './DotPlot.js';
8
+ import { useStyles } from './styles.js';
9
+ import { useExpressionSummaries } from './dot-plot-hook.js';
10
+ /**
11
+ * A subscriber component for DotPlot.
12
+ * @param {object} props
13
+ * @param {function} props.removeGridComponent The grid component removal function.
14
+ * @param {object} props.coordinationScopes An object mapping coordination
15
+ * types to coordination scopes.
16
+ * @param {string} props.theme The name of the current Vitessce theme.
17
+ */
18
+ export function DotPlotSubscriber(props) {
19
+ const { coordinationScopes, removeGridComponent, theme, title = 'Dot Plot', transpose = true, } = props;
20
+ const classes = useStyles();
21
+ const loaders = useLoaders();
22
+ // Get "props" from the coordination space.
23
+ const [{ dataset, obsType, featureType, featureValueType, featureSelection: geneSelection, featureValueTransform, featureValueTransformCoefficient, featureValuePositivityThreshold: posThreshold, featureValueColormap, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, additionalObsSets: additionalCellSets, sampleType, sampleSetSelection, }, { setFeatureValueTransform, setFeatureValueTransformCoefficient, setFeatureValuePositivityThreshold: setPosThreshold, setFeatureValueColormap, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.DOT_PLOT], coordinationScopes);
24
+ const [width, height, containerRef] = useGridItemSize();
25
+ const transformOptions = VALUE_TRANSFORM_OPTIONS;
26
+ const isStratified = Array.isArray(sampleSetSelection) && sampleSetSelection.length > 1;
27
+ // Get data from loaders using the data hooks.
28
+ // eslint-disable-next-line no-unused-vars
29
+ const [expressionData, loadedFeatureSelection, featureSelectionStatus] = useFeatureSelection(loaders, dataset, false, geneSelection, { obsType, featureType, featureValueType });
30
+ // TODO: support multiple feature labels using featureLabelsType coordination values.
31
+ const [{ featureLabelsMap }, featureLabelsStatus, featureLabelsUrl] = useFeatureLabelsData(loaders, dataset, false, {}, {}, { featureType });
32
+ const [{ obsIndex }, matrixIndicesStatus, matrixIndicesUrl] = useObsFeatureMatrixIndices(loaders, dataset, false, { obsType, featureType, featureValueType });
33
+ const [{ obsSets: cellSets }, obsSetsStatus, obsSetsUrl] = useObsSetsData(loaders, dataset, true, {}, {}, { obsType });
34
+ const [{ sampleSets }, sampleSetsStatus, sampleSetsUrl] = useSampleSetsData(loaders, dataset, false, {}, {}, { sampleType });
35
+ const [{ sampleEdges }, sampleEdgesStatus, sampleEdgesUrl] = useSampleEdgesData(loaders, dataset, false, {}, {}, { obsType, sampleType });
36
+ const isReady = useReady([
37
+ featureSelectionStatus,
38
+ matrixIndicesStatus,
39
+ obsSetsStatus,
40
+ featureLabelsStatus,
41
+ sampleSetsStatus,
42
+ sampleEdgesStatus,
43
+ ]);
44
+ const urls = useUrls([
45
+ featureLabelsUrl,
46
+ matrixIndicesUrl,
47
+ obsSetsUrl,
48
+ sampleSetsUrl,
49
+ sampleEdgesUrl,
50
+ ]);
51
+ const [resultArr, meanExpressionMax] = useExpressionSummaries(sampleEdges, sampleSets, sampleSetSelection, expressionData, obsIndex, cellSets, additionalCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient, posThreshold, featureLabelsMap);
52
+ const selectedTransformName = transformOptions.find(o => o.value === featureValueTransform)?.name;
53
+ return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, options: (_jsx(CellSetExpressionPlotOptions, { featureValueTransform: featureValueTransform, setFeatureValueTransform: setFeatureValueTransform, featureValueTransformCoefficient: featureValueTransformCoefficient, setFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, transformOptions: transformOptions, featureValuePositivityThreshold: posThreshold, setFeatureValuePositivityThreshold: setPosThreshold, featureValueColormap: featureValueColormap, setFeatureValueColormap: setFeatureValueColormap })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: resultArr ? (_jsx(DotPlot, { isStratified: isStratified, transpose: transpose, domainMax: meanExpressionMax, data: resultArr, theme: theme, width: width, height: height, obsType: obsType, featureType: featureType, featureValueType: featureValueType, featureValueTransformName: selectedTransformName, featureValueColormap: featureValueColormap, cellSetSelection: cellSetSelection })) : (_jsxs("span", { children: ["Select at least one ", featureType, "."] })) }) }));
54
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressionHistogram.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogram.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH;IAd2B,aAAa,EAA7B,MAAM,EAAE;IAEQ,IAAI,EAApB,MAAM,EAAE;IAEM,KAAK,EAAnB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,WAAW,EAAzB,MAAM;IAGQ,YAAY,EAA1B,MAAM;gBAqGhB"}
1
+ {"version":3,"file":"ExpressionHistogram.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogram.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,mDAdG;IAAwB,aAAa,EAA7B,MAAM,EAAE;IAEQ,IAAI,EAApB,MAAM,EAAE;IAEM,KAAK,EAAnB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,WAAW,EAAzB,MAAM;IAGQ,YAAY,EAA1B,MAAM;CAGhB,eAkGA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressionHistogramSubscriber.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogramSubscriber.js"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;gBA2HhB"}
1
+ {"version":3,"file":"ExpressionHistogramSubscriber.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogramSubscriber.js"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH,qDALG;IAAwB,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;CAChB,eA0HA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Get expression data for the cells
3
+ * in the selected cell sets.
4
+ * @param {object} expressionMatrix
5
+ * @param {string[]} expressionMatrix.rows Cell IDs.
6
+ * @param {string[]} expressionMatrix.cols Gene names.
7
+ * @param {Uint8Array} expressionMatrix.matrix The
8
+ * flattened expression matrix as a typed array.
9
+ * @param {object} cellSets The cell sets from the dataset.
10
+ * @param {object} additionalCellSets The user-defined cell sets
11
+ * from the coordination space.
12
+ * @param {array} geneSelection Array of selected genes.
13
+ * @param {array} cellSetSelection Array of selected cell set paths.
14
+ * @param {object[]} cellSetColor Array of objects with properties
15
+ * @param {string|null} featureValueTransform The name of the
16
+ * feature value transform function.
17
+ * @param {number} featureValueTransformCoefficient A coefficient
18
+ * to be used in the transform function.
19
+ * @param {string} theme "light" or "dark" for the vitessce theme
20
+ * `path` and `color`.
21
+ */
22
+ export function useExpressionSummaries(sampleEdges: any, sampleSets: any, sampleSetSelection: any, expressionData: any, obsIndex: any, cellSets: object, additionalCellSets: object, geneSelection: array, cellSetSelection: array, cellSetColor: object[], featureValueTransform: string | null, featureValueTransformCoefficient: number, posThreshold: any, featureLabelsMap: any): any[];
23
+ //# sourceMappingURL=dot-plot-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dot-plot-hook.d.ts","sourceRoot":"","sources":["../src/dot-plot-hook.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iJAbW,MAAM,sBACN,MAAM,iBAEN,KAAK,oBACL,KAAK,gBACL,MAAM,EAAE,yBACR,MAAM,GAAC,IAAI,oCAEX,MAAM,mDA8EhB"}
@@ -0,0 +1,69 @@
1
+ import { useMemo } from 'react';
2
+ import { InternMap } from 'internmap';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { mergeObsSets, stratifyExpressionData, } from '@vitessce/sets-utils';
5
+ import { dotStratifiedExpressionData, } from './expr-hooks.js';
6
+ /**
7
+ * Get expression data for the cells
8
+ * in the selected cell sets.
9
+ * @param {object} expressionMatrix
10
+ * @param {string[]} expressionMatrix.rows Cell IDs.
11
+ * @param {string[]} expressionMatrix.cols Gene names.
12
+ * @param {Uint8Array} expressionMatrix.matrix The
13
+ * flattened expression matrix as a typed array.
14
+ * @param {object} cellSets The cell sets from the dataset.
15
+ * @param {object} additionalCellSets The user-defined cell sets
16
+ * from the coordination space.
17
+ * @param {array} geneSelection Array of selected genes.
18
+ * @param {array} cellSetSelection Array of selected cell set paths.
19
+ * @param {object[]} cellSetColor Array of objects with properties
20
+ * @param {string|null} featureValueTransform The name of the
21
+ * feature value transform function.
22
+ * @param {number} featureValueTransformCoefficient A coefficient
23
+ * to be used in the transform function.
24
+ * @param {string} theme "light" or "dark" for the vitessce theme
25
+ * `path` and `color`.
26
+ */
27
+ export function useExpressionSummaries(sampleEdges, sampleSets, sampleSetSelection, expressionData, obsIndex, cellSets, additionalCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient, posThreshold, featureLabelsMap) {
28
+ const mergedCellSets = useMemo(() => mergeObsSets(cellSets, additionalCellSets), [cellSets, additionalCellSets]);
29
+ // From the expression matrix and the list of selected genes / cell sets,
30
+ // generate the array of data points for the plot.
31
+ const [resultArr, meanExpressionMax] = useMemo(() => {
32
+ const [stratifiedData, exprMax] = stratifyExpressionData(sampleEdges, sampleSets, sampleSetSelection, expressionData, obsIndex, mergedCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient);
33
+ if (stratifiedData) {
34
+ const dotData = dotStratifiedExpressionData(stratifiedData, posThreshold);
35
+ const geneToUuid = new Map(geneSelection?.map(gene => [gene, uuidv4()]));
36
+ const cellSetToUuid = new InternMap(cellSetSelection?.map(sampleSet => ([sampleSet, uuidv4()])), JSON.stringify);
37
+ const sampleSetToUuid = new InternMap(sampleSetSelection?.map(sampleSet => ([sampleSet, uuidv4()])), JSON.stringify);
38
+ const result = [];
39
+ ([...dotData.keys()]).forEach((cellSetKey) => {
40
+ ([...dotData.get(cellSetKey).keys()]).forEach((sampleSetKey) => {
41
+ ([...dotData.get(cellSetKey).get(sampleSetKey).keys()]).forEach((geneKey) => {
42
+ const dotObj = dotData.get(cellSetKey).get(sampleSetKey).get(geneKey);
43
+ const featureName = geneKey;
44
+ result.push({
45
+ key: uuidv4(), // Unique key for this dot.
46
+ featureKey: geneToUuid.get(geneKey),
47
+ feature: featureLabelsMap?.get(featureName) || featureName,
48
+ groupKey: cellSetToUuid.get(cellSetKey),
49
+ group: cellSetKey?.at(-1),
50
+ secondaryGroup: sampleSetKey?.at(-1),
51
+ secondaryGroupKey: sampleSetToUuid.get(sampleSetKey),
52
+ meanExpInGroup: dotObj.meanExpInGroup,
53
+ fracPosInGroup: dotObj.fracPosInGroup,
54
+ pctPosInGroup: dotObj.fracPosInGroup * 100.0,
55
+ });
56
+ });
57
+ });
58
+ });
59
+ return [result, exprMax];
60
+ }
61
+ return [null, null];
62
+ }, [expressionData, obsIndex, geneSelection,
63
+ mergedCellSets, cellSetSelection,
64
+ featureValueTransform, featureValueTransformCoefficient,
65
+ posThreshold, featureLabelsMap,
66
+ sampleEdges, sampleSets, sampleSetSelection,
67
+ ]);
68
+ return [resultArr, meanExpressionMax];
69
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Supports three-level stratified input
3
+ * (cell set, sample set, gene).
4
+ * @param {*} stratifiedResult
5
+ * @param {*} posThreshold
6
+ * @returns
7
+ */
8
+ export function dotStratifiedExpressionData(stratifiedResult: any, posThreshold: any): any;
9
+ /**
10
+ * Supports two-level stratified input
11
+ * (cell set, sample set).
12
+ * @param {*} stratifiedResult
13
+ * @param {*} keepZeros
14
+ * @returns
15
+ */
16
+ export function summarizeStratifiedExpressionData(stratifiedResult: any, keepZeros: any): any;
17
+ /**
18
+ * Supports two-level summarized input
19
+ * (cell set, sample set),
20
+ * the output from summarizeStratifiedExpressionData.
21
+ * @param {*} summarizedResult
22
+ * @param {*} binCount
23
+ * @param {*} yMinProp
24
+ * @returns
25
+ */
26
+ export function histogramStratifiedExpressionData(summarizedResult: any, binCount: any, yMinProp: any): {
27
+ groupSummaries: {
28
+ key: any;
29
+ value: {
30
+ key: any;
31
+ value: any;
32
+ }[];
33
+ }[];
34
+ groupData: {
35
+ key: any;
36
+ value: {
37
+ key: any;
38
+ value: any;
39
+ }[];
40
+ }[];
41
+ groupBins: {
42
+ key: any;
43
+ value: {
44
+ key: any;
45
+ value: any;
46
+ }[];
47
+ }[];
48
+ groupBinsMax: any;
49
+ y: any;
50
+ };
51
+ //# sourceMappingURL=expr-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expr-hooks.d.ts","sourceRoot":"","sources":["../src/expr-hooks.js"],"names":[],"mappings":"AAgDA;;;;;;GAMG;AACH,8DAJW,GAAC,gBACD,GAAC,OA+BX;AAED;;;;;;GAMG;AACH,oEAJW,GAAC,aACD,GAAC,OAkBX;AAED;;;;;;;;GAQG;AACH,oEALW,GAAC,YACD,GAAC,YACD,GAAC;;;;;;;;;;;;;;;;;;;;;;;;EAsDX"}
@@ -0,0 +1,135 @@
1
+ /* eslint-disable camelcase */
2
+ import { InternMap } from 'internmap';
3
+ import { scaleLinear } from 'd3-scale';
4
+ import { bin, min, max, mean as d3_mean, deviation as d3_deviation, ascending as d3_ascending, quantileSorted, } from 'd3-array';
5
+ // Reference: https://github.com/d3/d3-array/issues/180#issuecomment-851378012
6
+ function summarize(iterable, keepZeros) {
7
+ const values = iterable
8
+ .filter(d => keepZeros || d !== 0.0)
9
+ .sort(d3_ascending);
10
+ const minVal = values[0];
11
+ const maxVal = values[values.length - 1];
12
+ const q1 = quantileSorted(values, 0.25);
13
+ const q2 = quantileSorted(values, 0.5);
14
+ const q3 = quantileSorted(values, 0.75);
15
+ const iqr = q3 - q1; // interquartile range
16
+ const r0 = Math.max(minVal, q1 - iqr * 1.5);
17
+ const r1 = Math.min(maxVal, q3 + iqr * 1.5);
18
+ let i = -1;
19
+ while (values[++i] < r0)
20
+ ;
21
+ const w0 = values[i];
22
+ while (values[++i] <= r1)
23
+ ;
24
+ const w1 = values[i - 1];
25
+ // Chauvenet
26
+ // Reference: https://en.wikipedia.org/wiki/Chauvenet%27s_criterion
27
+ const mean = d3_mean(values);
28
+ const stdv = d3_deviation(values);
29
+ const c0 = mean - 3 * stdv;
30
+ const c1 = mean + 3 * stdv;
31
+ return {
32
+ quartiles: [q1, q2, q3],
33
+ range: [r0, r1],
34
+ whiskers: [w0, w1],
35
+ chauvenetRange: [c0, c1],
36
+ nonOutliers: values.filter(v => c0 <= v && v <= c1),
37
+ };
38
+ }
39
+ /**
40
+ * Supports three-level stratified input
41
+ * (cell set, sample set, gene).
42
+ * @param {*} stratifiedResult
43
+ * @param {*} posThreshold
44
+ * @returns
45
+ */
46
+ export function dotStratifiedExpressionData(stratifiedResult, posThreshold) {
47
+ const result = new InternMap([], JSON.stringify);
48
+ ([...stratifiedResult.keys()]).forEach((cellSetKey) => {
49
+ result.set(cellSetKey, new InternMap([], JSON.stringify));
50
+ ([...stratifiedResult.get(cellSetKey).keys()]).forEach((sampleSetKey) => {
51
+ result.get(cellSetKey).set(sampleSetKey, new InternMap([], JSON.stringify));
52
+ const allGenes = stratifiedResult.get(cellSetKey).get(sampleSetKey);
53
+ ([...allGenes.keys()]).forEach((geneKey) => {
54
+ const values = allGenes.get(geneKey);
55
+ const exprMean = d3_mean(values);
56
+ const numPos = values.reduce((acc, val) => (val > posThreshold ? acc + 1 : acc), 0);
57
+ const fracPos = numPos / values.length;
58
+ const dotSummary = {
59
+ meanExpInGroup: exprMean,
60
+ fracPosInGroup: fracPos,
61
+ };
62
+ result.get(cellSetKey).get(sampleSetKey).set(geneKey, dotSummary);
63
+ });
64
+ });
65
+ });
66
+ return result;
67
+ }
68
+ /**
69
+ * Supports two-level stratified input
70
+ * (cell set, sample set).
71
+ * @param {*} stratifiedResult
72
+ * @param {*} keepZeros
73
+ * @returns
74
+ */
75
+ export function summarizeStratifiedExpressionData(stratifiedResult, keepZeros) {
76
+ const summarizedResult = new InternMap([], JSON.stringify);
77
+ ([...stratifiedResult.keys()]).forEach((cellSetKey) => {
78
+ summarizedResult.set(cellSetKey, new InternMap([], JSON.stringify));
79
+ ([...stratifiedResult.get(cellSetKey).keys()]).forEach((sampleSetKey) => {
80
+ const values = stratifiedResult.get(cellSetKey).get(sampleSetKey);
81
+ const summary = summarize(values, keepZeros);
82
+ summarizedResult.get(cellSetKey).set(sampleSetKey, summary);
83
+ });
84
+ });
85
+ return summarizedResult;
86
+ }
87
+ /**
88
+ * Supports two-level summarized input
89
+ * (cell set, sample set),
90
+ * the output from summarizeStratifiedExpressionData.
91
+ * @param {*} summarizedResult
92
+ * @param {*} binCount
93
+ * @param {*} yMinProp
94
+ * @returns
95
+ */
96
+ export function histogramStratifiedExpressionData(summarizedResult, binCount, yMinProp) {
97
+ const groupSummaries = ([...summarizedResult.keys()]).map(cellSetKey => ({
98
+ key: cellSetKey,
99
+ value: ([...summarizedResult.get(cellSetKey).keys()]).map(sampleSetKey => ({
100
+ key: sampleSetKey,
101
+ value: summarizedResult.get(cellSetKey).get(sampleSetKey),
102
+ })),
103
+ }));
104
+ const groupData = groupSummaries
105
+ .map(({ key, value }) => ({
106
+ key,
107
+ value: value.map(({ key: subKey, value: subValue }) => ({ key: subKey, value: subValue.nonOutliers })),
108
+ }));
109
+ const trimmedData = groupData.map(kv => kv.value.map(subKv => subKv.value).flat()).flat();
110
+ const yMin = (yMinProp === null ? Math.min(0, min(trimmedData)) : yMinProp);
111
+ // For the y domain, use the yMin prop
112
+ // to support a use case such as 'Aspect Ratio',
113
+ // where the domain minimum should be 1 rather than 0.
114
+ const y = scaleLinear()
115
+ .domain([yMin, max(trimmedData)]);
116
+ const histogram = bin()
117
+ .thresholds(y.ticks(binCount))
118
+ .domain(y.domain());
119
+ const groupBins = groupData.map(kv => ({ key: kv.key,
120
+ value: kv.value.map(subKv => ({ key: subKv.key, value: histogram(subKv.value) })) }));
121
+ const groupBinsMax = max(groupBins
122
+ .flatMap(d => d.value.flatMap(subKv => subKv.value.map(v => v.length))));
123
+ return {
124
+ // Array of [{ key, value: [
125
+ // { key, value: {
126
+ // quartiles, range, whiskers, chauvenetRange, nonOutliers }
127
+ // }
128
+ // ] }]
129
+ groupSummaries,
130
+ groupData, // Array of [{ key, value: [{ key, value: nonOutliers }] }]
131
+ groupBins, // Array of [{ key, value: [{ key, value: histogram(nonOutliers) }] }]
132
+ groupBinsMax, // Number
133
+ y, // d3.scaleLinear without a range set
134
+ };
135
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=expr-hooks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expr-hooks.test.d.ts","sourceRoot":"","sources":["../src/expr-hooks.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { stratifyExpressionData, aggregateStratifiedExpressionData, } from '@vitessce/sets-utils';
3
+ import { summarizeStratifiedExpressionData, histogramStratifiedExpressionData, } from './expr-hooks.js';
4
+ describe('Utility functions for processing expression data for statistical plots', () => {
5
+ describe('summarizeStratifiedExpressionData function', () => {
6
+ it('computes summarized information accurately', () => {
7
+ const sampleEdges = new Map([
8
+ ['cell1-1', 'donor1'],
9
+ ['cell1-2', 'donor1'],
10
+ ['cell1-3', 'donor1'],
11
+ ['cell1-4', 'donor1'],
12
+ ['cell2-1', 'donor2'],
13
+ ['cell2-2', 'donor2'],
14
+ ['cell2-3', 'donor2'],
15
+ ['cell2-4', 'donor2'],
16
+ ]);
17
+ const sampleSets = {
18
+ tree: [
19
+ {
20
+ name: 'Clinical groups',
21
+ children: [
22
+ {
23
+ name: 'AKI',
24
+ set: [['donor1', null]],
25
+ },
26
+ {
27
+ name: 'CKD',
28
+ set: [['donor2', null]],
29
+ },
30
+ ],
31
+ },
32
+ ],
33
+ };
34
+ const sampleSetSelection = [
35
+ ['Clinical groups', 'AKI'],
36
+ ['Clinical groups', 'CKD'],
37
+ ];
38
+ const expressionData = [
39
+ // Gene 1
40
+ [10, 20, 30, 40, 11, 21, 31, 41],
41
+ ];
42
+ const obsIndex = ['cell1-1', 'cell1-2', 'cell1-3', 'cell1-4', 'cell2-1', 'cell2-2', 'cell2-3', 'cell2-4'];
43
+ const mergedCellSets = {
44
+ tree: [
45
+ {
46
+ name: 'Cell type',
47
+ children: [
48
+ {
49
+ name: 'T cell',
50
+ set: [['cell1-1', null], ['cell1-3', null], ['cell2-1', null], ['cell2-3', null]],
51
+ },
52
+ {
53
+ name: 'B cell',
54
+ set: [['cell1-2', null], ['cell1-4', null], ['cell2-2', null], ['cell2-4', null]],
55
+ },
56
+ ],
57
+ },
58
+ ],
59
+ };
60
+ const geneSelection = [
61
+ 'Gene 1',
62
+ ];
63
+ const cellSetSelection = [
64
+ ['Cell type', 'T cell'],
65
+ ['Cell type', 'B cell'],
66
+ ];
67
+ const cellSetColor = [
68
+ { set: ['Cell type', 'T cell'], color: [255, 0, 0] },
69
+ { set: ['Cell type', 'B cell'], color: [0, 255, 0] },
70
+ ];
71
+ const featureValueTransform = null;
72
+ const featureValueTransformCoefficient = 1;
73
+ const [result] = stratifyExpressionData(sampleEdges, sampleSets, sampleSetSelection, expressionData, obsIndex, mergedCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient);
74
+ const aggregateData = aggregateStratifiedExpressionData(result, geneSelection);
75
+ const summaryResult = summarizeStratifiedExpressionData(aggregateData, true);
76
+ expect([...summaryResult.keys()]).toEqual([['Cell type', 'T cell'], ['Cell type', 'B cell']]);
77
+ expect([...summaryResult.get(['Cell type', 'T cell']).keys()]).toEqual([['Clinical groups', 'AKI'], ['Clinical groups', 'CKD']]);
78
+ expect(Object.keys(summaryResult.get(['Cell type', 'T cell']).get(['Clinical groups', 'AKI']))).toEqual([
79
+ 'quartiles',
80
+ 'range',
81
+ 'whiskers',
82
+ 'chauvenetRange',
83
+ 'nonOutliers',
84
+ ]);
85
+ const histogramResult = histogramStratifiedExpressionData(summaryResult, 16, null);
86
+ expect(Object.keys(histogramResult)).toEqual([
87
+ 'groupSummaries',
88
+ 'groupData',
89
+ 'groupBins',
90
+ 'groupBinsMax',
91
+ 'y',
92
+ ]);
93
+ expect(histogramResult.groupSummaries.map(d => d.key)).toEqual([['Cell type', 'T cell'], ['Cell type', 'B cell']]);
94
+ expect(histogramResult.groupBinsMax).toEqual(1);
95
+ });
96
+ });
97
+ });