@vitessce/statistical-plots 3.5.9 → 3.5.10

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 (53) hide show
  1. package/dist/{deflate-19841f78.js → deflate-ad0dcbe4.js} +1 -1
  2. package/dist/{index-dc733355.js → index-b8398176.js} +33528 -15439
  3. package/dist/index.js +6 -5
  4. package/dist/{jpeg-a83077be.js → jpeg-81bd1053.js} +1 -1
  5. package/dist/{lerc-1edd075a.js → lerc-b15c3a4c.js} +1 -1
  6. package/dist/{lzw-9572eac3.js → lzw-503cb795.js} +1 -1
  7. package/dist/{packbits-cce11fbc.js → packbits-40cbad40.js} +1 -1
  8. package/dist/{raw-f7587aff.js → raw-9b8d9daf.js} +1 -1
  9. package/dist/{webimage-8d38cd8b.js → webimage-bbc59b4a.js} +1 -1
  10. package/dist-tsc/CellSetCompositionBarPlot.js +3 -3
  11. package/dist-tsc/CellSetCompositionBarPlotSubscriber.js +1 -1
  12. package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -1
  13. package/dist-tsc/CellSetExpressionPlot.js +26 -10
  14. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
  15. package/dist-tsc/CellSetExpressionPlotSubscriber.js +5 -2
  16. package/dist-tsc/DotPlot.d.ts.map +1 -1
  17. package/dist-tsc/DotPlot.js +54 -5
  18. package/dist-tsc/DotPlotSubscriber.d.ts.map +1 -1
  19. package/dist-tsc/DotPlotSubscriber.js +1 -1
  20. package/dist-tsc/FeatureStatsTable.d.ts +2 -0
  21. package/dist-tsc/FeatureStatsTable.d.ts.map +1 -0
  22. package/dist-tsc/FeatureStatsTable.js +81 -0
  23. package/dist-tsc/FeatureStatsTableSubscriber.d.ts +2 -0
  24. package/dist-tsc/FeatureStatsTableSubscriber.d.ts.map +1 -0
  25. package/dist-tsc/FeatureStatsTableSubscriber.js +28 -0
  26. package/dist-tsc/Treemap.d.ts.map +1 -1
  27. package/dist-tsc/Treemap.js +17 -3
  28. package/dist-tsc/TreemapSubscriber.d.ts.map +1 -1
  29. package/dist-tsc/TreemapSubscriber.js +5 -3
  30. package/dist-tsc/VolcanoPlot.d.ts.map +1 -1
  31. package/dist-tsc/VolcanoPlot.js +15 -46
  32. package/dist-tsc/VolcanoPlotSubscriber.d.ts.map +1 -1
  33. package/dist-tsc/VolcanoPlotSubscriber.js +4 -2
  34. package/dist-tsc/index.d.ts +1 -0
  35. package/dist-tsc/index.js +1 -0
  36. package/dist-tsc/utils.d.ts +1 -0
  37. package/dist-tsc/utils.d.ts.map +1 -1
  38. package/dist-tsc/utils.js +56 -0
  39. package/package.json +8 -7
  40. package/src/CellSetCompositionBarPlot.js +3 -3
  41. package/src/CellSetCompositionBarPlotSubscriber.js +1 -1
  42. package/src/CellSetExpressionPlot.js +33 -10
  43. package/src/CellSetExpressionPlotSubscriber.js +10 -3
  44. package/src/DotPlot.js +77 -9
  45. package/src/DotPlotSubscriber.js +3 -1
  46. package/src/FeatureStatsTable.js +116 -0
  47. package/src/FeatureStatsTableSubscriber.js +133 -0
  48. package/src/Treemap.js +21 -3
  49. package/src/TreemapSubscriber.js +6 -4
  50. package/src/VolcanoPlot.js +16 -64
  51. package/src/VolcanoPlotSubscriber.js +6 -1
  52. package/src/index.js +1 -0
  53. package/src/utils.js +82 -1
package/dist/index.js CHANGED
@@ -1,19 +1,20 @@
1
- import { b, e, C, d, a, h, D, f, E, F, c, T, V } from "./index-dc733355.js";
1
+ import { b, f, C, e, a, i, D, h, E, F, c, d, T, V } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
5
5
  export {
6
6
  b as CellSetCompositionBarPlotSubscriber,
7
- e as CellSetExpressionPlot,
7
+ f as CellSetExpressionPlot,
8
8
  C as CellSetExpressionPlotSubscriber,
9
- d as CellSetSizesPlot,
9
+ e as CellSetSizesPlot,
10
10
  a as CellSetSizesPlotSubscriber,
11
- h as DotPlot,
11
+ i as DotPlot,
12
12
  D as DotPlotSubscriber,
13
- f as ExpressionHistogram,
13
+ h as ExpressionHistogram,
14
14
  E as ExpressionHistogramSubscriber,
15
15
  F as FeatureBarPlotSubscriber,
16
16
  c as FeatureSetEnrichmentBarPlotSubscriber,
17
+ d as FeatureStatsTableSubscriber,
17
18
  T as TreemapSubscriber,
18
19
  V as VolcanoPlotSubscriber
19
20
  };
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-dc733355.js";
1
+ import { B as BaseDecoder } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
2
- import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-dc733355.js";
2
+ import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-b8398176.js";
3
3
  import "react";
4
4
  import "@vitessce/vit-s";
5
5
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-dc733355.js";
1
+ import { B as BaseDecoder } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-dc733355.js";
1
+ import { B as BaseDecoder } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-dc733355.js";
1
+ import { B as BaseDecoder } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-dc733355.js";
1
+ import { B as BaseDecoder } from "./index-b8398176.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -69,12 +69,12 @@ export default function CellSetCompositionBarPlot(props) {
69
69
  obsSetColorScale, sampleSetColorScale,
70
70
  ]);
71
71
  // Get an array of keys for sorting purposes.
72
- const keys = computedData.map(d => d.keyName);
72
+ const keys = computedData?.map(d => d.keyName);
73
73
  const colorScale = {
74
74
  // Manually set the color scale so that Vega-Lite does
75
75
  // not choose the colors automatically.
76
- domain: computedData.map(d => d.key),
77
- range: computedData.map(d => d.color),
76
+ domain: computedData?.map(d => d.key),
77
+ range: computedData?.map(d => d.color),
78
78
  };
79
79
  const captializedObsType = capitalize(obsType);
80
80
  const opacityScale = {
@@ -36,5 +36,5 @@ export function CellSetCompositionBarPlotSubscriber(props) {
36
36
  // TODO: support the following options
37
37
  // - Use logFoldChange vs. intercept+effect for the bar y-value.
38
38
  // - Boolean flag to allow hiding non-significant bars.
39
- return (_jsx(TitleInfo, { title: `${capitalize(obsType)} Set Composition Analysis Plot`, removeGridComponent: removeGridComponent, theme: theme, isReady: isReady, helpText: helpText, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: obsSetStats ? (_jsx(CellSetCompositionBarPlot, { theme: theme, width: width, height: height, obsType: obsType, obsSetsColumnNameMapping: obsSetsColumnNameMapping, obsSetsColumnNameMappingReversed: obsSetsColumnNameMappingReversed, sampleSetsColumnNameMapping: sampleSetsColumnNameMapping, sampleSetsColumnNameMappingReversed: sampleSetsColumnNameMappingReversed, sampleSetSelection: sampleSetSelection, obsSetSelection: obsSetSelection, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, data: obsSetStats, onBarSelect: onBarSelect })) : (_jsxs("span", { children: ["Select at least one ", obsType, " set."] })) }) }));
39
+ return (_jsx(TitleInfo, { title: `${capitalize(obsType)} Set Composition Analysis Plot`, removeGridComponent: removeGridComponent, theme: theme, isReady: isReady, helpText: helpText, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: obsSetStats ? (_jsx(CellSetCompositionBarPlot, { theme: theme, width: width, height: height, obsType: obsType, obsSetsColumnNameMapping: obsSetsColumnNameMapping, obsSetsColumnNameMappingReversed: obsSetsColumnNameMappingReversed, sampleSetsColumnNameMapping: sampleSetsColumnNameMapping, sampleSetsColumnNameMappingReversed: sampleSetsColumnNameMappingReversed, sampleSetSelection: sampleSetSelection, obsSetSelection: obsSetSelection, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, data: obsSetStats, onBarSelect: onBarSelect })) : (_jsxs("span", { children: ["Select at least one ", obsType, " set and a pair of ", sampleType, " sets."] })) }) }));
40
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CellSetExpressionPlot.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlot.js"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qDAjBG;IAAwB,IAAI,EAApB,MAAM,EAAE;IAEM,SAAS,EAAvB,MAAM;IACU,MAAM,EAAtB,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,eA0XA"}
1
+ {"version":3,"file":"CellSetExpressionPlot.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlot.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qDAjBG;IAAwB,IAAI,EAApB,MAAM,EAAE;IAEM,SAAS,EAAvB,MAAM;IACU,MAAM,EAAtB,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,eAgZA"}
@@ -9,6 +9,7 @@ import { area as d3_area, curveBasis } from 'd3-shape';
9
9
  import { select } from 'd3-selection';
10
10
  import { colorArrayToString } from '@vitessce/sets-utils';
11
11
  import { capitalize } from '@vitessce/utils';
12
+ import { getColorScale } from './utils.js';
12
13
  const scaleBand = vega_scale('band');
13
14
  /**
14
15
  * Gene expression histogram displayed as a bar chart,
@@ -32,20 +33,21 @@ const scaleBand = vega_scale('band');
32
33
  * for the feature value transformation function.
33
34
  */
34
35
  export default function CellSetExpressionPlot(props) {
35
- const { yMin: yMinProp, yUnits, jitter, cellSetSelection, sampleSetSelection, sampleSetColor, colors, data, theme, width, height, marginTop = 5, marginRight = 5, marginLeft = 50, marginBottom, obsType, featureType, featureValueType, featureValueTransformName, } = props;
36
+ const { yMin: yMinProp, xAxisTitle = null, yUnits, jitter, obsSetSelection, obsSetColor, sampleSetSelection, sampleSetColor, colors, data, theme, width, height, marginTop = 5, marginRight = 5, marginLeft = 50, marginBottom, obsType, featureType, featureValueType, featureValueTransformName, } = props;
36
37
  const svgRef = useRef();
38
+ const obsSetColorScale = useMemo(() => getColorScale(obsSetSelection, obsSetColor, theme), [obsSetSelection, obsSetColor, theme]);
37
39
  // Get the max characters in an axis label for autsizing the bottom margin.
38
40
  const maxCharactersForLabel = useMemo(() => {
39
- if (!cellSetSelection) {
41
+ if (!obsSetSelection) {
40
42
  return 0;
41
43
  }
42
- const cellSetNames = cellSetSelection.map(d => d.at(-1));
44
+ const cellSetNames = obsSetSelection.map(d => d.at(-1));
43
45
  return cellSetNames.reduce((acc, name) => {
44
46
  // eslint-disable-next-line no-param-reassign
45
47
  acc = acc === undefined || name.length > acc ? name.length : acc;
46
48
  return acc;
47
49
  }, 0);
48
- }, [cellSetSelection]);
50
+ }, [obsSetSelection]);
49
51
  const isStratified = (Array.isArray(sampleSetSelection) && sampleSetSelection.length === 2);
50
52
  useEffect(() => {
51
53
  const domElement = svgRef.current;
@@ -54,7 +56,7 @@ export default function CellSetExpressionPlot(props) {
54
56
  : '';
55
57
  const unitSuffix = yUnits ? ` (${yUnits})` : '';
56
58
  const yTitle = `${transformPrefix}${capitalize(featureValueType)}${unitSuffix}`;
57
- const xTitle = `${capitalize(obsType)} Set`;
59
+ const xTitle = xAxisTitle ?? `${capitalize(obsType)} Set`;
58
60
  // Use a square-root term because the angle of the labels is 45 degrees (see below)
59
61
  // so the perpendicular distance to the bottom of the labels is proportional to the
60
62
  // square root of the length of the labels along the imaginary hypotenuse.
@@ -102,7 +104,7 @@ export default function CellSetExpressionPlot(props) {
102
104
  const innerHeight = height - autoMarginBottom;
103
105
  const xGroup = scaleBand()
104
106
  .range([marginLeft, width - marginRight])
105
- .domain(cellSetSelection)
107
+ .domain(obsSetSelection)
106
108
  .padding(0.1);
107
109
  // For the y domain, use the yMin prop
108
110
  // to support a use case such as 'Aspect Ratio',
@@ -256,17 +258,30 @@ export default function CellSetExpressionPlot(props) {
256
258
  .selectAll('text')
257
259
  .style('font-size', '11px');
258
260
  // X-axis ticks
259
- g
261
+ const xTickG = g
260
262
  .append('g')
261
263
  .attr('transform', `translate(0,${innerHeight})`)
262
- .style('font-size', '14px')
263
- .call(axisBottom(xGroup).tickFormat(d => d.at(-1)))
264
+ .style('font-size', '14px');
265
+ xTickG.call(axisBottom(xGroup).tickFormat(d => d.at(-1)))
264
266
  .selectAll('text')
265
267
  .style('font-size', '11px')
266
268
  .attr('dx', '-6px')
267
269
  .attr('dy', '6px')
268
270
  .attr('transform', 'rotate(-45)')
269
271
  .style('text-anchor', 'end');
272
+ if (isStratified) {
273
+ // Associate each X tick with a cell type color,
274
+ // since in the stratified case the violins are colored
275
+ // by sample set.
276
+ const tickWidth = xGroup.bandwidth();
277
+ xTickG.selectAll('.tick')
278
+ .append('rect')
279
+ .attr('x', -tickWidth / 2)
280
+ // .attr("y", -innerHeight)
281
+ .attr('width', tickWidth)
282
+ .attr('height', 4)
283
+ .style('fill', d => obsSetColorScale(d));
284
+ }
270
285
  // Y-axis title
271
286
  g
272
287
  .append('text')
@@ -333,7 +348,8 @@ export default function CellSetExpressionPlot(props) {
333
348
  }, [width, height, data, marginLeft, marginBottom, colors,
334
349
  jitter, theme, yMinProp, marginTop, marginRight, featureType,
335
350
  featureValueType, featureValueTransformName, yUnits, obsType,
336
- maxCharactersForLabel, sampleSetSelection,
351
+ maxCharactersForLabel, sampleSetSelection, isStratified,
352
+ obsSetColorScale,
337
353
  ]);
338
354
  return (_jsx("svg", { ref: svgRef, style: {
339
355
  top: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"CellSetExpressionPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlotSubscriber.js"],"names":[],"mappings":"AAmGA;;;;;;;;;GASG;AACH,uDALG;IAAwB,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;CAChB,eAoKA"}
1
+ {"version":3,"file":"CellSetExpressionPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlotSubscriber.js"],"names":[],"mappings":"AAmGA;;;;;;;;;GASG;AACH,uDALG;IAAwB,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;CAChB,eA2KA"}
@@ -63,7 +63,7 @@ function useExpressionByCellSet(sampleEdges, sampleSets, sampleSetSelection, exp
63
63
  * @param {string} props.theme The name of the current Vitessce theme.
64
64
  */
65
65
  export function CellSetExpressionPlotSubscriber(props) {
66
- const { coordinationScopes, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, jitter = false, yMin = null, yUnits = null, helpText = ViewHelpMapping.OBS_SET_FEATURE_VALUE_DISTRIBUTION, } = props;
66
+ const { coordinationScopes, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, title, xAxisTitle, jitter = false, yMin = null, yUnits = null, helpText = ViewHelpMapping.OBS_SET_FEATURE_VALUE_DISTRIBUTION, } = props;
67
67
  const classes = useStyles();
68
68
  const loaders = useLoaders();
69
69
  // Get "props" from the coordination space.
@@ -104,5 +104,8 @@ export function CellSetExpressionPlotSubscriber(props) {
104
104
  || geneSelection[0])
105
105
  : null;
106
106
  const selectedTransformName = transformOptions.find(o => o.value === featureValueTransform)?.name;
107
- return (_jsx(TitleInfo, { title: `Expression by ${capitalize(obsType)} Set${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(CellSetExpressionPlotOptions, { featureValueTransform: featureValueTransform, setFeatureValueTransform: setFeatureValueTransform, featureValueTransformCoefficient: featureValueTransformCoefficient, setFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, transformOptions: transformOptions })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: histogramData ? (_jsx(CellSetExpressionPlot, { yMin: yMin, yUnits: yUnits, jitter: jitter, cellSetSelection: cellSetSelection, sampleSetSelection: sampleSetSelection, sampleSetColor: sampleSetColor, colors: setArr, data: histogramData, exprMax: exprMax, theme: theme, width: width, height: height, obsType: obsType, featureType: featureType, featureValueType: featureValueType, featureValueTransformName: selectedTransformName })) : (_jsxs("span", { children: ["Select a ", featureType, "."] })) }) }));
107
+ // Use empty string when firstGeneSelected is null
108
+ const titleSuffix = firstGeneSelected ? ` (${firstGeneSelected})` : '';
109
+ return (_jsx(TitleInfo, { title: title ? `${title}${titleSuffix}`
110
+ : `Expression by ${capitalize(obsType)} Set${titleSuffix}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(CellSetExpressionPlotOptions, { featureValueTransform: featureValueTransform, setFeatureValueTransform: setFeatureValueTransform, featureValueTransformCoefficient: featureValueTransformCoefficient, setFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, transformOptions: transformOptions })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: histogramData ? (_jsx(CellSetExpressionPlot, { yMin: yMin, yUnits: yUnits, jitter: jitter, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, sampleSetSelection: sampleSetSelection, sampleSetColor: sampleSetColor, colors: setArr, data: histogramData, exprMax: exprMax, theme: theme, width: width, height: height, obsType: obsType, featureType: featureType, featureValueType: featureValueType, featureValueTransformName: selectedTransformName, xAxisTitle: xAxisTitle })) : (_jsxs("span", { children: ["Select a ", featureType, "."] })) }) }));
108
111
  }
@@ -1 +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"}
1
+ {"version":3,"file":"DotPlot.d.ts","sourceRoot":"","sources":["../src/DotPlot.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;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,eA0NA"}
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import React from 'react';
2
+ import React, { useRef, useEffect, useCallback, useState, useMemo } from 'react';
3
3
  import { clamp } from 'lodash-es';
4
4
  import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
5
5
  import { capitalize, pluralize as plur } from '@vitessce/utils';
6
+ import { select } from 'd3-selection';
7
+ import { getColorScale } from './utils.js';
6
8
  /**
7
9
  * Gene expression dot plot,
8
10
  * implemented with the VegaPlot component.
@@ -22,7 +24,8 @@ import { capitalize, pluralize as plur } from '@vitessce/utils';
22
24
  * for the feature value transformation function.
23
25
  */
24
26
  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;
27
+ const { isStratified, transpose, data: rawData, theme, width, height, marginRight, marginBottom, obsType, sampleType, keyLength = 36, featureType, featureValueType, featureValueTransformName, featureValueColormap, obsSetSelection, obsSetColor, } = props;
28
+ const vegaContainerRef = useRef();
26
29
  // Add a property `keyGroup` and `keyFeature` which concatenates the key and the name,
27
30
  // which is both unique and can easily be converted
28
31
  // back to the name by taking a substring.
@@ -59,11 +62,11 @@ export default function DotPlot(props) {
59
62
  const autoMarginForSampleSet = marginRight
60
63
  || 30 + Math.sqrt(maxCharactersForSampleSet / 2) * 30;
61
64
  const plotWidth = transpose
62
- ? clamp(width - autoMarginForFeature - 180, 10, Infinity) / (cellSetSelection?.length || 1)
65
+ ? clamp(width - autoMarginForFeature - 180, 10, Infinity) / (obsSetSelection?.length || 1)
63
66
  : clamp(width - autoMarginForGroup - autoMarginForSampleSet - 200, 10, Infinity);
64
67
  const plotHeight = transpose
65
68
  ? clamp((height - autoMarginForGroup - autoMarginForSampleSet - 50), 10, Infinity)
66
- : clamp((height - autoMarginForFeature - 80), 10, Infinity) / (cellSetSelection?.length || 1);
69
+ : clamp((height - autoMarginForFeature - 80), 10, Infinity) / (obsSetSelection?.length || 1);
67
70
  // Get an array of keys for sorting purposes.
68
71
  const groupKeys = data.map(d => d.keyGroup);
69
72
  const featureKeys = data.map(d => d.keyFeature);
@@ -125,6 +128,11 @@ export default function DotPlot(props) {
125
128
  legend: {
126
129
  symbolFillColor: 'white',
127
130
  },
131
+ scale: { domain: [0, 100] },
132
+ },
133
+ tooltip: {
134
+ field: 'pctPosInGroup',
135
+ type: 'quantitative',
128
136
  },
129
137
  },
130
138
  width: plotWidth,
@@ -140,5 +148,46 @@ export default function DotPlot(props) {
140
148
  } : {}),
141
149
  },
142
150
  };
143
- return (_jsx(VegaPlot, { data: data, spec: spec }));
151
+ const getTooltipText = useCallback(item => ({
152
+ [`${capitalize(featureType)}`]: item.datum.feature,
153
+ [`${capitalize(obsType)} Set`]: item.datum.group,
154
+ ...(isStratified
155
+ ? ({ [`${capitalize(sampleType)} Set`]: item.datum.secondaryGroup })
156
+ : {}),
157
+ [`Percentage of ${plur(obsType, 2)} in set`]: item.datum.pctPosInGroup,
158
+ [meanTransform.join(' ')]: item.datum.meanExpInGroup,
159
+ }), [featureType, obsType, featureValueType, featureValueTransformName]);
160
+ const obsSetColorScale = useMemo(() => getColorScale(obsSetSelection, obsSetColor, theme), [obsSetSelection, obsSetColor, theme]);
161
+ const [vegaRenderIncrement, setVegaRenderIncrement] = useState(0);
162
+ useEffect(() => {
163
+ // If the dot plot is stratified by both obsSet and sampleSet,
164
+ // then we want to add cell set colors.
165
+ // TODO: do we also want to add these color bars
166
+ // when only stratified by obsSet?
167
+ const domElement = vegaContainerRef.current;
168
+ // Here, we assume that the Vega SVG renderer is being used.
169
+ const svg = select(domElement)
170
+ .select('svg');
171
+ // We use the following CSS selector to identify all of
172
+ // the <line> elements that we are interested to modify.
173
+ const tickEls = svg.selectAll('g.root g.column_footer g.role-axis g.role-axis-domain line');
174
+ tickEls
175
+ .attr('stroke-width', 5)
176
+ .attr('dy', 2.5)
177
+ .attr('stroke', (d, i) => {
178
+ const obsSetPath = obsSetSelection?.[i];
179
+ return obsSetColorScale(obsSetPath);
180
+ });
181
+ }, [vegaContainerRef, vegaRenderIncrement, obsSetSelection, obsSetColorScale]);
182
+ // We want to increment the counter whenever we detect that VegaPlot
183
+ // has re-rendered.
184
+ const onNewView = useCallback(() => {
185
+ setVegaRenderIncrement(prev => prev + 1);
186
+ }, []);
187
+ // This is kind of hacky, since it is possible that the useEffect runs prior
188
+ // to the Vega rendering, but in practice it seems to work.
189
+ useEffect(() => {
190
+ setVegaRenderIncrement(prev => prev + 1);
191
+ }, [rawData]);
192
+ return (_jsx("div", { ref: vegaContainerRef, children: _jsx(VegaPlot, { data: data, spec: spec, onNewView: onNewView, getTooltipText: getTooltipText, renderer: "svg" }) }));
144
193
  }
@@ -1 +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,eAoJA"}
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,eAsJA"}
@@ -50,5 +50,5 @@ export function DotPlotSubscriber(props) {
50
50
  ]);
51
51
  const [resultArr, meanExpressionMax] = useExpressionSummaries(sampleEdges, sampleSets, sampleSetSelection, expressionData, obsIndex, cellSets, additionalCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient, posThreshold, featureLabelsMap);
52
52
  const selectedTransformName = transformOptions.find(o => o.value === featureValueTransform)?.name;
53
- return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, 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, "."] })) }) }));
53
+ return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, 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, sampleType: sampleType, featureType: featureType, featureValueType: featureValueType, featureValueTransformName: selectedTransformName, featureValueColormap: featureValueColormap, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor })) : (_jsxs("span", { children: ["Select at least one ", featureType, "."] })) }) }));
54
54
  }
@@ -0,0 +1,2 @@
1
+ export default function FeatureStatsTable(props: any): JSX.Element;
2
+ //# sourceMappingURL=FeatureStatsTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeatureStatsTable.d.ts","sourceRoot":"","sources":["../src/FeatureStatsTable.js"],"names":[],"mappings":"AAWA,mEAwGC"}
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useMemo, useState, useCallback } from 'react';
3
+ import { DataGrid } from '@mui/x-data-grid';
4
+ import { capitalize } from '@vitessce/utils';
5
+ import { useFilteredVolcanoData } from './utils.js';
6
+ const ROW_ID_DELIMITER = '___';
7
+ const INITIAL_SORT_MODEL = [
8
+ // We initially set the sorting this way
9
+ { field: 'logFoldChange', sort: 'desc' },
10
+ ];
11
+ export default function FeatureStatsTable(props) {
12
+ const { obsType, featureType, obsSetsColumnNameMappingReversed, sampleSetsColumnNameMappingReversed, sampleSetSelection, data, setFeatureSelection, featurePointSignificanceThreshold, featurePointFoldChangeThreshold, } = props;
13
+ const [
14
+ // eslint-disable-next-line no-unused-vars
15
+ computedData, filteredData,] = useFilteredVolcanoData({
16
+ data,
17
+ obsSetsColumnNameMappingReversed,
18
+ sampleSetsColumnNameMappingReversed,
19
+ featurePointFoldChangeThreshold,
20
+ featurePointSignificanceThreshold,
21
+ sampleSetSelection,
22
+ });
23
+ // Reference: https://v4.mui.com/api/data-grid/data-grid/
24
+ const columns = useMemo(() => ([
25
+ {
26
+ field: 'featureId',
27
+ headerName: capitalize(featureType),
28
+ width: 200,
29
+ editable: false,
30
+ },
31
+ {
32
+ field: 'logFoldChange',
33
+ headerName: 'Log Fold-Change',
34
+ width: 200,
35
+ editable: false,
36
+ },
37
+ {
38
+ field: 'featureSignificance',
39
+ headerName: 'P-value',
40
+ width: 200,
41
+ editable: false,
42
+ },
43
+ {
44
+ field: 'obsSetName',
45
+ headerName: `${capitalize(obsType)} Set`,
46
+ width: 200,
47
+ editable: false,
48
+ },
49
+ ]), [obsType, featureType]);
50
+ const rows = useMemo(() => {
51
+ let result = [];
52
+ if (filteredData) {
53
+ filteredData.forEach((comparisonObject) => {
54
+ const { df, metadata } = comparisonObject;
55
+ const coordinationValues = metadata.coordination_values;
56
+ const rawObsSetPath = coordinationValues.obsSetFilter
57
+ ? coordinationValues.obsSetFilter[0]
58
+ : coordinationValues.obsSetSelection[0];
59
+ const obsSetPath = [...rawObsSetPath];
60
+ obsSetPath[0] = obsSetsColumnNameMappingReversed[rawObsSetPath[0]];
61
+ const obsSetName = obsSetPath.at(-1);
62
+ result = result.concat(df.map(row => ({
63
+ ...row,
64
+ id: `${row.featureId}${ROW_ID_DELIMITER}${obsSetName}`,
65
+ obsSetName,
66
+ })));
67
+ });
68
+ }
69
+ return result;
70
+ }, [filteredData, obsSetsColumnNameMappingReversed]);
71
+ const onSelectionModelChange = useCallback((rowIds) => {
72
+ const featureIds = rowIds.map(rowId => rowId.split(ROW_ID_DELIMITER)[0]);
73
+ setFeatureSelection(featureIds);
74
+ }, []);
75
+ const rowSelectionModel = useMemo(() => [], []);
76
+ const [sortModel, setSortModel] = useState(INITIAL_SORT_MODEL);
77
+ const getRowId = useCallback(row => row.id, []);
78
+ return (_jsx(DataGrid, { density: "compact", rows: rows, columns: columns, pageSize: 10,
79
+ // checkboxSelection // TODO: uncomment to enable multiple-row selection
80
+ onSelectionModelChange: onSelectionModelChange, rowSelectionModel: rowSelectionModel, getRowId: getRowId, sortModel: sortModel, onSortModelChange: setSortModel }));
81
+ }
@@ -0,0 +1,2 @@
1
+ export function FeatureStatsTableSubscriber(props: any): JSX.Element;
2
+ //# sourceMappingURL=FeatureStatsTableSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeatureStatsTableSubscriber.d.ts","sourceRoot":"","sources":["../src/FeatureStatsTableSubscriber.js"],"names":[],"mappings":"AAoBA,qEAgHC"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable no-unused-vars */
3
+ import React from 'react';
4
+ import { TitleInfo, useCoordination, useLoaders, useReady, useFeatureStatsData, useMatchingLoader, useColumnNameMapping, } from '@vitessce/vit-s';
5
+ import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping, DataType, } from '@vitessce/constants-internal';
6
+ import FeatureStatsTable from './FeatureStatsTable.js';
7
+ import { useRawSetPaths } from './utils.js';
8
+ export function FeatureStatsTableSubscriber(props) {
9
+ const { title = 'Differential Expression Results', coordinationScopes, removeGridComponent, theme, helpText = ViewHelpMapping.FEATURE_STATS_TABLE, } = props;
10
+ const loaders = useLoaders();
11
+ // Get "props" from the coordination space.
12
+ const [{ dataset, obsType, sampleType, featureType, featureValueType, obsFilter: cellFilter, obsHighlight: cellHighlight, obsSetSelection, obsSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, featurePointSignificanceThreshold, featurePointFoldChangeThreshold, featureValueTransform, featureValueTransformCoefficient, gatingFeatureSelectionX, gatingFeatureSelectionY, featureSelection, sampleSetSelection, sampleSetColor, }, { setObsFilter: setCellFilter, setObsSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setFeaturePointSignificanceThreshold, setFeaturePointFoldChangeThreshold, setFeatureValueTransform, setFeatureValueTransformCoefficient, setGatingFeatureSelectionX, setGatingFeatureSelectionY, setFeatureSelection, setSampleSetSelection, setSampleSetColor, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.FEATURE_STATS_TABLE], coordinationScopes);
13
+ const obsSetsLoader = useMatchingLoader(loaders, dataset, DataType.OBS_SETS, { obsType });
14
+ const sampleSetsLoader = useMatchingLoader(loaders, dataset, DataType.SAMPLE_SETS, { sampleType });
15
+ const obsSetsColumnNameMapping = useColumnNameMapping(obsSetsLoader);
16
+ const obsSetsColumnNameMappingReversed = useColumnNameMapping(obsSetsLoader, true);
17
+ const sampleSetsColumnNameMapping = useColumnNameMapping(sampleSetsLoader);
18
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
19
+ const rawSampleSetSelection = useRawSetPaths(sampleSetsColumnNameMapping, sampleSetSelection);
20
+ const rawObsSetSelection = useRawSetPaths(obsSetsColumnNameMapping, obsSetSelection);
21
+ const [{ featureStats }, featureStatsStatus] = useFeatureStatsData(loaders, dataset, false, { obsType, featureType, sampleType },
22
+ // These volcanoOptions are passed to FeatureStatsAnndataLoader.loadMulti():
23
+ { sampleSetSelection: rawSampleSetSelection, obsSetSelection: rawObsSetSelection });
24
+ const isReady = useReady([
25
+ featureStatsStatus,
26
+ ]);
27
+ return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, theme: theme, isReady: isReady, helpText: helpText, withPadding: false, children: featureStats ? (_jsx(FeatureStatsTable, { theme: theme, obsType: obsType, featureType: featureType, obsSetsColumnNameMapping: obsSetsColumnNameMapping, obsSetsColumnNameMappingReversed: obsSetsColumnNameMappingReversed, sampleSetsColumnNameMapping: sampleSetsColumnNameMapping, sampleSetsColumnNameMappingReversed: sampleSetsColumnNameMappingReversed, sampleSetSelection: sampleSetSelection, obsSetSelection: obsSetSelection, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, data: featureStats, featureSelection: featureSelection, setFeatureSelection: setFeatureSelection, featurePointSignificanceThreshold: featurePointSignificanceThreshold, featurePointFoldChangeThreshold: featurePointFoldChangeThreshold })) : (_jsxs("p", { style: { padding: '12px' }, children: ["Select at least one ", obsType, " set."] })) }));
28
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Treemap.d.ts","sourceRoot":"","sources":["../src/Treemap.js"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,yDAkLC"}
1
+ {"version":3,"file":"Treemap.d.ts","sourceRoot":"","sources":["../src/Treemap.js"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,yDAoMC"}
@@ -71,6 +71,9 @@ export default function Treemap(props) {
71
71
  const svgRef = useRef();
72
72
  useEffect(() => {
73
73
  const domElement = svgRef.current;
74
+ if (!width || !height) {
75
+ return;
76
+ }
74
77
  const svg = select(domElement);
75
78
  svg.selectAll('g').remove();
76
79
  svg
@@ -78,7 +81,7 @@ export default function Treemap(props) {
78
81
  .attr('height', height)
79
82
  .attr('viewBox', [0, 0, width, height])
80
83
  .attr('style', 'font: 10px sans-serif');
81
- if (!treemapLeaves || !obsSetSelection || !sampleSetSelection) {
84
+ if (!treemapLeaves || !obsSetSelection) {
82
85
  return;
83
86
  }
84
87
  // Add a group for each leaf of the hierarchy.
@@ -130,14 +133,25 @@ export default function Treemap(props) {
130
133
  })
131
134
  .append('use')
132
135
  .attr('xlink:href', d => d.leafUid.href);
136
+ const hasSampleSetSelection = Array.isArray(sampleSetSelection);
133
137
  // Append multiline text.
134
138
  leaf.append('text')
135
139
  .attr('clip-path', d => `url(${d.clipUid.href})`)
136
140
  .selectAll('tspan')
137
141
  .data(d => ([
138
142
  // Each element in this array corresponds to a line of text.
139
- d.data?.[0]?.at(-1),
140
- d.parent?.data?.[0]?.at(-1),
143
+ ...(hasSampleSetSelection
144
+ ? ([
145
+ d.data?.[0]?.at(-1),
146
+ d.parent?.data?.[0]?.at(-1),
147
+ ]) : ([
148
+ // Only use the cell set name
149
+ // for the line of text
150
+ // (since no sample set selection)
151
+ hierarchyLevels[0] === 'obsSet'
152
+ ? d.parent?.data?.[0].at(-1)
153
+ : d.data?.[0].at(-1),
154
+ ])),
141
155
  `${d.data?.[1].toLocaleString()} ${plur(obsType, d.data?.[1])}`,
142
156
  ]))
143
157
  .join('tspan')
@@ -1 +1 @@
1
- {"version":3,"file":"TreemapSubscriber.d.ts","sourceRoot":"","sources":["../src/TreemapSubscriber.js"],"names":[],"mappings":"AAyBA,2DAiPC"}
1
+ {"version":3,"file":"TreemapSubscriber.d.ts","sourceRoot":"","sources":["../src/TreemapSubscriber.js"],"names":[],"mappings":"AAyBA,2DAmPC"}
@@ -66,9 +66,9 @@ export function TreemapSubscriber(props) {
66
66
  obsResult.get(cellSetKey).set(sampleSetKey, 0);
67
67
  });
68
68
  });
69
- const sampleSetSizes = treeToSetSizesBySetNames(mergedSampleSets, sampleSetSelection, sampleSetSelection, sampleSetColor, theme);
69
+ const sampleSetSizes = hasSampleSetSelection ? treeToSetSizesBySetNames(mergedSampleSets, sampleSetSelection, sampleSetSelection, sampleSetColor, theme) : null;
70
70
  sampleSetKeys.forEach((sampleSetKey) => {
71
- const sampleSetSize = sampleSetSizes.find(d => isEqual(d.setNamePath, sampleSetKey))?.size;
71
+ const sampleSetSize = sampleSetSizes?.find(d => isEqual(d.setNamePath, sampleSetKey))?.size;
72
72
  sampleResult.set(sampleSetKey, sampleSetSize || 0);
73
73
  });
74
74
  if (mergedObsSets && obsSetSelection) {
@@ -80,7 +80,9 @@ export function TreemapSubscriber(props) {
80
80
  const obsId = obsIndex[i];
81
81
  const cellSet = cellIdToSetMap?.get(obsId);
82
82
  const sampleId = sampleEdges?.get(obsId);
83
- const sampleSet = sampleId ? sampleIdToSetMap?.get(sampleId) : null;
83
+ const sampleSet = sampleId && hasSampleSetSelection
84
+ ? sampleIdToSetMap?.get(sampleId)
85
+ : null;
84
86
  if (hasSampleSetSelection && !sampleSet) {
85
87
  // Skip this sample if it is not in the selected sample set.
86
88
  // eslint-disable-next-line no-continue
@@ -1 +1 @@
1
- {"version":3,"file":"VolcanoPlot.d.ts","sourceRoot":"","sources":["../src/VolcanoPlot.js"],"names":[],"mappings":"AAYA,6DA+SC"}
1
+ {"version":3,"file":"VolcanoPlot.d.ts","sourceRoot":"","sources":["../src/VolcanoPlot.js"],"names":[],"mappings":"AAWA,6DAgQC"}