@vitessce/statistical-plots 3.5.8 → 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-1679ef33.js → deflate-ad0dcbe4.js} +1 -1
- package/dist/{index-0f4fe21d.js → index-b8398176.js} +33547 -15443
- package/dist/index.js +6 -5
- package/dist/{jpeg-280f0ee1.js → jpeg-81bd1053.js} +1 -1
- package/dist/{lerc-12264a36.js → lerc-b15c3a4c.js} +1 -1
- package/dist/{lzw-70f852cc.js → lzw-503cb795.js} +1 -1
- package/dist/{packbits-393c67b2.js → packbits-40cbad40.js} +1 -1
- package/dist/{raw-d8d7ab7f.js → raw-9b8d9daf.js} +1 -1
- package/dist/{webimage-5d24a8e2.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/FeatureSetEnrichmentBarPlot.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 +26 -6
- package/dist-tsc/TreemapSubscriber.d.ts.map +1 -1
- package/dist-tsc/TreemapSubscriber.js +10 -5
- package/dist-tsc/VolcanoPlot.d.ts.map +1 -1
- package/dist-tsc/VolcanoPlot.js +18 -48
- 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/FeatureSetEnrichmentBarPlot.js +1 -1
- package/src/FeatureStatsTable.js +116 -0
- package/src/FeatureStatsTableSubscriber.js +133 -0
- package/src/Treemap.js +31 -5
- package/src/TreemapSubscriber.js +12 -5
- package/src/VolcanoPlot.js +21 -66
- 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,20 +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 {
|
10
|
-
import {
|
11
|
-
import { getColorScale } from './utils.js';
|
9
|
+
import { capitalize, getDefaultForegroundColor } from '@vitessce/utils';
|
10
|
+
import { colorArrayToString } from '@vitessce/sets-utils';
|
11
|
+
import { getColorScale, useFilteredVolcanoData } from './utils.js';
|
12
12
|
export default function VolcanoPlot(props) {
|
13
|
-
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;
|
14
14
|
const svgRef = useRef();
|
15
|
-
const computedData =
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
})
|
15
|
+
const [computedData, filteredData] = useFilteredVolcanoData({
|
16
|
+
data,
|
17
|
+
obsSetsColumnNameMappingReversed,
|
18
|
+
sampleSetsColumnNameMappingReversed,
|
19
|
+
featurePointFoldChangeThreshold,
|
20
|
+
featurePointSignificanceThreshold,
|
21
|
+
sampleSetSelection,
|
22
|
+
});
|
23
23
|
const [xExtent, yExtent] = useMemo(() => {
|
24
24
|
if (!computedData) {
|
25
25
|
return [null, null];
|
@@ -43,7 +43,7 @@ export default function VolcanoPlot(props) {
|
|
43
43
|
.attr('height', height)
|
44
44
|
.attr('viewBox', [0, 0, width, height])
|
45
45
|
.attr('style', 'font: 10px sans-serif');
|
46
|
-
if (!
|
46
|
+
if (!filteredData || !xExtent || !yExtent) {
|
47
47
|
return;
|
48
48
|
}
|
49
49
|
// Render scatterplot
|
@@ -68,7 +68,7 @@ export default function VolcanoPlot(props) {
|
|
68
68
|
.call(axisLeft(yScale));
|
69
69
|
// Axis titles
|
70
70
|
const titleG = svg.append('g');
|
71
|
-
const fgColor =
|
71
|
+
const fgColor = colorArrayToString(getDefaultForegroundColor(theme));
|
72
72
|
// Y-axis title
|
73
73
|
titleG
|
74
74
|
.append('text')
|
@@ -88,13 +88,6 @@ export default function VolcanoPlot(props) {
|
|
88
88
|
.text('log2 fold-change')
|
89
89
|
.style('font-size', '12px')
|
90
90
|
.style('fill', fgColor);
|
91
|
-
// Get a mapping from column name to group name.
|
92
|
-
const obsSetsColumnNameMappingReversed = Object.fromEntries(Object
|
93
|
-
.entries(obsSetsColumnNameMapping)
|
94
|
-
.map(([key, value]) => ([value, key])));
|
95
|
-
const sampleSetsColumnNameMappingReversed = Object.fromEntries(Object
|
96
|
-
.entries(sampleSetsColumnNameMapping)
|
97
|
-
.map(([key, value]) => ([value, key])));
|
98
91
|
// Horizontal and vertical rules to indicate currently-selected thresholds
|
99
92
|
// Vertical lines
|
100
93
|
const ruleColor = 'silver';
|
@@ -149,39 +142,15 @@ export default function VolcanoPlot(props) {
|
|
149
142
|
.style('fill', fgColor);
|
150
143
|
const g = svg.append('g');
|
151
144
|
// Append a circle for each data point.
|
152
|
-
|
145
|
+
filteredData.forEach((comparisonObject) => {
|
153
146
|
const obsSetG = g.append('g');
|
154
|
-
const { df, metadata } = comparisonObject;
|
147
|
+
const { df: filteredDf, metadata } = comparisonObject;
|
155
148
|
const coordinationValues = metadata.coordination_values;
|
156
149
|
const rawObsSetPath = coordinationValues.obsSetFilter
|
157
150
|
? coordinationValues.obsSetFilter[0]
|
158
151
|
: coordinationValues.obsSetSelection[0];
|
159
152
|
const obsSetPath = [...rawObsSetPath];
|
160
153
|
obsSetPath[0] = obsSetsColumnNameMappingReversed[rawObsSetPath[0]];
|
161
|
-
// Swap the foldchange direction if backwards with
|
162
|
-
// respect to the current sampleSetSelection pair.
|
163
|
-
// TODO: move this swapping into the computedData useMemo?
|
164
|
-
let shouldSwapFoldChangeDirection = false;
|
165
|
-
if (coordinationValues.sampleSetFilter
|
166
|
-
&& coordinationValues.sampleSetFilter.length === 2) {
|
167
|
-
const rawSampleSetPathA = coordinationValues.sampleSetFilter[0];
|
168
|
-
const sampleSetPathA = [...rawSampleSetPathA];
|
169
|
-
sampleSetPathA[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathA[0]];
|
170
|
-
const rawSampleSetPathB = coordinationValues.sampleSetFilter[1];
|
171
|
-
const sampleSetPathB = [...rawSampleSetPathB];
|
172
|
-
sampleSetPathB[0] = sampleSetsColumnNameMappingReversed[rawSampleSetPathB[0]];
|
173
|
-
if (isEqual(sampleSetPathA, sampleSetSelection[1])
|
174
|
-
&& isEqual(sampleSetPathB, sampleSetSelection[0])) {
|
175
|
-
shouldSwapFoldChangeDirection = true;
|
176
|
-
}
|
177
|
-
}
|
178
|
-
const filteredDf = df.featureId.map((featureId, i) => ({
|
179
|
-
featureId,
|
180
|
-
logFoldChange: df.logFoldChange[i] * (shouldSwapFoldChangeDirection ? -1 : 1),
|
181
|
-
featureSignificance: df.featureSignificance[i],
|
182
|
-
minusLog10p: df.minusLog10p[i],
|
183
|
-
})).filter(d => ((Math.abs(d.logFoldChange) >= (featurePointFoldChangeThreshold ?? 1.0))
|
184
|
-
&& (d.featureSignificance <= (featurePointSignificanceThreshold ?? 0.05))));
|
185
154
|
const color = obsSetColorScale(obsSetPath);
|
186
155
|
obsSetG.append('g')
|
187
156
|
.selectAll('circle')
|
@@ -213,12 +182,13 @@ export default function VolcanoPlot(props) {
|
|
213
182
|
.text(d => `${featureType}: ${d.featureId}\nin ${obsSetPath?.at(-1)}\nlog2 fold-change: ${d.logFoldChange}\np-value: ${d.featureSignificance}`);
|
214
183
|
});
|
215
184
|
}, [width, height, theme, sampleSetColor, sampleSetSelection,
|
216
|
-
obsSetSelection, obsSetColor, featureType,
|
185
|
+
obsSetSelection, obsSetColor, featureType, filteredData,
|
217
186
|
xExtent, yExtent, obsType,
|
218
187
|
marginLeft, marginBottom, marginTop, marginRight,
|
219
188
|
obsSetColorScale, sampleSetColorScale, onFeatureClick,
|
220
189
|
featurePointSignificanceThreshold, featurePointFoldChangeThreshold,
|
221
190
|
featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold,
|
191
|
+
obsSetsColumnNameMappingReversed,
|
222
192
|
]);
|
223
193
|
return (_jsx("svg", { ref: svgRef, style: {
|
224
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>
|