@vitessce/statistical-plots 3.5.10 → 3.5.12

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 (35) hide show
  1. package/dist/{deflate-ad0dcbe4.js → deflate-9e272e07.js} +1 -1
  2. package/dist/{index-b8398176.js → index-fa429ace.js} +230 -83
  3. package/dist/index.js +1 -1
  4. package/dist/{jpeg-81bd1053.js → jpeg-affe217d.js} +1 -1
  5. package/dist/{lerc-b15c3a4c.js → lerc-eeb05714.js} +1 -1
  6. package/dist/{lzw-503cb795.js → lzw-fdf7d374.js} +1 -1
  7. package/dist/{packbits-40cbad40.js → packbits-530d3977.js} +1 -1
  8. package/dist/{raw-9b8d9daf.js → raw-5d9364f4.js} +1 -1
  9. package/dist/{webimage-bbc59b4a.js → webimage-57a383de.js} +1 -1
  10. package/dist-tsc/CellSetCompositionBarPlot.d.ts.map +1 -1
  11. package/dist-tsc/CellSetCompositionBarPlot.js +28 -7
  12. package/dist-tsc/CellSetExpressionPlotOptions.d.ts.map +1 -1
  13. package/dist-tsc/CellSetExpressionPlotOptions.js +11 -4
  14. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
  15. package/dist-tsc/CellSetExpressionPlotSubscriber.js +43 -11
  16. package/dist-tsc/DotPlot.d.ts.map +1 -1
  17. package/dist-tsc/DotPlot.js +6 -2
  18. package/dist-tsc/FeatureSetEnrichmentBarPlot.d.ts.map +1 -1
  19. package/dist-tsc/FeatureSetEnrichmentBarPlot.js +7 -6
  20. package/dist-tsc/FeatureSetEnrichmentBarPlotSubscriber.d.ts.map +1 -1
  21. package/dist-tsc/FeatureSetEnrichmentBarPlotSubscriber.js +5 -2
  22. package/dist-tsc/TreemapSubscriber.d.ts.map +1 -1
  23. package/dist-tsc/TreemapSubscriber.js +10 -6
  24. package/dist-tsc/expr-hooks.d.ts.map +1 -1
  25. package/dist-tsc/expr-hooks.test.js +2 -1
  26. package/package.json +7 -7
  27. package/src/CellSetCompositionBarPlot.js +35 -9
  28. package/src/CellSetExpressionPlotOptions.js +39 -2
  29. package/src/CellSetExpressionPlotSubscriber.js +47 -11
  30. package/src/DotPlot.js +4 -2
  31. package/src/FeatureSetEnrichmentBarPlot.js +7 -6
  32. package/src/FeatureSetEnrichmentBarPlotSubscriber.js +5 -2
  33. package/src/TreemapSubscriber.js +20 -7
  34. package/src/expr-hooks.js +0 -1
  35. package/src/expr-hooks.test.js +2 -1
@@ -3,6 +3,10 @@ import { useId } from 'react-aria';
3
3
  import { TableCell, TableRow, TextField, Slider } from '@material-ui/core';
4
4
  import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
5
5
  import { GLSL_COLORMAPS } from '@vitessce/gl';
6
+ import { capitalize } from '@vitessce/utils';
7
+
8
+
9
+ const FEATURE_AGGREGATION_STRATEGIES = ['first', 'last', 'sum', 'mean'];
6
10
 
7
11
  export default function CellSetExpressionPlotOptions(props) {
8
12
  const {
@@ -15,6 +19,8 @@ export default function CellSetExpressionPlotOptions(props) {
15
19
  setFeatureValuePositivityThreshold,
16
20
  featureValueColormap,
17
21
  setFeatureValueColormap,
22
+ featureAggregationStrategy,
23
+ setFeatureAggregationStrategy,
18
24
  } = props;
19
25
 
20
26
  const cellSetExpressionPlotOptionsId = useId();
@@ -25,9 +31,13 @@ export default function CellSetExpressionPlotOptions(props) {
25
31
  setFeatureValueColormap(event.target.value);
26
32
  }
27
33
 
28
- const handleTransformChange = (event) => {
34
+ function handleTransformChange(event) {
29
35
  setFeatureValueTransform(event.target.value === '' ? null : event.target.value);
30
- };
36
+ }
37
+
38
+ function handleFeatureAggregationStrategyChange(event) {
39
+ setFeatureAggregationStrategy(event.target.value);
40
+ }
31
41
 
32
42
  function handlePositivityThresholdChange(event, value) {
33
43
  setFeatureValuePositivityThreshold(value);
@@ -119,6 +129,33 @@ export default function CellSetExpressionPlotOptions(props) {
119
129
  />
120
130
  </TableCell>
121
131
  </TableRow>
132
+ {setFeatureAggregationStrategy ? (
133
+ <TableRow>
134
+ <TableCell className={classes.labelCell} variant="head" scope="row">
135
+ <label
136
+ htmlFor={`feature-aggregation-strategy-${cellSetExpressionPlotOptionsId}`}
137
+ >
138
+ Feature Aggregation Strategy
139
+ </label>
140
+ </TableCell>
141
+ <TableCell className={classes.inputCell} variant="body">
142
+ <OptionSelect
143
+ className={classes.select}
144
+ value={featureAggregationStrategy ?? 'first'}
145
+ onChange={handleFeatureAggregationStrategyChange}
146
+ inputProps={{
147
+ id: `feature-aggregation-strategy-${cellSetExpressionPlotOptionsId}`,
148
+ }}
149
+ >
150
+ {FEATURE_AGGREGATION_STRATEGIES.map(opt => (
151
+ <option key={opt} value={opt}>
152
+ {capitalize(opt)}
153
+ </option>
154
+ ))}
155
+ </OptionSelect>
156
+ </TableCell>
157
+ </TableRow>
158
+ ) : null}
122
159
  {setFeatureValuePositivityThreshold ? (
123
160
  <TableRow key="transform-coefficient-option-row">
124
161
  <TableCell className={classes.labelCell}>
@@ -26,6 +26,28 @@ import {
26
26
  histogramStratifiedExpressionData,
27
27
  } from './expr-hooks.js';
28
28
 
29
+ const DEFAULT_FEATURE_AGGREGATION_STRATEGY = 'first';
30
+
31
+ function featureSummary(geneSelection, featureAggregationStrategy) {
32
+ if (featureAggregationStrategy === 'first') {
33
+ return geneSelection?.[0];
34
+ } if (featureAggregationStrategy === 'last') {
35
+ return geneSelection?.at(-1);
36
+ } if (typeof featureAggregationStrategy === 'number') {
37
+ const i = featureAggregationStrategy;
38
+ return geneSelection?.[i];
39
+ } if (featureAggregationStrategy === 'sum') {
40
+ // TODO: make these .join()-ed labels more scalable,
41
+ // in particular, if more than 10 or so elements.
42
+ return geneSelection?.join(' + ');
43
+ } if (featureAggregationStrategy === 'mean') {
44
+ return `Mean of ${geneSelection?.join(', ')}`;
45
+ } if (featureAggregationStrategy === 'difference') {
46
+ return geneSelection?.join(' - ');
47
+ }
48
+ return '';
49
+ }
50
+
29
51
  /**
30
52
  * Get expression data for the cells
31
53
  * in the selected cell sets.
@@ -50,7 +72,7 @@ function useExpressionByCellSet(
50
72
  expressionData, obsIndex, cellSets, additionalCellSets,
51
73
  geneSelection, cellSetSelection, cellSetColor,
52
74
  featureValueTransform, featureValueTransformCoefficient,
53
- theme, yMinProp,
75
+ theme, yMinProp, featureAggregationStrategy,
54
76
  ) {
55
77
  const mergedCellSets = useMemo(
56
78
  () => mergeObsSets(cellSets, additionalCellSets),
@@ -68,7 +90,7 @@ function useExpressionByCellSet(
68
90
  );
69
91
  if (stratifiedData) {
70
92
  const aggregateData = aggregateStratifiedExpressionData(
71
- stratifiedData, geneSelection,
93
+ stratifiedData, geneSelection, featureAggregationStrategy,
72
94
  );
73
95
  const summarizedData = summarizeStratifiedExpressionData(
74
96
  aggregateData, true,
@@ -83,6 +105,7 @@ function useExpressionByCellSet(
83
105
  mergedCellSets, cellSetSelection, cellSetColor,
84
106
  featureValueTransform, featureValueTransformCoefficient,
85
107
  yMinProp, sampleEdges, sampleSets, sampleSetSelection,
108
+ featureAggregationStrategy,
86
109
  ]);
87
110
 
88
111
  // From the cell sets hierarchy and the list of selected cell sets,
@@ -140,10 +163,12 @@ export function CellSetExpressionPlotSubscriber(props) {
140
163
  sampleType,
141
164
  sampleSetSelection,
142
165
  sampleSetColor,
166
+ featureAggregationStrategy,
143
167
  }, {
144
168
  setFeatureValueTransform,
145
169
  setFeatureValueTransformCoefficient,
146
170
  setSampleSetColor,
171
+ setFeatureAggregationStrategy,
147
172
  }] = useCoordination(
148
173
  COMPONENT_COORDINATION_TYPES[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION],
149
174
  coordinationScopes,
@@ -206,26 +231,35 @@ export function CellSetExpressionPlotSubscriber(props) {
206
231
  sampleEdgesUrls,
207
232
  ]);
208
233
 
234
+ const featureAggregationStrategyToUse = featureAggregationStrategy
235
+ ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
236
+
209
237
  const [histogramData, setArr, exprMax] = useExpressionByCellSet(
210
238
  sampleEdges, sampleSets, sampleSetSelection,
211
239
  expressionData, obsIndex, cellSets, additionalCellSets,
212
240
  geneSelection, cellSetSelection, cellSetColor,
213
241
  featureValueTransform, featureValueTransformCoefficient,
214
- theme, yMin,
242
+ theme, yMin, featureAggregationStrategyToUse,
215
243
  );
216
244
 
217
- const firstGeneSelected = geneSelection && geneSelection.length >= 1
218
- ? (
219
- featureLabelsMap?.get(geneSelection[0])
220
- || featureLabelsMap?.get(cleanFeatureId(geneSelection[0]))
221
- || geneSelection[0]
222
- )
223
- : null;
245
+ const featureSuffix = useMemo(() => {
246
+ const cleanedGeneSelection = geneSelection?.map(geneName => (
247
+ featureLabelsMap?.get(geneName)
248
+ || featureLabelsMap?.get(cleanFeatureId(geneName))
249
+ || geneName
250
+ ));
251
+ if (Array.isArray(cleanedGeneSelection)) {
252
+ return featureSummary(cleanedGeneSelection, featureAggregationStrategyToUse);
253
+ }
254
+ return null;
255
+ }, [geneSelection, featureAggregationStrategyToUse]);
256
+
257
+
224
258
  const selectedTransformName = transformOptions.find(
225
259
  o => o.value === featureValueTransform,
226
260
  )?.name;
227
261
  // Use empty string when firstGeneSelected is null
228
- const titleSuffix = firstGeneSelected ? ` (${firstGeneSelected})` : '';
262
+ const titleSuffix = featureSuffix ? ` (${featureSuffix})` : '';
229
263
 
230
264
  return (
231
265
  <TitleInfo
@@ -246,6 +280,8 @@ export function CellSetExpressionPlotSubscriber(props) {
246
280
  featureValueTransformCoefficient={featureValueTransformCoefficient}
247
281
  setFeatureValueTransformCoefficient={setFeatureValueTransformCoefficient}
248
282
  transformOptions={transformOptions}
283
+ featureAggregationStrategy={featureAggregationStrategy}
284
+ setFeatureAggregationStrategy={setFeatureAggregationStrategy}
249
285
  />
250
286
  )}
251
287
  >
package/src/DotPlot.js CHANGED
@@ -39,7 +39,8 @@ export default function DotPlot(props) {
39
39
  featureType,
40
40
  featureValueType,
41
41
  featureValueTransformName,
42
- featureValueColormap,
42
+ // TODO: re-enable featureValueColormap coordination
43
+ // featureValueColormap,
43
44
  obsSetSelection,
44
45
  obsSetColor,
45
46
  } = props;
@@ -134,7 +135,8 @@ export default function DotPlot(props) {
134
135
  type: 'quantitative',
135
136
  title: meanTransform,
136
137
  scale: {
137
- scheme: featureValueColormap,
138
+ // scheme: featureValueColormap,
139
+ scheme: 'greys',
138
140
  },
139
141
  legend: {
140
142
  direction: 'horizontal',
@@ -14,7 +14,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
14
14
  theme,
15
15
  width,
16
16
  height,
17
- marginRight = 200,
17
+ marginRight = 300,
18
18
  marginBottom = 120,
19
19
  keyLength = 36,
20
20
  featureType,
@@ -86,7 +86,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
86
86
  return [...a, h];
87
87
  }, []);
88
88
 
89
- const MAX_ROWS = 25;
89
+ const MAX_ROWS = 50;
90
90
  result = result.slice(0, MAX_ROWS);
91
91
  return result;
92
92
  }
@@ -115,7 +115,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
115
115
  select: {
116
116
  type: 'point',
117
117
  on: 'click[event.shiftKey === false]',
118
- fields: ['name'],
118
+ fields: ['name', 'term'],
119
119
  empty: 'none',
120
120
  },
121
121
  },
@@ -124,7 +124,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
124
124
  select: {
125
125
  type: 'point',
126
126
  on: 'click[event.shiftKey]',
127
- fields: ['name'],
127
+ fields: ['name', 'term'],
128
128
  empty: 'none',
129
129
  },
130
130
  },
@@ -173,9 +173,10 @@ export default function FeatureSetEnrichmentBarPlot(props) {
173
173
 
174
174
  const handleSignal = (name, value) => {
175
175
  if (name === 'bar_select') {
176
- onBarSelect(value.obsSetPath);
176
+ onBarSelect(value.name?.[0], value.term?.[0]);
177
177
  } else if (name === 'shift_bar_select') {
178
- onBarSelect(value.obsSetPath, true);
178
+ // Name and term may be arrays
179
+ onBarSelect(value.name, value.term, true);
179
180
  }
180
181
  };
181
182
 
@@ -119,8 +119,11 @@ export function FeatureSetEnrichmentBarPlotSubscriber(props) {
119
119
  // Will not work since transformFeature currently:
120
120
  // - matches based on kgId (rather than term)
121
121
  // - only knows about Reactome pathways (not GO terms).
122
- // console.log(await transformFeature(kgNode, targetFeatureType))
123
- // setFeatureSelection(featureIds);
122
+ const targetsInPathway = await transformFeature(kgNode, targetFeatureType);
123
+ const featureIds = targetsInPathway
124
+ .filter((d, i) => i < 10) // TODO: do not limit the number of genes here
125
+ .map(d => d.label);
126
+ setFeatureSelection(featureIds);
124
127
  }, [setFeatureSelection]);
125
128
 
126
129
  // TODO: support the following options
@@ -141,9 +141,6 @@ export function TreemapSubscriber(props) {
141
141
  [sampleSets],
142
142
  );
143
143
 
144
- const obsCount = obsIndex?.length || 0;
145
- const sampleCount = sampleIndex?.length || 0;
146
-
147
144
  // TODO: use obsFilter / sampleFilter to display
148
145
  // _all_ cells/samples in gray / transparent in background,
149
146
  // and use obsSetSelection/sampleSetSelection to display
@@ -182,7 +179,7 @@ export function TreemapSubscriber(props) {
182
179
  sampleResult.set(sampleSetKey, sampleSetSize || 0);
183
180
  });
184
181
 
185
- if (mergedObsSets && obsSetSelection) {
182
+ if (mergedObsSets && obsSetSelection && obsIndex) {
186
183
  const sampleIdToSetMap = sampleSets && sampleSetSelection
187
184
  ? treeToSelectedSetMap(sampleSets, sampleSetSelection)
188
185
  : null;
@@ -213,10 +210,20 @@ export function TreemapSubscriber(props) {
213
210
  ];
214
211
  }, [obsIndex, sampleEdges, sampleSets, obsSetColor,
215
212
  sampleSetColor, mergedObsSets, obsSetSelection, mergedSampleSets,
216
- sampleSetSelection,
213
+ sampleSetSelection, obsIndex,
217
214
  // TODO: consider filtering-related coordination values
218
215
  ]);
219
216
 
217
+ const totalObsCount = obsIndex?.length || 0;
218
+ const totalSampleCount = sampleIndex?.length || 0;
219
+
220
+ const selectedObsCount = obsCounts.reduce((a, h) => a + h.value, 0);
221
+ const selectedSampleCount = sampleCounts.reduce((a, h) => a + h.value, 0);
222
+
223
+ const unselectedObsCount = totalObsCount - selectedObsCount;
224
+ const unselectedSampleCount = totalSampleCount - selectedSampleCount;
225
+
226
+
220
227
  const onNodeClick = useCallback((obsSetPath) => {
221
228
  setObsSetSelection([obsSetPath]);
222
229
  }, [setObsSetSelection]);
@@ -224,12 +231,13 @@ export function TreemapSubscriber(props) {
224
231
  return (
225
232
  <TitleInfo
226
233
  title={`Treemap of ${capitalize(plur(obsType, 2))}`}
227
- info={`${commaNumber(obsCount)} ${plur(obsType, obsCount)} from ${commaNumber(sampleCount)} ${plur(sampleType, sampleCount)}`}
234
+ info={`${commaNumber(selectedObsCount)} ${plur(obsType, selectedObsCount)} from ${commaNumber(selectedSampleCount)} ${plur(sampleType, selectedSampleCount)}`}
228
235
  removeGridComponent={removeGridComponent}
229
236
  urls={urls}
230
237
  theme={theme}
231
238
  isReady={isReady}
232
239
  helpText={helpText}
240
+ withPadding={false}
233
241
  options={(
234
242
  <TreemapOptions
235
243
  obsType={obsType}
@@ -254,7 +262,7 @@ export function TreemapSubscriber(props) {
254
262
  hierarchyLevels={hierarchyLevels || DEFAULT_HIERARCHY_LEVELS}
255
263
  theme={theme}
256
264
  width={width}
257
- height={height}
265
+ height={Math.max(height * (selectedObsCount / totalObsCount), 40)}
258
266
  obsType={obsType}
259
267
  sampleType={sampleType}
260
268
  obsSetColor={obsSetColor}
@@ -264,6 +272,11 @@ export function TreemapSubscriber(props) {
264
272
  onNodeClick={onNodeClick}
265
273
  />
266
274
  </div>
275
+ <div style={{ position: 'absolute', right: '2px', bottom: '2px', fontSize: '10px' }}>
276
+ {unselectedObsCount > 0 ? (
277
+ <span>{`${commaNumber(unselectedObsCount)} ${plur(obsType, unselectedObsCount)} from ${commaNumber(unselectedSampleCount)} ${plur(sampleType, unselectedSampleCount)} currently omitted`}</span>
278
+ ) : null}
279
+ </div>
267
280
  </TitleInfo>
268
281
  );
269
282
  }
package/src/expr-hooks.js CHANGED
@@ -92,7 +92,6 @@ export function summarizeStratifiedExpressionData(
92
92
  stratifiedResult, keepZeros,
93
93
  ) {
94
94
  const summarizedResult = new InternMap([], JSON.stringify);
95
-
96
95
  Array.from(stratifiedResult.entries()).forEach(([cellSetKey, firstLevelInternMap]) => {
97
96
  summarizedResult.set(cellSetKey, new InternMap([], JSON.stringify));
98
97
  Array.from(firstLevelInternMap.entries()).forEach(([sampleSetKey, secondLevelInternMap]) => {
@@ -78,6 +78,7 @@ describe('Utility functions for processing expression data for statistical plots
78
78
  ];
79
79
  const featureValueTransform = null;
80
80
  const featureValueTransformCoefficient = 1;
81
+ const featureAggregationStrategy = 'first';
81
82
 
82
83
  const [result] = stratifyExpressionData(
83
84
  sampleEdges, sampleSets, sampleSetSelection,
@@ -86,7 +87,7 @@ describe('Utility functions for processing expression data for statistical plots
86
87
  featureValueTransform, featureValueTransformCoefficient,
87
88
  );
88
89
  const aggregateData = aggregateStratifiedExpressionData(
89
- result, geneSelection,
90
+ result, geneSelection, featureAggregationStrategy,
90
91
  );
91
92
  const summaryResult = summarizeStratifiedExpressionData(aggregateData, true);
92
93