@vitessce/statistical-plots 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +76191 -0
- package/{dist → dist-tsc}/index.js +0 -0
- package/package.json +10 -10
- package/src/CellSetExpressionPlot.js +278 -0
- package/src/CellSetExpressionPlotOptions.js +75 -0
- package/src/CellSetExpressionPlotSubscriber.js +228 -0
- package/src/CellSetSizesPlot.js +96 -0
- package/src/CellSetSizesPlotSubscriber.js +105 -0
- package/{dist → src}/ExpressionHistogram.js +42 -26
- package/src/ExpressionHistogramSubscriber.js +118 -0
- package/src/index.js +6 -0
- package/src/styles.js +9 -0
- package/dist/CellSetExpressionPlot.js +0 -248
- package/dist/CellSetExpressionPlotOptions.js +0 -32
- package/dist/CellSetExpressionPlotSubscriber.js +0 -111
- package/dist/CellSetSizesPlot.js +0 -77
- package/dist/CellSetSizesPlotSubscriber.js +0 -44
- package/dist/ExpressionHistogramSubscriber.js +0 -63
- package/dist/styles.js +0 -8
File without changes
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitessce/statistical-plots",
|
3
|
-
"version": "2.0.
|
3
|
+
"version": "2.0.3",
|
4
4
|
"author": "Gehlenborg Lab",
|
5
5
|
"homepage": "http://vitessce.io",
|
6
6
|
"repository": {
|
@@ -8,19 +8,20 @@
|
|
8
8
|
"url": "git+https://github.com/vitessce/vitessce.git"
|
9
9
|
},
|
10
10
|
"license": "MIT",
|
11
|
-
"main": "dist/index.
|
11
|
+
"main": "dist/index.mjs",
|
12
12
|
"files": [
|
13
|
-
"dist"
|
13
|
+
"dist",
|
14
|
+
"src"
|
14
15
|
],
|
15
16
|
"dependencies": {
|
16
17
|
"@material-ui/core": "~4.12.3",
|
17
18
|
"d3-array": "^2.4.0",
|
18
19
|
"lodash": "^4.17.21",
|
19
|
-
"@vitessce/constants-internal": "2.0.
|
20
|
-
"@vitessce/sets-utils": "2.0.
|
21
|
-
"@vitessce/utils": "2.0.
|
22
|
-
"@vitessce/vega": "2.0.
|
23
|
-
"@vitessce/vit-s": "2.0.
|
20
|
+
"@vitessce/constants-internal": "2.0.3",
|
21
|
+
"@vitessce/sets-utils": "2.0.3",
|
22
|
+
"@vitessce/utils": "2.0.3",
|
23
|
+
"@vitessce/vega": "2.0.3",
|
24
|
+
"@vitessce/vit-s": "2.0.3"
|
24
25
|
},
|
25
26
|
"devDependencies": {
|
26
27
|
"react": "^18.0.0",
|
@@ -31,8 +32,7 @@
|
|
31
32
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
32
33
|
},
|
33
34
|
"scripts": {
|
34
|
-
"
|
35
|
-
"build": "tsc",
|
35
|
+
"bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
|
36
36
|
"test": "pnpm exec vitest --run -r ../../../ --dir ."
|
37
37
|
}
|
38
38
|
}
|
@@ -0,0 +1,278 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import clamp from 'lodash/clamp';
|
3
|
+
import { VegaPlot, VEGA_THEMES, DATASET_NAME } from '@vitessce/vega';
|
4
|
+
import { colorArrayToString } from '@vitessce/sets-utils';
|
5
|
+
import { capitalize } from '@vitessce/utils';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Gene expression histogram displayed as a bar chart,
|
9
|
+
* implemented with the VegaPlot component.
|
10
|
+
* @param {object} props
|
11
|
+
* @param {object[]} props.data The expression data, an array
|
12
|
+
* of objects with properties `value`, `gene`, and `set`.
|
13
|
+
* @param {number} props.domainMax The maximum gene expression value.
|
14
|
+
* @param {object[]} props.colors An object for each
|
15
|
+
* cell set, with properties `name` and `color`.
|
16
|
+
* @param {string} props.theme The name of the current Vitessce theme.
|
17
|
+
* @param {number} props.width The container width.
|
18
|
+
* @param {number} props.height The container height.
|
19
|
+
* @param {number} props.marginRight The size of the margin
|
20
|
+
* on the right side of the plot, to account for the vega menu button.
|
21
|
+
* By default, 90.
|
22
|
+
* @param {number} props.marginBottom The size of the margin
|
23
|
+
* on the bottom of the plot, to account for long x-axis labels.
|
24
|
+
* Default is allowing the component to automatically determine the margin.
|
25
|
+
* @param {string|null} props.featureValueTransformName A name
|
26
|
+
* for the feature value transformation function.
|
27
|
+
*/
|
28
|
+
export default function CellSetExpressionPlot(props) {
|
29
|
+
const {
|
30
|
+
domainMax = 100,
|
31
|
+
colors,
|
32
|
+
data,
|
33
|
+
theme,
|
34
|
+
width,
|
35
|
+
height,
|
36
|
+
marginRight = 90,
|
37
|
+
marginBottom,
|
38
|
+
obsType,
|
39
|
+
featureValueType,
|
40
|
+
featureValueTransformName,
|
41
|
+
} = props;
|
42
|
+
// Get the max characters in an axis label for autsizing the bottom margin.
|
43
|
+
const maxCharactersForLabel = data.reduce((acc, val) => {
|
44
|
+
// eslint-disable-next-line no-param-reassign
|
45
|
+
acc = acc === undefined || val.set.length > acc ? val.set.length : acc;
|
46
|
+
return acc;
|
47
|
+
}, 0);
|
48
|
+
// Use a square-root term because the angle of the labels is 45 degrees (see below)
|
49
|
+
// so the perpendicular distance to the bottom of the labels is proportional to the
|
50
|
+
// square root of the length of the labels along the imaginary hypotenuse.
|
51
|
+
// 30 is an estimate of the pixel size of a given character and seems to work well.
|
52
|
+
const autoMarginBottom = marginBottom
|
53
|
+
|| 30 + Math.sqrt(maxCharactersForLabel / 2) * 30;
|
54
|
+
// Manually set the color scale so that Vega-Lite does
|
55
|
+
// not choose the colors automatically.
|
56
|
+
const colorScale = {
|
57
|
+
domain: colors.map(d => d.name),
|
58
|
+
range: colors.map(d => colorArrayToString(d.color)),
|
59
|
+
};
|
60
|
+
|
61
|
+
const plotWidth = clamp(width - marginRight, 10, Infinity);
|
62
|
+
const plotHeight = clamp(height - autoMarginBottom, 10, Infinity);
|
63
|
+
|
64
|
+
const numBands = colors.length;
|
65
|
+
const bandWidth = plotWidth / numBands;
|
66
|
+
|
67
|
+
const rectColor = (theme === 'dark' ? 'white' : 'black');
|
68
|
+
|
69
|
+
const spec = {
|
70
|
+
$schema: 'https://vega.github.io/schema/vega/v5.json',
|
71
|
+
description: `A violin plot showing distributions of expression levels for selected ${obsType} sets.`,
|
72
|
+
width: plotWidth,
|
73
|
+
height: plotHeight,
|
74
|
+
config: {
|
75
|
+
...VEGA_THEMES[theme],
|
76
|
+
axisBand: {
|
77
|
+
bandPosition: 1,
|
78
|
+
tickExtra: true,
|
79
|
+
tickOffset: 0,
|
80
|
+
},
|
81
|
+
},
|
82
|
+
|
83
|
+
signals: [
|
84
|
+
{ name: 'bandWidth', value: bandWidth },
|
85
|
+
{ name: 'width', value: plotWidth },
|
86
|
+
{ name: 'height', value: plotHeight },
|
87
|
+
{ name: 'trim', value: true },
|
88
|
+
],
|
89
|
+
|
90
|
+
data: [
|
91
|
+
{
|
92
|
+
name: 'density',
|
93
|
+
source: DATASET_NAME,
|
94
|
+
transform: [
|
95
|
+
{
|
96
|
+
type: 'kde',
|
97
|
+
field: 'value',
|
98
|
+
groupby: ['set'],
|
99
|
+
bandwidth: 0,
|
100
|
+
extent: [0, domainMax],
|
101
|
+
},
|
102
|
+
],
|
103
|
+
},
|
104
|
+
{
|
105
|
+
name: 'stats',
|
106
|
+
source: DATASET_NAME,
|
107
|
+
transform: [
|
108
|
+
{
|
109
|
+
type: 'aggregate',
|
110
|
+
groupby: ['set'],
|
111
|
+
fields: ['value', 'value', 'value'],
|
112
|
+
ops: ['q1', 'median', 'q3'],
|
113
|
+
as: ['q1', 'median', 'q3'],
|
114
|
+
},
|
115
|
+
],
|
116
|
+
},
|
117
|
+
],
|
118
|
+
|
119
|
+
scales: [
|
120
|
+
{
|
121
|
+
name: 'layout',
|
122
|
+
type: 'band',
|
123
|
+
range: 'width',
|
124
|
+
domain: { data: DATASET_NAME, field: 'set' },
|
125
|
+
},
|
126
|
+
{
|
127
|
+
name: 'yscale',
|
128
|
+
type: 'linear',
|
129
|
+
range: 'height',
|
130
|
+
domain: [0, domainMax],
|
131
|
+
},
|
132
|
+
{
|
133
|
+
name: 'wscale',
|
134
|
+
type: 'linear',
|
135
|
+
range: [0, { signal: 'bandWidth' }],
|
136
|
+
domain: { data: 'density', field: 'density' },
|
137
|
+
},
|
138
|
+
{
|
139
|
+
name: 'wscaleReversed',
|
140
|
+
type: 'linear',
|
141
|
+
reverse: true,
|
142
|
+
range: [0, { signal: 'bandWidth' }],
|
143
|
+
domain: { data: 'density', field: 'density' },
|
144
|
+
},
|
145
|
+
{
|
146
|
+
name: 'color',
|
147
|
+
type: 'ordinal',
|
148
|
+
...colorScale,
|
149
|
+
},
|
150
|
+
],
|
151
|
+
|
152
|
+
axes: [
|
153
|
+
{
|
154
|
+
orient: 'left',
|
155
|
+
scale: 'yscale',
|
156
|
+
zindex: 1,
|
157
|
+
title: (featureValueTransformName && featureValueTransformName !== 'None')
|
158
|
+
? [`${featureValueTransformName}-Transformed`, `Normalized ${capitalize(featureValueType)} Values`]
|
159
|
+
: `Normalized ${capitalize(featureValueType)} Values`,
|
160
|
+
},
|
161
|
+
{
|
162
|
+
orient: 'bottom',
|
163
|
+
scale: 'layout',
|
164
|
+
tickCount: 5,
|
165
|
+
zindex: 1,
|
166
|
+
title: `${capitalize(obsType)} Set`,
|
167
|
+
labelAngle: -45,
|
168
|
+
labelAlign: 'right',
|
169
|
+
},
|
170
|
+
],
|
171
|
+
|
172
|
+
marks: [
|
173
|
+
{
|
174
|
+
type: 'group',
|
175
|
+
from: {
|
176
|
+
facet: {
|
177
|
+
data: 'density',
|
178
|
+
name: 'violin',
|
179
|
+
groupby: 'set',
|
180
|
+
},
|
181
|
+
},
|
182
|
+
|
183
|
+
encode: {
|
184
|
+
enter: {
|
185
|
+
xc: { scale: 'layout', field: 'set', band: 0.5 },
|
186
|
+
width: { signal: 'bandWidth' },
|
187
|
+
height: { signal: 'height' },
|
188
|
+
},
|
189
|
+
},
|
190
|
+
|
191
|
+
data: [
|
192
|
+
{
|
193
|
+
name: 'summary',
|
194
|
+
source: 'stats',
|
195
|
+
transform: [
|
196
|
+
{
|
197
|
+
type: 'filter',
|
198
|
+
expr: 'datum.set === parent.set',
|
199
|
+
},
|
200
|
+
],
|
201
|
+
},
|
202
|
+
],
|
203
|
+
|
204
|
+
marks: [
|
205
|
+
{
|
206
|
+
type: 'area',
|
207
|
+
orient: 'vertical',
|
208
|
+
from: { data: 'violin' },
|
209
|
+
encode: {
|
210
|
+
enter: {
|
211
|
+
fill: { scale: 'color', field: { parent: 'set' } },
|
212
|
+
},
|
213
|
+
update: {
|
214
|
+
width: { scale: 'wscale', field: 'density' },
|
215
|
+
xc: { signal: 'bandWidth / 2' },
|
216
|
+
y2: { scale: 'yscale', field: 'value' },
|
217
|
+
y: { scale: 'yscale', value: 0 },
|
218
|
+
},
|
219
|
+
},
|
220
|
+
},
|
221
|
+
{
|
222
|
+
type: 'area',
|
223
|
+
orient: 'vertical',
|
224
|
+
from: { data: 'violin' },
|
225
|
+
encode: {
|
226
|
+
enter: {
|
227
|
+
fill: { scale: 'color', field: { parent: 'set' } },
|
228
|
+
},
|
229
|
+
update: {
|
230
|
+
width: { scale: 'wscaleReversed', field: 'density' },
|
231
|
+
xc: { signal: 'bandWidth' },
|
232
|
+
y2: { scale: 'yscale', field: 'value' },
|
233
|
+
y: { scale: 'yscale', value: 0 },
|
234
|
+
},
|
235
|
+
},
|
236
|
+
},
|
237
|
+
{
|
238
|
+
type: 'rect',
|
239
|
+
from: { data: 'summary' },
|
240
|
+
encode: {
|
241
|
+
enter: {
|
242
|
+
fill: { value: rectColor },
|
243
|
+
width: { value: 2 },
|
244
|
+
},
|
245
|
+
update: {
|
246
|
+
y: { scale: 'yscale', field: 'q1' },
|
247
|
+
y2: { scale: 'yscale', field: 'q3' },
|
248
|
+
xc: { signal: 'bandWidth / 2' },
|
249
|
+
},
|
250
|
+
},
|
251
|
+
},
|
252
|
+
{
|
253
|
+
type: 'rect',
|
254
|
+
from: { data: 'summary' },
|
255
|
+
encode: {
|
256
|
+
enter: {
|
257
|
+
fill: { value: rectColor },
|
258
|
+
height: { value: 2 },
|
259
|
+
width: { value: 8 },
|
260
|
+
},
|
261
|
+
update: {
|
262
|
+
y: { scale: 'yscale', field: 'median' },
|
263
|
+
xc: { signal: 'bandWidth / 2' },
|
264
|
+
},
|
265
|
+
},
|
266
|
+
},
|
267
|
+
],
|
268
|
+
},
|
269
|
+
],
|
270
|
+
};
|
271
|
+
|
272
|
+
return (
|
273
|
+
<VegaPlot
|
274
|
+
data={data}
|
275
|
+
spec={spec}
|
276
|
+
/>
|
277
|
+
);
|
278
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import TableCell from '@material-ui/core/TableCell';
|
3
|
+
import TableRow from '@material-ui/core/TableRow';
|
4
|
+
import TextField from '@material-ui/core/TextField';
|
5
|
+
import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
|
6
|
+
|
7
|
+
export default function CellSetExpressionPlotOptions(props) {
|
8
|
+
const {
|
9
|
+
featureValueTransform,
|
10
|
+
setFeatureValueTransform,
|
11
|
+
featureValueTransformCoefficient,
|
12
|
+
setFeatureValueTransformCoefficient,
|
13
|
+
transformOptions,
|
14
|
+
} = props;
|
15
|
+
const classes = usePlotOptionsStyles();
|
16
|
+
|
17
|
+
const handleTransformChange = (event) => {
|
18
|
+
setFeatureValueTransform(event.target.value === '' ? null : event.target.value);
|
19
|
+
};
|
20
|
+
|
21
|
+
// Feels a little hacky, but I think this is the best way to handle
|
22
|
+
// the limitations of the v4 material-ui number input.
|
23
|
+
const handleTransformCoefficientChange = (event) => {
|
24
|
+
const { value } = event.target;
|
25
|
+
if (!value) {
|
26
|
+
setFeatureValueTransformCoefficient(value);
|
27
|
+
} else {
|
28
|
+
const newCoefficient = Number(value);
|
29
|
+
if (!Number.isNaN(newCoefficient) && newCoefficient >= 0) {
|
30
|
+
setFeatureValueTransformCoefficient(value);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
};
|
34
|
+
|
35
|
+
return (
|
36
|
+
<OptionsContainer>
|
37
|
+
<TableRow>
|
38
|
+
<TableCell className={classes.labelCell}>Transform</TableCell>
|
39
|
+
<TableCell className={classes.inputCell}>
|
40
|
+
<OptionSelect
|
41
|
+
key="gating-transform-select"
|
42
|
+
className={classes.select}
|
43
|
+
value={featureValueTransform === null ? '' : featureValueTransform}
|
44
|
+
onChange={handleTransformChange}
|
45
|
+
inputProps={{
|
46
|
+
id: 'scatterplot-transform-select',
|
47
|
+
}}
|
48
|
+
>
|
49
|
+
{transformOptions.map(opt => (
|
50
|
+
<option key={opt.name} value={opt.value === null ? '' : opt.value}>
|
51
|
+
{opt.name}
|
52
|
+
</option>
|
53
|
+
))}
|
54
|
+
</OptionSelect>
|
55
|
+
</TableCell>
|
56
|
+
</TableRow>
|
57
|
+
<TableRow key="transform-coefficient-option-row">
|
58
|
+
<TableCell className={classes.labelCell}>
|
59
|
+
Transform Coefficient
|
60
|
+
</TableCell>
|
61
|
+
<TableCell className={classes.inputCell}>
|
62
|
+
<TextField
|
63
|
+
label="Number"
|
64
|
+
type="number"
|
65
|
+
onChange={handleTransformCoefficientChange}
|
66
|
+
value={featureValueTransformCoefficient}
|
67
|
+
InputLabelProps={{
|
68
|
+
shrink: true,
|
69
|
+
}}
|
70
|
+
/>
|
71
|
+
</TableCell>
|
72
|
+
</TableRow>
|
73
|
+
</OptionsContainer>
|
74
|
+
);
|
75
|
+
}
|
@@ -0,0 +1,228 @@
|
|
1
|
+
import React, { useMemo } from 'react';
|
2
|
+
import {
|
3
|
+
TitleInfo,
|
4
|
+
useCoordination, useLoaders,
|
5
|
+
useUrls, useReady, useGridItemSize,
|
6
|
+
useFeatureSelection, useObsSetsData,
|
7
|
+
useObsFeatureMatrixIndices,
|
8
|
+
useFeatureLabelsData,
|
9
|
+
registerPluginViewType,
|
10
|
+
} from '@vitessce/vit-s';
|
11
|
+
import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
12
|
+
import { VALUE_TRANSFORM_OPTIONS, capitalize, getValueTransformFunction } from '@vitessce/utils';
|
13
|
+
import { treeToObjectsBySetNames, treeToSetSizesBySetNames, mergeObsSets } from '@vitessce/sets-utils';
|
14
|
+
import CellSetExpressionPlotOptions from './CellSetExpressionPlotOptions';
|
15
|
+
import CellSetExpressionPlot from './CellSetExpressionPlot';
|
16
|
+
import { useStyles } from './styles';
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Get expression data for the cells
|
20
|
+
* in the selected cell sets.
|
21
|
+
* @param {object} expressionMatrix
|
22
|
+
* @param {string[]} expressionMatrix.rows Cell IDs.
|
23
|
+
* @param {string[]} expressionMatrix.cols Gene names.
|
24
|
+
* @param {Uint8Array} expressionMatrix.matrix The
|
25
|
+
* flattened expression matrix as a typed array.
|
26
|
+
* @param {object} cellSets The cell sets from the dataset.
|
27
|
+
* @param {object} additionalCellSets The user-defined cell sets
|
28
|
+
* from the coordination space.
|
29
|
+
* @param {array} geneSelection Array of selected genes.
|
30
|
+
* @param {array} cellSetSelection Array of selected cell set paths.
|
31
|
+
* @param {object[]} cellSetColor Array of objects with properties
|
32
|
+
* @param {string|null} featureValueTransform The name of the
|
33
|
+
* feature value transform function.
|
34
|
+
* @param {number} featureValueTransformCoefficient A coefficient
|
35
|
+
* to be used in the transform function.
|
36
|
+
* @param {string} theme "light" or "dark" for the vitessce theme
|
37
|
+
* `path` and `color`.
|
38
|
+
*/
|
39
|
+
export function useExpressionByCellSet(
|
40
|
+
expressionData, obsIndex, cellSets, additionalCellSets,
|
41
|
+
geneSelection, cellSetSelection, cellSetColor,
|
42
|
+
featureValueTransform, featureValueTransformCoefficient,
|
43
|
+
theme,
|
44
|
+
) {
|
45
|
+
const mergedCellSets = useMemo(
|
46
|
+
() => mergeObsSets(cellSets, additionalCellSets),
|
47
|
+
[cellSets, additionalCellSets],
|
48
|
+
);
|
49
|
+
|
50
|
+
// From the expression matrix and the list of selected genes / cell sets,
|
51
|
+
// generate the array of data points for the plot.
|
52
|
+
const [expressionArr, expressionMax] = useMemo(() => {
|
53
|
+
if (mergedCellSets && cellSetSelection
|
54
|
+
&& geneSelection && geneSelection.length >= 1
|
55
|
+
&& expressionData
|
56
|
+
) {
|
57
|
+
const cellObjects = treeToObjectsBySetNames(
|
58
|
+
mergedCellSets, cellSetSelection, cellSetColor, theme,
|
59
|
+
);
|
60
|
+
|
61
|
+
const firstGeneSelected = geneSelection[0];
|
62
|
+
// Create new cellColors map based on the selected gene.
|
63
|
+
let exprMax = -Infinity;
|
64
|
+
const cellIndices = {};
|
65
|
+
for (let i = 0; i < obsIndex.length; i += 1) {
|
66
|
+
cellIndices[obsIndex[i]] = i;
|
67
|
+
}
|
68
|
+
const exprValues = cellObjects.map((cell) => {
|
69
|
+
const cellIndex = cellIndices[cell.obsId];
|
70
|
+
const value = expressionData[0][cellIndex];
|
71
|
+
const normValue = value * 100 / 255;
|
72
|
+
const transformFunction = getValueTransformFunction(
|
73
|
+
featureValueTransform, featureValueTransformCoefficient,
|
74
|
+
);
|
75
|
+
const transformedValue = transformFunction(normValue);
|
76
|
+
exprMax = Math.max(transformedValue, exprMax);
|
77
|
+
return { value: transformedValue, gene: firstGeneSelected, set: cell.name };
|
78
|
+
});
|
79
|
+
return [exprValues, exprMax];
|
80
|
+
}
|
81
|
+
return [null, null];
|
82
|
+
}, [expressionData, obsIndex, geneSelection, theme,
|
83
|
+
mergedCellSets, cellSetSelection, cellSetColor,
|
84
|
+
featureValueTransform, featureValueTransformCoefficient,
|
85
|
+
]);
|
86
|
+
|
87
|
+
// From the cell sets hierarchy and the list of selected cell sets,
|
88
|
+
// generate the array of set sizes data points for the bar plot.
|
89
|
+
const setArr = useMemo(() => (mergedCellSets && cellSetSelection && cellSetColor
|
90
|
+
? treeToSetSizesBySetNames(mergedCellSets, cellSetSelection, cellSetColor, theme)
|
91
|
+
: []
|
92
|
+
), [mergedCellSets, cellSetSelection, cellSetColor, theme]);
|
93
|
+
|
94
|
+
return [expressionArr, setArr, expressionMax];
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
/**
|
99
|
+
* A subscriber component for `CellSetExpressionPlot`,
|
100
|
+
* which listens for gene selection updates and
|
101
|
+
* `GRID_RESIZE` events.
|
102
|
+
* @param {object} props
|
103
|
+
* @param {function} props.removeGridComponent The grid component removal function.
|
104
|
+
* @param {object} props.coordinationScopes An object mapping coordination
|
105
|
+
* types to coordination scopes.
|
106
|
+
* @param {string} props.theme The name of the current Vitessce theme.
|
107
|
+
*/
|
108
|
+
export function CellSetExpressionPlotSubscriber(props) {
|
109
|
+
const {
|
110
|
+
coordinationScopes,
|
111
|
+
removeGridComponent,
|
112
|
+
theme,
|
113
|
+
} = props;
|
114
|
+
|
115
|
+
const classes = useStyles();
|
116
|
+
const loaders = useLoaders();
|
117
|
+
|
118
|
+
// Get "props" from the coordination space.
|
119
|
+
const [{
|
120
|
+
dataset,
|
121
|
+
obsType,
|
122
|
+
featureType,
|
123
|
+
featureValueType,
|
124
|
+
featureSelection: geneSelection,
|
125
|
+
featureValueTransform,
|
126
|
+
featureValueTransformCoefficient,
|
127
|
+
obsSetSelection: cellSetSelection,
|
128
|
+
obsSetColor: cellSetColor,
|
129
|
+
additionalObsSets: additionalCellSets,
|
130
|
+
}, {
|
131
|
+
setFeatureValueTransform,
|
132
|
+
setFeatureValueTransformCoefficient,
|
133
|
+
}] = useCoordination(
|
134
|
+
COMPONENT_COORDINATION_TYPES[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION],
|
135
|
+
coordinationScopes,
|
136
|
+
);
|
137
|
+
|
138
|
+
const [width, height, containerRef] = useGridItemSize();
|
139
|
+
const [urls, addUrl] = useUrls(loaders, dataset);
|
140
|
+
|
141
|
+
const transformOptions = VALUE_TRANSFORM_OPTIONS;
|
142
|
+
|
143
|
+
// Get data from loaders using the data hooks.
|
144
|
+
// eslint-disable-next-line no-unused-vars
|
145
|
+
const [expressionData, loadedFeatureSelection, featureSelectionStatus] = useFeatureSelection(
|
146
|
+
loaders, dataset, false, geneSelection,
|
147
|
+
{ obsType, featureType, featureValueType },
|
148
|
+
);
|
149
|
+
// TODO: support multiple feature labels using featureLabelsType coordination values.
|
150
|
+
const [{ featureLabelsMap }, featureLabelsStatus] = useFeatureLabelsData(
|
151
|
+
loaders, dataset, addUrl, false, {}, {},
|
152
|
+
{ featureType },
|
153
|
+
);
|
154
|
+
const [{ obsIndex }, matrixIndicesStatus] = useObsFeatureMatrixIndices(
|
155
|
+
loaders, dataset, addUrl, false,
|
156
|
+
{ obsType, featureType, featureValueType },
|
157
|
+
);
|
158
|
+
const [{ obsSets: cellSets }, obsSetsStatus] = useObsSetsData(
|
159
|
+
loaders, dataset, addUrl, true, {}, {},
|
160
|
+
{ obsType },
|
161
|
+
);
|
162
|
+
const isReady = useReady([
|
163
|
+
featureSelectionStatus,
|
164
|
+
matrixIndicesStatus,
|
165
|
+
obsSetsStatus,
|
166
|
+
featureLabelsStatus,
|
167
|
+
]);
|
168
|
+
|
169
|
+
const [expressionArr, setArr, expressionMax] = useExpressionByCellSet(
|
170
|
+
expressionData, obsIndex, cellSets, additionalCellSets,
|
171
|
+
geneSelection, cellSetSelection, cellSetColor,
|
172
|
+
featureValueTransform, featureValueTransformCoefficient,
|
173
|
+
theme,
|
174
|
+
);
|
175
|
+
|
176
|
+
const firstGeneSelected = geneSelection && geneSelection.length >= 1
|
177
|
+
? (featureLabelsMap?.get(geneSelection[0]) || geneSelection[0])
|
178
|
+
: null;
|
179
|
+
const selectedTransformName = transformOptions.find(
|
180
|
+
o => o.value === featureValueTransform,
|
181
|
+
)?.name;
|
182
|
+
|
183
|
+
|
184
|
+
return (
|
185
|
+
<TitleInfo
|
186
|
+
title={`Expression by ${capitalize(obsType)} Set${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`}
|
187
|
+
removeGridComponent={removeGridComponent}
|
188
|
+
urls={urls}
|
189
|
+
theme={theme}
|
190
|
+
isReady={isReady}
|
191
|
+
options={(
|
192
|
+
<CellSetExpressionPlotOptions
|
193
|
+
featureValueTransform={featureValueTransform}
|
194
|
+
setFeatureValueTransform={setFeatureValueTransform}
|
195
|
+
featureValueTransformCoefficient={featureValueTransformCoefficient}
|
196
|
+
setFeatureValueTransformCoefficient={setFeatureValueTransformCoefficient}
|
197
|
+
transformOptions={transformOptions}
|
198
|
+
/>
|
199
|
+
)}
|
200
|
+
>
|
201
|
+
<div ref={containerRef} className={classes.vegaContainer}>
|
202
|
+
{expressionArr ? (
|
203
|
+
<CellSetExpressionPlot
|
204
|
+
domainMax={expressionMax}
|
205
|
+
colors={setArr}
|
206
|
+
data={expressionArr}
|
207
|
+
theme={theme}
|
208
|
+
width={width}
|
209
|
+
height={height}
|
210
|
+
obsType={obsType}
|
211
|
+
featureValueType={featureValueType}
|
212
|
+
featureValueTransformName={selectedTransformName}
|
213
|
+
/>
|
214
|
+
) : (
|
215
|
+
<span>Select a {featureType}.</span>
|
216
|
+
)}
|
217
|
+
</div>
|
218
|
+
</TitleInfo>
|
219
|
+
);
|
220
|
+
}
|
221
|
+
|
222
|
+
export function register() {
|
223
|
+
registerPluginViewType(
|
224
|
+
ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION,
|
225
|
+
CellSetExpressionPlotSubscriber,
|
226
|
+
COMPONENT_COORDINATION_TYPES[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION],
|
227
|
+
);
|
228
|
+
}
|