@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.
- package/dist/{deflate-19841f78.js → deflate-ad0dcbe4.js} +1 -1
- package/dist/{index-dc733355.js → index-b8398176.js} +33528 -15439
- package/dist/index.js +6 -5
- package/dist/{jpeg-a83077be.js → jpeg-81bd1053.js} +1 -1
- package/dist/{lerc-1edd075a.js → lerc-b15c3a4c.js} +1 -1
- package/dist/{lzw-9572eac3.js → lzw-503cb795.js} +1 -1
- package/dist/{packbits-cce11fbc.js → packbits-40cbad40.js} +1 -1
- package/dist/{raw-f7587aff.js → raw-9b8d9daf.js} +1 -1
- package/dist/{webimage-8d38cd8b.js → webimage-bbc59b4a.js} +1 -1
- package/dist-tsc/CellSetCompositionBarPlot.js +3 -3
- package/dist-tsc/CellSetCompositionBarPlotSubscriber.js +1 -1
- package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlot.js +26 -10
- package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlotSubscriber.js +5 -2
- package/dist-tsc/DotPlot.d.ts.map +1 -1
- package/dist-tsc/DotPlot.js +54 -5
- package/dist-tsc/DotPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/DotPlotSubscriber.js +1 -1
- package/dist-tsc/FeatureStatsTable.d.ts +2 -0
- package/dist-tsc/FeatureStatsTable.d.ts.map +1 -0
- package/dist-tsc/FeatureStatsTable.js +81 -0
- package/dist-tsc/FeatureStatsTableSubscriber.d.ts +2 -0
- package/dist-tsc/FeatureStatsTableSubscriber.d.ts.map +1 -0
- package/dist-tsc/FeatureStatsTableSubscriber.js +28 -0
- package/dist-tsc/Treemap.d.ts.map +1 -1
- package/dist-tsc/Treemap.js +17 -3
- package/dist-tsc/TreemapSubscriber.d.ts.map +1 -1
- package/dist-tsc/TreemapSubscriber.js +5 -3
- package/dist-tsc/VolcanoPlot.d.ts.map +1 -1
- package/dist-tsc/VolcanoPlot.js +15 -46
- package/dist-tsc/VolcanoPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/VolcanoPlotSubscriber.js +4 -2
- package/dist-tsc/index.d.ts +1 -0
- package/dist-tsc/index.js +1 -0
- package/dist-tsc/utils.d.ts +1 -0
- package/dist-tsc/utils.d.ts.map +1 -1
- package/dist-tsc/utils.js +56 -0
- package/package.json +8 -7
- package/src/CellSetCompositionBarPlot.js +3 -3
- package/src/CellSetCompositionBarPlotSubscriber.js +1 -1
- package/src/CellSetExpressionPlot.js +33 -10
- package/src/CellSetExpressionPlotSubscriber.js +10 -3
- package/src/DotPlot.js +77 -9
- package/src/DotPlotSubscriber.js +3 -1
- package/src/FeatureStatsTable.js +116 -0
- package/src/FeatureStatsTableSubscriber.js +133 -0
- package/src/Treemap.js +21 -3
- package/src/TreemapSubscriber.js +6 -4
- package/src/VolcanoPlot.js +16 -64
- package/src/VolcanoPlotSubscriber.js +6 -1
- package/src/index.js +1 -0
- package/src/utils.js +82 -1
package/dist-tsc/VolcanoPlot.js
CHANGED
@@ -6,21 +6,20 @@ import { scaleLinear } from 'd3-scale';
|
|
6
6
|
import { axisBottom, axisLeft } from 'd3-axis';
|
7
7
|
import { extent as d3_extent } from 'd3-array';
|
8
8
|
import { select } from 'd3-selection';
|
9
|
-
import { isEqual } from 'lodash-es';
|
10
9
|
import { capitalize, getDefaultForegroundColor } from '@vitessce/utils';
|
11
10
|
import { colorArrayToString } from '@vitessce/sets-utils';
|
12
|
-
import { getColorScale } from './utils.js';
|
11
|
+
import { getColorScale, useFilteredVolcanoData } from './utils.js';
|
13
12
|
export default function VolcanoPlot(props) {
|
14
|
-
const { theme, width, height, obsType, featureType,
|
13
|
+
const { theme, width, height, obsType, featureType, obsSetsColumnNameMappingReversed, sampleSetsColumnNameMappingReversed, sampleSetSelection, obsSetSelection, obsSetColor, sampleSetColor, data, marginTop = 5, marginRight = 5, marginLeft = 50, marginBottom = 50, onFeatureClick, featurePointSignificanceThreshold, featurePointFoldChangeThreshold, featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold, } = props;
|
15
14
|
const svgRef = useRef();
|
16
|
-
const computedData =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
})
|
15
|
+
const [computedData, filteredData] = useFilteredVolcanoData({
|
16
|
+
data,
|
17
|
+
obsSetsColumnNameMappingReversed,
|
18
|
+
sampleSetsColumnNameMappingReversed,
|
19
|
+
featurePointFoldChangeThreshold,
|
20
|
+
featurePointSignificanceThreshold,
|
21
|
+
sampleSetSelection,
|
22
|
+
});
|
24
23
|
const [xExtent, yExtent] = useMemo(() => {
|
25
24
|
if (!computedData) {
|
26
25
|
return [null, null];
|
@@ -44,7 +43,7 @@ export default function VolcanoPlot(props) {
|
|
44
43
|
.attr('height', height)
|
45
44
|
.attr('viewBox', [0, 0, width, height])
|
46
45
|
.attr('style', 'font: 10px sans-serif');
|
47
|
-
if (!
|
46
|
+
if (!filteredData || !xExtent || !yExtent) {
|
48
47
|
return;
|
49
48
|
}
|
50
49
|
// Render scatterplot
|
@@ -89,13 +88,6 @@ export default function VolcanoPlot(props) {
|
|
89
88
|
.text('log2 fold-change')
|
90
89
|
.style('font-size', '12px')
|
91
90
|
.style('fill', fgColor);
|
92
|
-
// Get a mapping from column name to group name.
|
93
|
-
const obsSetsColumnNameMappingReversed = Object.fromEntries(Object
|
94
|
-
.entries(obsSetsColumnNameMapping)
|
95
|
-
.map(([key, value]) => ([value, key])));
|
96
|
-
const sampleSetsColumnNameMappingReversed = Object.fromEntries(Object
|
97
|
-
.entries(sampleSetsColumnNameMapping)
|
98
|
-
.map(([key, value]) => ([value, key])));
|
99
91
|
// Horizontal and vertical rules to indicate currently-selected thresholds
|
100
92
|
// Vertical lines
|
101
93
|
const ruleColor = 'silver';
|
@@ -150,39 +142,15 @@ export default function VolcanoPlot(props) {
|
|
150
142
|
.style('fill', fgColor);
|
151
143
|
const g = svg.append('g');
|
152
144
|
// Append a circle for each data point.
|
153
|
-
|
145
|
+
filteredData.forEach((comparisonObject) => {
|
154
146
|
const obsSetG = g.append('g');
|
155
|
-
const { df, metadata } = comparisonObject;
|
147
|
+
const { df: filteredDf, metadata } = comparisonObject;
|
156
148
|
const coordinationValues = metadata.coordination_values;
|
157
149
|
const rawObsSetPath = coordinationValues.obsSetFilter
|
158
150
|
? coordinationValues.obsSetFilter[0]
|
159
151
|
: coordinationValues.obsSetSelection[0];
|
160
152
|
const obsSetPath = [...rawObsSetPath];
|
161
153
|
obsSetPath[0] = obsSetsColumnNameMappingReversed[rawObsSetPath[0]];
|
162
|
-
// Swap the foldchange direction if backwards with
|
163
|
-
// respect to the current sampleSetSelection pair.
|
164
|
-
// TODO: move this swapping into the computedData useMemo?
|
165
|
-
let shouldSwapFoldChangeDirection = false;
|
166
|
-
if (coordinationValues.sampleSetFilter
|
167
|
-
&& coordinationValues.sampleSetFilter.length === 2) {
|
168
|
-
const rawSampleSetPathA = coordinationValues.sampleSetFilter[0];
|
169
|
-
const sampleSetPathA = [...rawSampleSetPathA];
|
170
|
-
sampleSetPathA[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathA[0]];
|
171
|
-
const rawSampleSetPathB = coordinationValues.sampleSetFilter[1];
|
172
|
-
const sampleSetPathB = [...rawSampleSetPathB];
|
173
|
-
sampleSetPathB[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathB[0]];
|
174
|
-
if (isEqual(sampleSetPathA, sampleSetSelection[1])
|
175
|
-
&& isEqual(sampleSetPathB, sampleSetSelection[0])) {
|
176
|
-
shouldSwapFoldChangeDirection = true;
|
177
|
-
}
|
178
|
-
}
|
179
|
-
const filteredDf = df.featureId.map((featureId, i) => ({
|
180
|
-
featureId,
|
181
|
-
logFoldChange: df.logFoldChange[i] * (shouldSwapFoldChangeDirection ? -1 : 1),
|
182
|
-
featureSignificance: df.featureSignificance[i],
|
183
|
-
minusLog10p: df.minusLog10p[i],
|
184
|
-
})).filter(d => ((Math.abs(d.logFoldChange) >= (featurePointFoldChangeThreshold ?? 1.0))
|
185
|
-
&& (d.featureSignificance <= (featurePointSignificanceThreshold ?? 0.05))));
|
186
154
|
const color = obsSetColorScale(obsSetPath);
|
187
155
|
obsSetG.append('g')
|
188
156
|
.selectAll('circle')
|
@@ -214,12 +182,13 @@ export default function VolcanoPlot(props) {
|
|
214
182
|
.text(d => `${featureType}: ${d.featureId}\nin ${obsSetPath?.at(-1)}\nlog2 fold-change: ${d.logFoldChange}\np-value: ${d.featureSignificance}`);
|
215
183
|
});
|
216
184
|
}, [width, height, theme, sampleSetColor, sampleSetSelection,
|
217
|
-
obsSetSelection, obsSetColor, featureType,
|
185
|
+
obsSetSelection, obsSetColor, featureType, filteredData,
|
218
186
|
xExtent, yExtent, obsType,
|
219
187
|
marginLeft, marginBottom, marginTop, marginRight,
|
220
188
|
obsSetColorScale, sampleSetColorScale, onFeatureClick,
|
221
189
|
featurePointSignificanceThreshold, featurePointFoldChangeThreshold,
|
222
190
|
featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold,
|
191
|
+
obsSetsColumnNameMappingReversed,
|
223
192
|
]);
|
224
193
|
return (_jsx("svg", { ref: svgRef, style: {
|
225
194
|
top: 0,
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"VolcanoPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/VolcanoPlotSubscriber.js"],"names":[],"mappings":"AAuBA,+
|
1
|
+
{"version":3,"file":"VolcanoPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/VolcanoPlotSubscriber.js"],"names":[],"mappings":"AAuBA,+DA+IC"}
|
@@ -8,7 +8,7 @@ import { useStyles } from './styles.js';
|
|
8
8
|
import VolcanoPlotOptions from './VolcanoPlotOptions.js';
|
9
9
|
import { useRawSetPaths } from './utils.js';
|
10
10
|
export function VolcanoPlotSubscriber(props) {
|
11
|
-
const { coordinationScopes, removeGridComponent, theme, helpText = ViewHelpMapping.VOLCANO_PLOT, } = props;
|
11
|
+
const { title = 'Volcano Plot', coordinationScopes, removeGridComponent, theme, helpText = ViewHelpMapping.VOLCANO_PLOT, } = props;
|
12
12
|
const classes = useStyles();
|
13
13
|
const loaders = useLoaders();
|
14
14
|
// Get "props" from the coordination space.
|
@@ -17,7 +17,9 @@ export function VolcanoPlotSubscriber(props) {
|
|
17
17
|
const obsSetsLoader = useMatchingLoader(loaders, dataset, DataType.OBS_SETS, { obsType });
|
18
18
|
const sampleSetsLoader = useMatchingLoader(loaders, dataset, DataType.SAMPLE_SETS, { sampleType });
|
19
19
|
const obsSetsColumnNameMapping = useColumnNameMapping(obsSetsLoader);
|
20
|
+
const obsSetsColumnNameMappingReversed = useColumnNameMapping(obsSetsLoader, true);
|
20
21
|
const sampleSetsColumnNameMapping = useColumnNameMapping(sampleSetsLoader);
|
22
|
+
const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
|
21
23
|
const rawSampleSetSelection = useRawSetPaths(sampleSetsColumnNameMapping, sampleSetSelection);
|
22
24
|
const rawObsSetSelection = useRawSetPaths(obsSetsColumnNameMapping, obsSetSelection);
|
23
25
|
const [{ featureStats }, featureStatsStatus] = useFeatureStatsData(loaders, dataset, false, { obsType, featureType, sampleType },
|
@@ -29,5 +31,5 @@ export function VolcanoPlotSubscriber(props) {
|
|
29
31
|
const onFeatureClick = useCallback((featureId) => {
|
30
32
|
setFeatureSelection([featureId]);
|
31
33
|
}, [setFeatureSelection]);
|
32
|
-
return (_jsx(TitleInfo, { title:
|
34
|
+
return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(VolcanoPlotOptions, { obsType: obsType, featureType: featureType, featurePointSignificanceThreshold: featurePointSignificanceThreshold, featurePointFoldChangeThreshold: featurePointFoldChangeThreshold, featureLabelSignificanceThreshold: featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold: featureLabelFoldChangeThreshold, setFeaturePointSignificanceThreshold: setFeaturePointSignificanceThreshold, setFeaturePointFoldChangeThreshold: setFeaturePointFoldChangeThreshold, setFeatureLabelSignificanceThreshold: setFeatureLabelSignificanceThreshold, setFeatureLabelFoldChangeThreshold: setFeatureLabelFoldChangeThreshold })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: featureStats ? (_jsx(VolcanoPlot, { theme: theme, width: width, height: height, obsType: obsType, featureType: featureType, obsSetsColumnNameMapping: obsSetsColumnNameMapping, obsSetsColumnNameMappingReversed: obsSetsColumnNameMappingReversed, sampleSetsColumnNameMapping: sampleSetsColumnNameMapping, sampleSetsColumnNameMappingReversed: sampleSetsColumnNameMappingReversed, sampleSetSelection: sampleSetSelection, obsSetSelection: obsSetSelection, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, data: featureStats, onFeatureClick: onFeatureClick, featurePointSignificanceThreshold: featurePointSignificanceThreshold, featurePointFoldChangeThreshold: featurePointFoldChangeThreshold, featureLabelSignificanceThreshold: featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold: featureLabelFoldChangeThreshold })) : (_jsxs("span", { children: ["Select at least one ", obsType, " set."] })) }) }));
|
33
35
|
}
|
package/dist-tsc/index.d.ts
CHANGED
@@ -7,6 +7,7 @@ export { TreemapSubscriber } from "./TreemapSubscriber.js";
|
|
7
7
|
export { VolcanoPlotSubscriber } from "./VolcanoPlotSubscriber.js";
|
8
8
|
export { CellSetCompositionBarPlotSubscriber } from "./CellSetCompositionBarPlotSubscriber.js";
|
9
9
|
export { FeatureSetEnrichmentBarPlotSubscriber } from "./FeatureSetEnrichmentBarPlotSubscriber.js";
|
10
|
+
export { FeatureStatsTableSubscriber } from "./FeatureStatsTableSubscriber.js";
|
10
11
|
export { default as CellSetSizesPlot } from "./CellSetSizesPlot.js";
|
11
12
|
export { default as CellSetExpressionPlot } from "./CellSetExpressionPlot.js";
|
12
13
|
export { default as ExpressionHistogram } from "./ExpressionHistogram.js";
|
package/dist-tsc/index.js
CHANGED
@@ -7,6 +7,7 @@ export { TreemapSubscriber } from './TreemapSubscriber.js';
|
|
7
7
|
export { VolcanoPlotSubscriber } from './VolcanoPlotSubscriber.js';
|
8
8
|
export { CellSetCompositionBarPlotSubscriber } from './CellSetCompositionBarPlotSubscriber.js';
|
9
9
|
export { FeatureSetEnrichmentBarPlotSubscriber } from './FeatureSetEnrichmentBarPlotSubscriber.js';
|
10
|
+
export { FeatureStatsTableSubscriber } from './FeatureStatsTableSubscriber.js';
|
10
11
|
export { default as CellSetSizesPlot } from './CellSetSizesPlot.js';
|
11
12
|
export { default as CellSetExpressionPlot } from './CellSetExpressionPlot.js';
|
12
13
|
export { default as ExpressionHistogram } from './ExpressionHistogram.js';
|
package/dist-tsc/utils.d.ts
CHANGED
@@ -6,4 +6,5 @@ export function getColorScale(setSelectionArr: any, setColorArr: any, theme: any
|
|
6
6
|
* @returns {string[][]} Transformed set paths.
|
7
7
|
*/
|
8
8
|
export function useRawSetPaths(columnNameMapping: Record<string, string>, setPaths: string[][]): string[][];
|
9
|
+
export function useFilteredVolcanoData(props: any): any[];
|
9
10
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist-tsc/utils.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAcA,0GAeC;AAGD;;;;;GAKG;AACH,kDAJW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YACtB,MAAM,EAAE,EAAE,GACR,MAAM,EAAE,EAAE,CAUtB"}
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAcA,0GAeC;AAGD;;;;;GAKG;AACH,kDAJW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YACtB,MAAM,EAAE,EAAE,GACR,MAAM,EAAE,EAAE,CAUtB;AAID,0DA6EC"}
|
package/dist-tsc/utils.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
1
2
|
import { useMemo } from 'react';
|
2
3
|
import { isEqual } from 'lodash-es';
|
3
4
|
import { colorArrayToString } from '@vitessce/sets-utils';
|
@@ -38,3 +39,58 @@ export function useRawSetPaths(columnNameMapping, setPaths) {
|
|
38
39
|
return newSetPath;
|
39
40
|
}), [columnNameMapping, setPaths]);
|
40
41
|
}
|
42
|
+
// Data transformation hook function that is used both here
|
43
|
+
// and in the FeatureStatsTable view.
|
44
|
+
export function useFilteredVolcanoData(props) {
|
45
|
+
const { data, obsSetsColumnNameMappingReversed, sampleSetsColumnNameMappingReversed, featurePointFoldChangeThreshold, featurePointSignificanceThreshold, sampleSetSelection, } = props;
|
46
|
+
const computedData = useMemo(() => data.map((d) => {
|
47
|
+
const { metadata } = d;
|
48
|
+
const coordinationValues = metadata.coordination_values;
|
49
|
+
const rawObsSetPath = coordinationValues.obsSetFilter
|
50
|
+
? coordinationValues.obsSetFilter[0]
|
51
|
+
: coordinationValues.obsSetSelection[0];
|
52
|
+
const obsSetPath = [...rawObsSetPath];
|
53
|
+
obsSetPath[0] = obsSetsColumnNameMappingReversed[rawObsSetPath[0]];
|
54
|
+
// Swap the foldchange direction if backwards with
|
55
|
+
// respect to the current sampleSetSelection pair.
|
56
|
+
// TODO: move this swapping into the computedData useMemo?
|
57
|
+
let shouldSwapFoldChangeDirection = false;
|
58
|
+
if (coordinationValues.sampleSetFilter
|
59
|
+
&& coordinationValues.sampleSetFilter.length === 2) {
|
60
|
+
const rawSampleSetPathA = coordinationValues.sampleSetFilter[0];
|
61
|
+
const sampleSetPathA = [...rawSampleSetPathA];
|
62
|
+
sampleSetPathA[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathA[0]];
|
63
|
+
const rawSampleSetPathB = coordinationValues.sampleSetFilter[1];
|
64
|
+
const sampleSetPathB = [...rawSampleSetPathB];
|
65
|
+
sampleSetPathB[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathB[0]];
|
66
|
+
if (isEqual(sampleSetPathA, sampleSetSelection[1])
|
67
|
+
&& isEqual(sampleSetPathB, sampleSetSelection[0])) {
|
68
|
+
shouldSwapFoldChangeDirection = true;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
return ({
|
72
|
+
...d,
|
73
|
+
df: {
|
74
|
+
...d.df,
|
75
|
+
minusLog10p: d.df.featureSignificance.map(v => -Math.log10(v)),
|
76
|
+
logFoldChange: d.df.featureFoldChange.map(v => (Math.log2(v) * (shouldSwapFoldChangeDirection ? -1 : 1))),
|
77
|
+
},
|
78
|
+
});
|
79
|
+
}), [
|
80
|
+
data, obsSetsColumnNameMappingReversed, sampleSetsColumnNameMappingReversed,
|
81
|
+
sampleSetSelection,
|
82
|
+
]);
|
83
|
+
const filteredData = useMemo(() => computedData.map(obj => ({
|
84
|
+
...obj,
|
85
|
+
// Instead of an object of one array per column,
|
86
|
+
// this is now an array of one object per row.
|
87
|
+
df: obj.df.featureId.map((featureId, i) => ({
|
88
|
+
featureId,
|
89
|
+
logFoldChange: obj.df.logFoldChange[i],
|
90
|
+
featureSignificance: obj.df.featureSignificance[i],
|
91
|
+
minusLog10p: obj.df.minusLog10p[i],
|
92
|
+
})).filter(d => ((Math.abs(d.logFoldChange) >= (featurePointFoldChangeThreshold ?? 1.0))
|
93
|
+
&& (d.featureSignificance <= (featurePointSignificanceThreshold ?? 0.05)))),
|
94
|
+
})), [computedData, featurePointFoldChangeThreshold, featurePointSignificanceThreshold]);
|
95
|
+
return [computedData, filteredData];
|
96
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitessce/statistical-plots",
|
3
|
-
"version": "3.5.
|
3
|
+
"version": "3.5.10",
|
4
4
|
"author": "HIDIVE Lab at HMS",
|
5
5
|
"homepage": "http://vitessce.io",
|
6
6
|
"repository": {
|
@@ -17,6 +17,7 @@
|
|
17
17
|
],
|
18
18
|
"dependencies": {
|
19
19
|
"@material-ui/core": "~4.12.3",
|
20
|
+
"@mui/x-data-grid": "^4.0.2",
|
20
21
|
"d3-array": "^2.4.0",
|
21
22
|
"d3-scale": "^4.0.0",
|
22
23
|
"d3-shape": "^3.2.0",
|
@@ -29,12 +30,12 @@
|
|
29
30
|
"react-aria": "^3.28.0",
|
30
31
|
"internmap": "^2.0.3",
|
31
32
|
"uuid": "^9.0.0",
|
32
|
-
"@vitessce/constants-internal": "3.5.
|
33
|
-
"@vitessce/sets-utils": "3.5.
|
34
|
-
"@vitessce/utils": "3.5.
|
35
|
-
"@vitessce/vega": "3.5.
|
36
|
-
"@vitessce/vit-s": "3.5.
|
37
|
-
"@vitessce/gl": "3.5.
|
33
|
+
"@vitessce/constants-internal": "3.5.10",
|
34
|
+
"@vitessce/sets-utils": "3.5.10",
|
35
|
+
"@vitessce/utils": "3.5.10",
|
36
|
+
"@vitessce/vega": "3.5.10",
|
37
|
+
"@vitessce/vit-s": "3.5.10",
|
38
|
+
"@vitessce/gl": "3.5.10"
|
38
39
|
},
|
39
40
|
"devDependencies": {
|
40
41
|
"react": "^18.0.0",
|
@@ -95,13 +95,13 @@ export default function CellSetCompositionBarPlot(props) {
|
|
95
95
|
]);
|
96
96
|
|
97
97
|
// Get an array of keys for sorting purposes.
|
98
|
-
const keys = computedData
|
98
|
+
const keys = computedData?.map(d => d.keyName);
|
99
99
|
|
100
100
|
const colorScale = {
|
101
101
|
// Manually set the color scale so that Vega-Lite does
|
102
102
|
// not choose the colors automatically.
|
103
|
-
domain: computedData
|
104
|
-
range: computedData
|
103
|
+
domain: computedData?.map(d => d.key),
|
104
|
+
range: computedData?.map(d => d.color),
|
105
105
|
};
|
106
106
|
const captializedObsType = capitalize(obsType);
|
107
107
|
|
@@ -143,7 +143,7 @@ export function CellSetCompositionBarPlotSubscriber(props) {
|
|
143
143
|
onBarSelect={onBarSelect}
|
144
144
|
/>
|
145
145
|
) : (
|
146
|
-
<span>Select at least one {obsType} set.</span>
|
146
|
+
<span>Select at least one {obsType} set and a pair of {sampleType} sets.</span>
|
147
147
|
)}
|
148
148
|
</div>
|
149
149
|
</TitleInfo>
|
@@ -8,6 +8,7 @@ import { area as d3_area, curveBasis } from 'd3-shape';
|
|
8
8
|
import { select } from 'd3-selection';
|
9
9
|
import { colorArrayToString } from '@vitessce/sets-utils';
|
10
10
|
import { capitalize } from '@vitessce/utils';
|
11
|
+
import { getColorScale } from './utils.js';
|
11
12
|
|
12
13
|
const scaleBand = vega_scale('band');
|
13
14
|
|
@@ -35,9 +36,11 @@ const scaleBand = vega_scale('band');
|
|
35
36
|
export default function CellSetExpressionPlot(props) {
|
36
37
|
const {
|
37
38
|
yMin: yMinProp,
|
39
|
+
xAxisTitle = null,
|
38
40
|
yUnits,
|
39
41
|
jitter,
|
40
|
-
|
42
|
+
obsSetSelection,
|
43
|
+
obsSetColor,
|
41
44
|
sampleSetSelection,
|
42
45
|
sampleSetColor,
|
43
46
|
colors,
|
@@ -57,18 +60,22 @@ export default function CellSetExpressionPlot(props) {
|
|
57
60
|
|
58
61
|
const svgRef = useRef();
|
59
62
|
|
63
|
+
const obsSetColorScale = useMemo(() => getColorScale(
|
64
|
+
obsSetSelection, obsSetColor, theme,
|
65
|
+
), [obsSetSelection, obsSetColor, theme]);
|
66
|
+
|
60
67
|
// Get the max characters in an axis label for autsizing the bottom margin.
|
61
68
|
const maxCharactersForLabel = useMemo(() => {
|
62
|
-
if (!
|
69
|
+
if (!obsSetSelection) {
|
63
70
|
return 0;
|
64
71
|
}
|
65
|
-
const cellSetNames =
|
72
|
+
const cellSetNames = obsSetSelection.map(d => d.at(-1));
|
66
73
|
return cellSetNames.reduce((acc, name) => {
|
67
74
|
// eslint-disable-next-line no-param-reassign
|
68
75
|
acc = acc === undefined || name.length > acc ? name.length : acc;
|
69
76
|
return acc;
|
70
77
|
}, 0);
|
71
|
-
}, [
|
78
|
+
}, [obsSetSelection]);
|
72
79
|
|
73
80
|
const isStratified = (Array.isArray(sampleSetSelection) && sampleSetSelection.length === 2);
|
74
81
|
|
@@ -81,7 +88,7 @@ export default function CellSetExpressionPlot(props) {
|
|
81
88
|
const unitSuffix = yUnits ? ` (${yUnits})` : '';
|
82
89
|
const yTitle = `${transformPrefix}${capitalize(featureValueType)}${unitSuffix}`;
|
83
90
|
|
84
|
-
const xTitle = `${capitalize(obsType)} Set`;
|
91
|
+
const xTitle = xAxisTitle ?? `${capitalize(obsType)} Set`;
|
85
92
|
|
86
93
|
// Use a square-root term because the angle of the labels is 45 degrees (see below)
|
87
94
|
// so the perpendicular distance to the bottom of the labels is proportional to the
|
@@ -146,7 +153,7 @@ export default function CellSetExpressionPlot(props) {
|
|
146
153
|
|
147
154
|
const xGroup = scaleBand()
|
148
155
|
.range([marginLeft, width - marginRight])
|
149
|
-
.domain(
|
156
|
+
.domain(obsSetSelection)
|
150
157
|
.padding(0.1);
|
151
158
|
|
152
159
|
|
@@ -314,11 +321,12 @@ export default function CellSetExpressionPlot(props) {
|
|
314
321
|
.style('font-size', '11px');
|
315
322
|
|
316
323
|
// X-axis ticks
|
317
|
-
g
|
324
|
+
const xTickG = g
|
318
325
|
.append('g')
|
319
326
|
.attr('transform', `translate(0,${innerHeight})`)
|
320
|
-
.style('font-size', '14px')
|
321
|
-
|
327
|
+
.style('font-size', '14px');
|
328
|
+
|
329
|
+
xTickG.call(axisBottom(xGroup).tickFormat(d => d.at(-1)))
|
322
330
|
.selectAll('text')
|
323
331
|
.style('font-size', '11px')
|
324
332
|
.attr('dx', '-6px')
|
@@ -326,6 +334,20 @@ export default function CellSetExpressionPlot(props) {
|
|
326
334
|
.attr('transform', 'rotate(-45)')
|
327
335
|
.style('text-anchor', 'end');
|
328
336
|
|
337
|
+
if (isStratified) {
|
338
|
+
// Associate each X tick with a cell type color,
|
339
|
+
// since in the stratified case the violins are colored
|
340
|
+
// by sample set.
|
341
|
+
const tickWidth = xGroup.bandwidth();
|
342
|
+
xTickG.selectAll('.tick')
|
343
|
+
.append('rect')
|
344
|
+
.attr('x', -tickWidth / 2)
|
345
|
+
// .attr("y", -innerHeight)
|
346
|
+
.attr('width', tickWidth)
|
347
|
+
.attr('height', 4)
|
348
|
+
.style('fill', d => obsSetColorScale(d));
|
349
|
+
}
|
350
|
+
|
329
351
|
// Y-axis title
|
330
352
|
g
|
331
353
|
.append('text')
|
@@ -394,7 +416,8 @@ export default function CellSetExpressionPlot(props) {
|
|
394
416
|
}, [width, height, data, marginLeft, marginBottom, colors,
|
395
417
|
jitter, theme, yMinProp, marginTop, marginRight, featureType,
|
396
418
|
featureValueType, featureValueTransformName, yUnits, obsType,
|
397
|
-
maxCharactersForLabel, sampleSetSelection,
|
419
|
+
maxCharactersForLabel, sampleSetSelection, isStratified,
|
420
|
+
obsSetColorScale,
|
398
421
|
]);
|
399
422
|
|
400
423
|
return (
|
@@ -114,6 +114,8 @@ export function CellSetExpressionPlotSubscriber(props) {
|
|
114
114
|
downloadButtonVisible,
|
115
115
|
removeGridComponent,
|
116
116
|
theme,
|
117
|
+
title,
|
118
|
+
xAxisTitle,
|
117
119
|
jitter = false,
|
118
120
|
yMin = null,
|
119
121
|
yUnits = null,
|
@@ -222,11 +224,14 @@ export function CellSetExpressionPlotSubscriber(props) {
|
|
222
224
|
const selectedTransformName = transformOptions.find(
|
223
225
|
o => o.value === featureValueTransform,
|
224
226
|
)?.name;
|
225
|
-
|
227
|
+
// Use empty string when firstGeneSelected is null
|
228
|
+
const titleSuffix = firstGeneSelected ? ` (${firstGeneSelected})` : '';
|
226
229
|
|
227
230
|
return (
|
228
231
|
<TitleInfo
|
229
|
-
title={
|
232
|
+
title={title ? `${title}${titleSuffix}`
|
233
|
+
: `Expression by ${capitalize(obsType)} Set${titleSuffix}`
|
234
|
+
}
|
230
235
|
closeButtonVisible={closeButtonVisible}
|
231
236
|
downloadButtonVisible={downloadButtonVisible}
|
232
237
|
removeGridComponent={removeGridComponent}
|
@@ -250,7 +255,8 @@ export function CellSetExpressionPlotSubscriber(props) {
|
|
250
255
|
yMin={yMin}
|
251
256
|
yUnits={yUnits}
|
252
257
|
jitter={jitter}
|
253
|
-
|
258
|
+
obsSetSelection={cellSetSelection}
|
259
|
+
obsSetColor={cellSetColor}
|
254
260
|
sampleSetSelection={sampleSetSelection}
|
255
261
|
sampleSetColor={sampleSetColor}
|
256
262
|
colors={setArr}
|
@@ -263,6 +269,7 @@ export function CellSetExpressionPlotSubscriber(props) {
|
|
263
269
|
featureType={featureType}
|
264
270
|
featureValueType={featureValueType}
|
265
271
|
featureValueTransformName={selectedTransformName}
|
272
|
+
xAxisTitle={xAxisTitle}
|
266
273
|
/>
|
267
274
|
) : (
|
268
275
|
<span>Select a {featureType}.</span>
|
package/src/DotPlot.js
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useRef, useEffect, useCallback, useState, useMemo } from 'react';
|
2
2
|
import { clamp } from 'lodash-es';
|
3
3
|
import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
|
4
4
|
import { capitalize, pluralize as plur } from '@vitessce/utils';
|
5
|
+
import { select } from 'd3-selection';
|
6
|
+
import { getColorScale } from './utils.js';
|
5
7
|
|
6
8
|
/**
|
7
9
|
* Gene expression dot plot,
|
@@ -32,14 +34,18 @@ export default function DotPlot(props) {
|
|
32
34
|
marginRight,
|
33
35
|
marginBottom,
|
34
36
|
obsType,
|
37
|
+
sampleType,
|
35
38
|
keyLength = 36,
|
36
39
|
featureType,
|
37
40
|
featureValueType,
|
38
41
|
featureValueTransformName,
|
39
42
|
featureValueColormap,
|
40
|
-
|
43
|
+
obsSetSelection,
|
44
|
+
obsSetColor,
|
41
45
|
} = props;
|
42
46
|
|
47
|
+
const vegaContainerRef = useRef();
|
48
|
+
|
43
49
|
// Add a property `keyGroup` and `keyFeature` which concatenates the key and the name,
|
44
50
|
// which is both unique and can easily be converted
|
45
51
|
// back to the name by taking a substring.
|
@@ -80,18 +86,17 @@ export default function DotPlot(props) {
|
|
80
86
|
|| 30 + Math.sqrt(maxCharactersForSampleSet / 2) * 30;
|
81
87
|
|
82
88
|
const plotWidth = transpose
|
83
|
-
? clamp(width - autoMarginForFeature - 180, 10, Infinity) / (
|
89
|
+
? clamp(width - autoMarginForFeature - 180, 10, Infinity) / (obsSetSelection?.length || 1)
|
84
90
|
: clamp(width - autoMarginForGroup - autoMarginForSampleSet - 200, 10, Infinity);
|
85
91
|
const plotHeight = transpose
|
86
92
|
? clamp((height - autoMarginForGroup - autoMarginForSampleSet - 50), 10, Infinity)
|
87
|
-
: clamp((height - autoMarginForFeature - 80), 10, Infinity) / (
|
93
|
+
: clamp((height - autoMarginForFeature - 80), 10, Infinity) / (obsSetSelection?.length || 1);
|
88
94
|
|
89
95
|
// Get an array of keys for sorting purposes.
|
90
96
|
const groupKeys = data.map(d => d.keyGroup);
|
91
97
|
const featureKeys = data.map(d => d.keyFeature);
|
92
98
|
const groupSecondaryKeys = data.map(d => d.keyGroupSecondary);
|
93
99
|
|
94
|
-
|
95
100
|
const meanTransform = (featureValueTransformName && featureValueTransformName !== 'None')
|
96
101
|
// Mean Log-Transformed Normalized Expression
|
97
102
|
? [`Mean ${featureValueTransformName}-transformed`, `normalized ${featureValueType}`, 'in set']
|
@@ -150,6 +155,11 @@ export default function DotPlot(props) {
|
|
150
155
|
legend: {
|
151
156
|
symbolFillColor: 'white',
|
152
157
|
},
|
158
|
+
scale: { domain: [0, 100] },
|
159
|
+
},
|
160
|
+
tooltip: {
|
161
|
+
field: 'pctPosInGroup',
|
162
|
+
type: 'quantitative',
|
153
163
|
},
|
154
164
|
},
|
155
165
|
width: plotWidth,
|
@@ -166,10 +176,68 @@ export default function DotPlot(props) {
|
|
166
176
|
},
|
167
177
|
};
|
168
178
|
|
179
|
+
const getTooltipText = useCallback(item => ({
|
180
|
+
[`${capitalize(featureType)}`]: item.datum.feature,
|
181
|
+
[`${capitalize(obsType)} Set`]: item.datum.group,
|
182
|
+
...(isStratified
|
183
|
+
? ({ [`${capitalize(sampleType)} Set`]: item.datum.secondaryGroup })
|
184
|
+
: {}
|
185
|
+
),
|
186
|
+
[`Percentage of ${plur(obsType, 2)} in set`]: item.datum.pctPosInGroup,
|
187
|
+
[meanTransform.join(' ')]: item.datum.meanExpInGroup,
|
188
|
+
}), [featureType, obsType, featureValueType, featureValueTransformName]);
|
189
|
+
|
190
|
+
const obsSetColorScale = useMemo(() => getColorScale(
|
191
|
+
obsSetSelection, obsSetColor, theme,
|
192
|
+
), [obsSetSelection, obsSetColor, theme]);
|
193
|
+
|
194
|
+
const [vegaRenderIncrement, setVegaRenderIncrement] = useState(0);
|
195
|
+
|
196
|
+
useEffect(() => {
|
197
|
+
// If the dot plot is stratified by both obsSet and sampleSet,
|
198
|
+
// then we want to add cell set colors.
|
199
|
+
// TODO: do we also want to add these color bars
|
200
|
+
// when only stratified by obsSet?
|
201
|
+
const domElement = vegaContainerRef.current;
|
202
|
+
|
203
|
+
// Here, we assume that the Vega SVG renderer is being used.
|
204
|
+
const svg = select(domElement)
|
205
|
+
.select('svg');
|
206
|
+
// We use the following CSS selector to identify all of
|
207
|
+
// the <line> elements that we are interested to modify.
|
208
|
+
const tickEls = svg.selectAll('g.root g.column_footer g.role-axis g.role-axis-domain line');
|
209
|
+
|
210
|
+
tickEls
|
211
|
+
.attr('stroke-width', 5)
|
212
|
+
.attr('dy', 2.5)
|
213
|
+
.attr('stroke', (d, i) => {
|
214
|
+
const obsSetPath = obsSetSelection?.[i];
|
215
|
+
return obsSetColorScale(obsSetPath);
|
216
|
+
});
|
217
|
+
}, [vegaContainerRef, vegaRenderIncrement, obsSetSelection, obsSetColorScale]);
|
218
|
+
|
219
|
+
|
220
|
+
// We want to increment the counter whenever we detect that VegaPlot
|
221
|
+
// has re-rendered.
|
222
|
+
const onNewView = useCallback(() => {
|
223
|
+
setVegaRenderIncrement(prev => prev + 1);
|
224
|
+
}, []);
|
225
|
+
|
226
|
+
// This is kind of hacky, since it is possible that the useEffect runs prior
|
227
|
+
// to the Vega rendering, but in practice it seems to work.
|
228
|
+
useEffect(() => {
|
229
|
+
setVegaRenderIncrement(prev => prev + 1);
|
230
|
+
}, [rawData]);
|
231
|
+
|
169
232
|
return (
|
170
|
-
<
|
171
|
-
|
172
|
-
|
173
|
-
|
233
|
+
<div ref={vegaContainerRef}>
|
234
|
+
<VegaPlot
|
235
|
+
data={data}
|
236
|
+
spec={spec}
|
237
|
+
onNewView={onNewView}
|
238
|
+
getTooltipText={getTooltipText}
|
239
|
+
renderer="svg"
|
240
|
+
/>
|
241
|
+
</div>
|
174
242
|
);
|
175
243
|
}
|
package/src/DotPlotSubscriber.js
CHANGED
@@ -160,11 +160,13 @@ export function DotPlotSubscriber(props) {
|
|
160
160
|
width={width}
|
161
161
|
height={height}
|
162
162
|
obsType={obsType}
|
163
|
+
sampleType={sampleType}
|
163
164
|
featureType={featureType}
|
164
165
|
featureValueType={featureValueType}
|
165
166
|
featureValueTransformName={selectedTransformName}
|
166
167
|
featureValueColormap={featureValueColormap}
|
167
|
-
|
168
|
+
obsSetSelection={cellSetSelection}
|
169
|
+
obsSetColor={cellSetColor}
|
168
170
|
/>
|
169
171
|
) : (
|
170
172
|
<span>Select at least one {featureType}.</span>
|