@vitessce/statistical-plots 3.4.6 → 3.4.7
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-c8c2f459.js +13 -0
- package/dist/index-a1925e78.js +206649 -0
- package/dist/index.js +13 -91680
- package/dist/jpeg-ffd14ffe.js +840 -0
- package/dist/lerc-9d1dd17e.js +2014 -0
- package/dist/lzw-3705b408.js +128 -0
- package/dist/packbits-6f657116.js +30 -0
- package/dist/pako.esm-68f84e2a.js +4022 -0
- package/dist/raw-0a76dec9.js +12 -0
- package/dist/webimage-fbdf3bdf.js +32 -0
- package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlot.js +207 -106
- package/dist-tsc/CellSetExpressionPlotOptions.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlotOptions.js +14 -4
- package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlotSubscriber.js +15 -31
- package/dist-tsc/CellSetSizesPlot.d.ts.map +1 -1
- package/dist-tsc/CellSetSizesPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/DotPlot.d.ts +28 -0
- package/dist-tsc/DotPlot.d.ts.map +1 -0
- package/dist-tsc/DotPlot.js +144 -0
- package/dist-tsc/DotPlotSubscriber.d.ts +14 -0
- package/dist-tsc/DotPlotSubscriber.d.ts.map +1 -0
- package/dist-tsc/DotPlotSubscriber.js +54 -0
- package/dist-tsc/ExpressionHistogram.d.ts.map +1 -1
- package/dist-tsc/ExpressionHistogramSubscriber.d.ts.map +1 -1
- package/dist-tsc/dot-plot-hook.d.ts +23 -0
- package/dist-tsc/dot-plot-hook.d.ts.map +1 -0
- package/dist-tsc/dot-plot-hook.js +69 -0
- package/dist-tsc/expr-hooks.d.ts +51 -0
- package/dist-tsc/expr-hooks.d.ts.map +1 -0
- package/dist-tsc/expr-hooks.js +135 -0
- package/dist-tsc/expr-hooks.test.d.ts +2 -0
- package/dist-tsc/expr-hooks.test.d.ts.map +1 -0
- package/dist-tsc/expr-hooks.test.js +97 -0
- package/dist-tsc/index.d.ts +2 -0
- package/dist-tsc/index.js +2 -0
- package/package.json +10 -7
- package/src/CellSetExpressionPlot.js +223 -124
- package/src/CellSetExpressionPlotOptions.js +57 -1
- package/src/CellSetExpressionPlotSubscriber.js +45 -38
- package/src/DotPlot.js +175 -0
- package/src/DotPlotSubscriber.js +173 -0
- package/src/dot-plot-hook.js +107 -0
- package/src/expr-hooks.js +170 -0
- package/src/expr-hooks.test.js +116 -0
- package/src/index.js +2 -0
package/dist-tsc/index.d.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
export { CellSetExpressionPlotSubscriber } from "./CellSetExpressionPlotSubscriber.js";
|
2
2
|
export { CellSetSizesPlotSubscriber } from "./CellSetSizesPlotSubscriber.js";
|
3
3
|
export { ExpressionHistogramSubscriber } from "./ExpressionHistogramSubscriber.js";
|
4
|
+
export { DotPlotSubscriber } from "./DotPlotSubscriber.js";
|
4
5
|
export { FeatureBarPlotSubscriber } from "./FeatureBarPlotSubscriber.js";
|
5
6
|
export { default as CellSetSizesPlot } from "./CellSetSizesPlot.js";
|
6
7
|
export { default as CellSetExpressionPlot } from "./CellSetExpressionPlot.js";
|
7
8
|
export { default as ExpressionHistogram } from "./ExpressionHistogram.js";
|
9
|
+
export { default as DotPlot } from "./DotPlot.js";
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist-tsc/index.js
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
export { CellSetExpressionPlotSubscriber } from './CellSetExpressionPlotSubscriber.js';
|
2
2
|
export { CellSetSizesPlotSubscriber } from './CellSetSizesPlotSubscriber.js';
|
3
3
|
export { ExpressionHistogramSubscriber } from './ExpressionHistogramSubscriber.js';
|
4
|
+
export { DotPlotSubscriber } from './DotPlotSubscriber.js';
|
4
5
|
export { FeatureBarPlotSubscriber } from './FeatureBarPlotSubscriber.js';
|
5
6
|
export { default as CellSetSizesPlot } from './CellSetSizesPlot.js';
|
6
7
|
export { default as CellSetExpressionPlot } from './CellSetExpressionPlot.js';
|
7
8
|
export { default as ExpressionHistogram } from './ExpressionHistogram.js';
|
9
|
+
export { default as DotPlot } from './DotPlot.js';
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitessce/statistical-plots",
|
3
|
-
"version": "3.4.
|
3
|
+
"version": "3.4.7",
|
4
4
|
"author": "Gehlenborg Lab",
|
5
5
|
"homepage": "http://vitessce.io",
|
6
6
|
"repository": {
|
@@ -22,15 +22,18 @@
|
|
22
22
|
"d3-shape": "^3.2.0",
|
23
23
|
"d3-axis": "^3.0.0",
|
24
24
|
"d3-selection": "^3.0.0",
|
25
|
+
"d3-format": "^3.1.0",
|
25
26
|
"vega-scale": "^6.0.0",
|
26
27
|
"lodash-es": "^4.17.21",
|
27
28
|
"react-aria": "^3.28.0",
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"@vitessce/
|
31
|
-
"@vitessce/utils": "3.4.
|
32
|
-
"@vitessce/
|
33
|
-
"@vitessce/
|
29
|
+
"internmap": "^2.0.3",
|
30
|
+
"uuid": "^9.0.0",
|
31
|
+
"@vitessce/constants-internal": "3.4.7",
|
32
|
+
"@vitessce/sets-utils": "3.4.7",
|
33
|
+
"@vitessce/utils": "3.4.7",
|
34
|
+
"@vitessce/vega": "3.4.7",
|
35
|
+
"@vitessce/vit-s": "3.4.7",
|
36
|
+
"@vitessce/gl": "3.4.7"
|
34
37
|
},
|
35
38
|
"devDependencies": {
|
36
39
|
"react": "^18.0.0",
|
@@ -1,20 +1,9 @@
|
|
1
1
|
/* eslint-disable indent */
|
2
2
|
/* eslint-disable camelcase */
|
3
3
|
import React, { useMemo, useEffect, useRef } from 'react';
|
4
|
-
import { scaleLinear } from 'd3-scale';
|
4
|
+
import { scaleLinear, scaleOrdinal } from 'd3-scale';
|
5
5
|
import { scale as vega_scale } from 'vega-scale';
|
6
6
|
import { axisBottom, axisLeft } from 'd3-axis';
|
7
|
-
import {
|
8
|
-
bin,
|
9
|
-
min,
|
10
|
-
max,
|
11
|
-
rollup as d3_rollup,
|
12
|
-
mean as d3_mean,
|
13
|
-
deviation as d3_deviation,
|
14
|
-
ascending as d3_ascending,
|
15
|
-
map as d3_map,
|
16
|
-
quantileSorted,
|
17
|
-
} from 'd3-array';
|
18
7
|
import { area as d3_area, curveBasis } from 'd3-shape';
|
19
8
|
import { select } from 'd3-selection';
|
20
9
|
import { colorArrayToString } from '@vitessce/sets-utils';
|
@@ -22,44 +11,6 @@ import { capitalize } from '@vitessce/utils';
|
|
22
11
|
|
23
12
|
const scaleBand = vega_scale('band');
|
24
13
|
|
25
|
-
const GROUP_KEY = 'set';
|
26
|
-
const VALUE_KEY = 'value';
|
27
|
-
|
28
|
-
// Reference: https://github.com/d3/d3-array/issues/180#issuecomment-851378012
|
29
|
-
function summarize(iterable, keepZeros) {
|
30
|
-
const values = d3_map(iterable, d => d[VALUE_KEY])
|
31
|
-
.filter(d => keepZeros || d !== 0.0)
|
32
|
-
.sort(d3_ascending);
|
33
|
-
const minVal = values[0];
|
34
|
-
const maxVal = values[values.length - 1];
|
35
|
-
const q1 = quantileSorted(values, 0.25);
|
36
|
-
const q2 = quantileSorted(values, 0.5);
|
37
|
-
const q3 = quantileSorted(values, 0.75);
|
38
|
-
const iqr = q3 - q1; // interquartile range
|
39
|
-
const r0 = Math.max(minVal, q1 - iqr * 1.5);
|
40
|
-
const r1 = Math.min(maxVal, q3 + iqr * 1.5);
|
41
|
-
let i = -1;
|
42
|
-
while (values[++i] < r0);
|
43
|
-
const w0 = values[i];
|
44
|
-
while (values[++i] <= r1);
|
45
|
-
const w1 = values[i - 1];
|
46
|
-
|
47
|
-
// Chauvenet
|
48
|
-
// Reference: https://en.wikipedia.org/wiki/Chauvenet%27s_criterion
|
49
|
-
const mean = d3_mean(values);
|
50
|
-
const stdv = d3_deviation(values);
|
51
|
-
const c0 = mean - 3 * stdv;
|
52
|
-
const c1 = mean + 3 * stdv;
|
53
|
-
|
54
|
-
return {
|
55
|
-
quartiles: [q1, q2, q3],
|
56
|
-
range: [r0, r1],
|
57
|
-
whiskers: [w0, w1],
|
58
|
-
chauvenetRange: [c0, c1],
|
59
|
-
nonOutliers: values.filter(v => c0 <= v && v <= c1),
|
60
|
-
};
|
61
|
-
}
|
62
|
-
|
63
14
|
/**
|
64
15
|
* Gene expression histogram displayed as a bar chart,
|
65
16
|
* implemented with the VegaPlot component.
|
@@ -86,6 +37,9 @@ export default function CellSetExpressionPlot(props) {
|
|
86
37
|
yMin: yMinProp,
|
87
38
|
yUnits,
|
88
39
|
jitter,
|
40
|
+
cellSetSelection,
|
41
|
+
sampleSetSelection,
|
42
|
+
sampleSetColor,
|
89
43
|
colors,
|
90
44
|
data,
|
91
45
|
theme,
|
@@ -104,11 +58,19 @@ export default function CellSetExpressionPlot(props) {
|
|
104
58
|
const svgRef = useRef();
|
105
59
|
|
106
60
|
// Get the max characters in an axis label for autsizing the bottom margin.
|
107
|
-
const maxCharactersForLabel = useMemo(() =>
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
61
|
+
const maxCharactersForLabel = useMemo(() => {
|
62
|
+
if (!cellSetSelection) {
|
63
|
+
return 0;
|
64
|
+
}
|
65
|
+
const cellSetNames = cellSetSelection.map(d => d.at(-1));
|
66
|
+
return cellSetNames.reduce((acc, name) => {
|
67
|
+
// eslint-disable-next-line no-param-reassign
|
68
|
+
acc = acc === undefined || name.length > acc ? name.length : acc;
|
69
|
+
return acc;
|
70
|
+
}, 0);
|
71
|
+
}, [cellSetSelection]);
|
72
|
+
|
73
|
+
const isStratified = (Array.isArray(sampleSetSelection) && sampleSetSelection.length === 2);
|
112
74
|
|
113
75
|
useEffect(() => {
|
114
76
|
const domElement = svgRef.current;
|
@@ -130,6 +92,8 @@ export default function CellSetExpressionPlot(props) {
|
|
130
92
|
|
131
93
|
const rectColor = (theme === 'dark' ? 'white' : 'black');
|
132
94
|
|
95
|
+
const fgColor = (theme === 'dark' ? 'white' : 'black');
|
96
|
+
|
133
97
|
const svg = select(domElement);
|
134
98
|
svg.selectAll('g').remove();
|
135
99
|
svg
|
@@ -141,48 +105,52 @@ export default function CellSetExpressionPlot(props) {
|
|
141
105
|
.attr('width', width)
|
142
106
|
.attr('height', height);
|
143
107
|
|
144
|
-
const groupNames = colors.map(d => d.name);
|
145
|
-
|
146
108
|
// Manually set the color scale so that Vega-Lite does
|
147
109
|
// not choose the colors automatically.
|
148
|
-
const colorScale =
|
149
|
-
domain
|
150
|
-
range
|
151
|
-
|
110
|
+
const colorScale = scaleOrdinal()
|
111
|
+
.domain(colors.map(d => d.setNamePath))
|
112
|
+
.range(colors.map(d => colorArrayToString(d.color)));
|
113
|
+
|
114
|
+
const sampleSetNames = sampleSetSelection?.map(path => path.at(-1));
|
115
|
+
|
116
|
+
let stratificationSide;
|
117
|
+
let stratificationColor;
|
118
|
+
if (isStratified) {
|
119
|
+
stratificationSide = scaleOrdinal()
|
120
|
+
.domain(sampleSetNames)
|
121
|
+
.range(['left', 'right']);
|
122
|
+
stratificationColor = scaleOrdinal()
|
123
|
+
.domain(sampleSetNames)
|
124
|
+
.range(
|
125
|
+
// TODO: check for full path equality here.
|
126
|
+
sampleSetNames
|
127
|
+
.map(name => sampleSetColor?.find(d => d.path.at(-1) === name).color)
|
128
|
+
.map(colorArrayToString),
|
129
|
+
);
|
130
|
+
}
|
152
131
|
|
153
132
|
// Remove outliers on a per-group basis.
|
154
|
-
const
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
133
|
+
const {
|
134
|
+
groupSummaries: groupedSummaries,
|
135
|
+
groupData: groupedData,
|
136
|
+
groupBins, // Array of [{ key, value: [{ key, value: histogram(nonOutliers) }] }]
|
137
|
+
groupBinsMax, // Number
|
138
|
+
} = data;
|
139
|
+
let { y } = data;
|
161
140
|
|
162
141
|
const innerWidth = width - marginLeft;
|
163
142
|
const innerHeight = height - autoMarginBottom;
|
164
143
|
|
165
144
|
const xGroup = scaleBand()
|
166
145
|
.range([marginLeft, width - marginRight])
|
167
|
-
.domain(
|
146
|
+
.domain(cellSetSelection)
|
168
147
|
.padding(0.1);
|
169
148
|
|
170
|
-
const yMin = (yMinProp === null ? Math.min(0, min(trimmedData)) : yMinProp);
|
171
149
|
|
172
150
|
// For the y domain, use the yMin prop
|
173
151
|
// to support a use case such as 'Aspect Ratio',
|
174
152
|
// where the domain minimum should be 1 rather than 0.
|
175
|
-
|
176
|
-
.domain([yMin, max(trimmedData)])
|
177
|
-
.range([innerHeight, marginTop]);
|
178
|
-
|
179
|
-
const histogram = bin()
|
180
|
-
.thresholds(y.ticks(16))
|
181
|
-
.domain(y.domain());
|
182
|
-
|
183
|
-
const groupBins = groupedData.map(kv => ({ key: kv.key, value: histogram(kv.value) }));
|
184
|
-
|
185
|
-
const groupBinsMax = max(groupBins.flatMap(d => d.value.map(v => v.length)));
|
153
|
+
y = y.range([innerHeight, marginTop]);
|
186
154
|
|
187
155
|
const x = scaleLinear()
|
188
156
|
.domain([-groupBinsMax, groupBinsMax])
|
@@ -194,18 +162,54 @@ export default function CellSetExpressionPlot(props) {
|
|
194
162
|
.y(d => y(d.x0))
|
195
163
|
.curve(curveBasis);
|
196
164
|
|
165
|
+
const leftArea = d3_area()
|
166
|
+
.x0(d => x(-d.length))
|
167
|
+
.x1(() => x(0))
|
168
|
+
.y(d => y(d.x0))
|
169
|
+
.curve(curveBasis);
|
170
|
+
|
171
|
+
const rightArea = d3_area()
|
172
|
+
.x0(() => x(0))
|
173
|
+
.x1(d => x(d.length))
|
174
|
+
.y(d => y(d.x0))
|
175
|
+
.curve(curveBasis);
|
176
|
+
|
177
|
+
const sideToAreaFunc = {
|
178
|
+
left: leftArea,
|
179
|
+
right: rightArea,
|
180
|
+
};
|
181
|
+
|
197
182
|
// Violin areas
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
.
|
203
|
-
.
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
183
|
+
if (isStratified) {
|
184
|
+
const violinG = g
|
185
|
+
.selectAll('violin')
|
186
|
+
.data(groupBins)
|
187
|
+
.enter()
|
188
|
+
.append('g')
|
189
|
+
.attr('transform', d => `translate(${xGroup(d.key)},0)`);
|
190
|
+
violinG.append('path')
|
191
|
+
.datum(d => d.value[0])
|
192
|
+
.style('stroke', 'none')
|
193
|
+
.style('fill', d => stratificationColor(d.key))
|
194
|
+
.attr('d', d => sideToAreaFunc[stratificationSide(d.key)](d.value));
|
195
|
+
violinG.append('path')
|
196
|
+
.datum(d => d.value[1])
|
197
|
+
.style('stroke', 'none')
|
198
|
+
.style('fill', d => stratificationColor(d.key))
|
199
|
+
.attr('d', d => sideToAreaFunc[stratificationSide(d.key)](d.value));
|
200
|
+
} else {
|
201
|
+
g
|
202
|
+
.selectAll('violin')
|
203
|
+
.data(groupBins)
|
204
|
+
.enter()
|
205
|
+
.append('g')
|
206
|
+
.attr('transform', d => `translate(${xGroup(d.key)},0)`)
|
207
|
+
.style('fill', d => colorScale(d.key))
|
208
|
+
.append('path')
|
209
|
+
.datum(d => d.value[0])
|
210
|
+
.style('stroke', 'none')
|
211
|
+
.attr('d', d => area(d.value));
|
212
|
+
}
|
209
213
|
|
210
214
|
// Whiskers
|
211
215
|
const whiskerGroups = g.selectAll('whiskers')
|
@@ -213,38 +217,88 @@ export default function CellSetExpressionPlot(props) {
|
|
213
217
|
.enter()
|
214
218
|
.append('g')
|
215
219
|
.attr('transform', d => `translate(${xGroup(d.key)},0)`);
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
.
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
.
|
229
|
-
|
230
|
-
|
231
|
-
|
220
|
+
|
221
|
+
if (isStratified) {
|
222
|
+
// Vertical line
|
223
|
+
whiskerGroups.append('line')
|
224
|
+
.datum(d => d.value[0])
|
225
|
+
.attr('stroke', rectColor)
|
226
|
+
.attr('x1', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 1.5))
|
227
|
+
.attr('x2', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 1.5))
|
228
|
+
.attr('y1', d => y(d.value.quartiles[0]))
|
229
|
+
.attr('y2', d => y(d.value.quartiles[2]))
|
230
|
+
.attr('stroke-width', 2);
|
231
|
+
|
232
|
+
whiskerGroups.append('line')
|
233
|
+
.datum(d => d.value[1])
|
234
|
+
.attr('stroke', rectColor)
|
235
|
+
.attr('x1', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 1.5))
|
236
|
+
.attr('x2', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 1.5))
|
237
|
+
.attr('y1', d => y(d.value.quartiles[0]))
|
238
|
+
.attr('y2', d => y(d.value.quartiles[2]))
|
239
|
+
.attr('stroke-width', 2);
|
240
|
+
|
241
|
+
// Horizontal line
|
242
|
+
whiskerGroups.append('line')
|
243
|
+
.datum(d => d.value[0])
|
244
|
+
.attr('stroke', rectColor)
|
245
|
+
.attr('x1', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -5.5 : 1.5))
|
246
|
+
.attr('x2', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 5.5))
|
247
|
+
.attr('y1', d => y(d.value.quartiles[1]))
|
248
|
+
.attr('y2', d => y(d.value.quartiles[1]))
|
249
|
+
.attr('stroke-width', 2);
|
250
|
+
|
251
|
+
whiskerGroups.append('line')
|
252
|
+
.datum(d => d.value[1])
|
253
|
+
.attr('stroke', rectColor)
|
254
|
+
.attr('x1', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -5.5 : 1.5))
|
255
|
+
.attr('x2', d => xGroup.bandwidth() / 2 + (stratificationSide(d.key) === 'left' ? -1.5 : 5.5))
|
256
|
+
.attr('y1', d => y(d.value.quartiles[1]))
|
257
|
+
.attr('y2', d => y(d.value.quartiles[1]))
|
258
|
+
.attr('stroke-width', 2);
|
259
|
+
} else {
|
260
|
+
// Vertical line
|
261
|
+
whiskerGroups.append('line')
|
262
|
+
.datum(d => d.value[0].value)
|
263
|
+
.attr('stroke', rectColor)
|
264
|
+
.attr('x1', xGroup.bandwidth() / 2)
|
265
|
+
.attr('x2', xGroup.bandwidth() / 2)
|
266
|
+
.attr('y1', d => y(d.quartiles[0]))
|
267
|
+
.attr('y2', d => y(d.quartiles[2]))
|
268
|
+
.attr('stroke-width', 2);
|
269
|
+
|
270
|
+
// Horizontal line
|
271
|
+
whiskerGroups.append('line')
|
272
|
+
.datum(d => d.value[0].value)
|
273
|
+
.attr('stroke', rectColor)
|
274
|
+
.attr('x1', xGroup.bandwidth() / 2 - (jitter ? 0 : 4))
|
275
|
+
.attr('x2', xGroup.bandwidth() / 2 + 4)
|
276
|
+
.attr('y1', d => y(d.quartiles[1]))
|
277
|
+
.attr('y2', d => y(d.quartiles[1]))
|
278
|
+
.attr('stroke-width', 2);
|
279
|
+
}
|
232
280
|
|
233
281
|
// Jittered points
|
234
282
|
if (jitter) {
|
235
283
|
groupedData.forEach(({ key, value }) => {
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
.
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
284
|
+
value.forEach(({ value: subValue }) => {
|
285
|
+
if (isStratified) {
|
286
|
+
// TODO
|
287
|
+
} else {
|
288
|
+
const groupG = g.append('g');
|
289
|
+
groupG.selectAll('point')
|
290
|
+
.data(subValue)
|
291
|
+
.enter()
|
292
|
+
.append('circle')
|
293
|
+
.attr('transform', `translate(${xGroup(key)},0)`)
|
294
|
+
.style('stroke', 'none')
|
295
|
+
.style('fill', 'silver')
|
296
|
+
.style('opacity', '0.1')
|
297
|
+
.attr('cx', () => 5 + Math.random() * ((xGroup.bandwidth() / 2) - 10))
|
298
|
+
.attr('cy', d => y(d))
|
299
|
+
.attr('r', 2);
|
300
|
+
}
|
301
|
+
});
|
248
302
|
});
|
249
303
|
}
|
250
304
|
|
@@ -261,7 +315,7 @@ export default function CellSetExpressionPlot(props) {
|
|
261
315
|
.append('g')
|
262
316
|
.attr('transform', `translate(0,${innerHeight})`)
|
263
317
|
.style('font-size', '14px')
|
264
|
-
.call(axisBottom(xGroup))
|
318
|
+
.call(axisBottom(xGroup).tickFormat(d => d.at(-1)))
|
265
319
|
.selectAll('text')
|
266
320
|
.style('font-size', '11px')
|
267
321
|
.attr('dx', '-6px')
|
@@ -278,7 +332,7 @@ export default function CellSetExpressionPlot(props) {
|
|
278
332
|
.attr('transform', 'rotate(-90)')
|
279
333
|
.text(yTitle)
|
280
334
|
.style('font-size', '12px')
|
281
|
-
.style('fill',
|
335
|
+
.style('fill', fgColor);
|
282
336
|
|
283
337
|
// X-axis title
|
284
338
|
g
|
@@ -288,11 +342,56 @@ export default function CellSetExpressionPlot(props) {
|
|
288
342
|
.attr('y', height - 10)
|
289
343
|
.text(xTitle)
|
290
344
|
.style('font-size', '12px')
|
291
|
-
.style('fill',
|
345
|
+
.style('fill', fgColor);
|
346
|
+
|
347
|
+
// Legend
|
348
|
+
if (isStratified) {
|
349
|
+
const legendG = g
|
350
|
+
.append('g')
|
351
|
+
.attr('transform', `translate(${marginLeft + innerWidth - 150},${marginTop})`);
|
352
|
+
legendG.append('rect')
|
353
|
+
.attr('width', 150)
|
354
|
+
.attr('height', 56)
|
355
|
+
.attr('x', 0)
|
356
|
+
.attr('y', 0)
|
357
|
+
.style('fill', 'rgba(215, 215, 215, 0.2)')
|
358
|
+
.attr('rx', 4);
|
359
|
+
legendG.append('text')
|
360
|
+
.text('Sample Group')
|
361
|
+
.style('font-size', '11px')
|
362
|
+
.style('line-height', 20)
|
363
|
+
.attr('x', 4)
|
364
|
+
.attr('y', 14)
|
365
|
+
.style('fill', fgColor);
|
366
|
+
legendG.append('rect')
|
367
|
+
.attr('width', 10)
|
368
|
+
.attr('height', 10)
|
369
|
+
.attr('x', 5)
|
370
|
+
.attr('y', 23)
|
371
|
+
.style('fill', stratificationColor(sampleSetNames[0]));
|
372
|
+
legendG.append('text')
|
373
|
+
.text(sampleSetNames[0])
|
374
|
+
.style('font-size', '11px')
|
375
|
+
.attr('x', 20)
|
376
|
+
.attr('y', 32)
|
377
|
+
.style('fill', fgColor);
|
378
|
+
legendG.append('rect')
|
379
|
+
.attr('width', 10)
|
380
|
+
.attr('height', 10)
|
381
|
+
.attr('x', 5)
|
382
|
+
.attr('y', 39)
|
383
|
+
.style('fill', stratificationColor(sampleSetNames[1]));
|
384
|
+
legendG.append('text')
|
385
|
+
.text(sampleSetNames[1])
|
386
|
+
.style('font-size', '11px')
|
387
|
+
.attr('x', 20)
|
388
|
+
.attr('y', 48)
|
389
|
+
.style('fill', fgColor);
|
390
|
+
}
|
292
391
|
}, [width, height, data, marginLeft, marginBottom, colors,
|
293
392
|
jitter, theme, yMinProp, marginTop, marginRight, featureType,
|
294
393
|
featureValueType, featureValueTransformName, yUnits, obsType,
|
295
|
-
maxCharactersForLabel,
|
394
|
+
maxCharactersForLabel, sampleSetSelection,
|
296
395
|
]);
|
297
396
|
|
298
397
|
return (
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { useId } from 'react-aria';
|
3
|
-
import { TableCell, TableRow, TextField } from '@material-ui/core';
|
3
|
+
import { TableCell, TableRow, TextField, Slider } from '@material-ui/core';
|
4
4
|
import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
|
5
|
+
import { GLSL_COLORMAPS } from '@vitessce/gl';
|
5
6
|
|
6
7
|
export default function CellSetExpressionPlotOptions(props) {
|
7
8
|
const {
|
@@ -10,16 +11,28 @@ export default function CellSetExpressionPlotOptions(props) {
|
|
10
11
|
featureValueTransformCoefficient,
|
11
12
|
setFeatureValueTransformCoefficient,
|
12
13
|
transformOptions,
|
14
|
+
featureValuePositivityThreshold,
|
15
|
+
setFeatureValuePositivityThreshold,
|
16
|
+
featureValueColormap,
|
17
|
+
setFeatureValueColormap,
|
13
18
|
} = props;
|
14
19
|
|
15
20
|
const cellSetExpressionPlotOptionsId = useId();
|
16
21
|
|
17
22
|
const classes = usePlotOptionsStyles();
|
18
23
|
|
24
|
+
function handleFeatureValueColormapChange(event) {
|
25
|
+
setFeatureValueColormap(event.target.value);
|
26
|
+
}
|
27
|
+
|
19
28
|
const handleTransformChange = (event) => {
|
20
29
|
setFeatureValueTransform(event.target.value === '' ? null : event.target.value);
|
21
30
|
};
|
22
31
|
|
32
|
+
function handlePositivityThresholdChange(event, value) {
|
33
|
+
setFeatureValuePositivityThreshold(value);
|
34
|
+
}
|
35
|
+
|
23
36
|
// Feels a little hacky, but I think this is the best way to handle
|
24
37
|
// the limitations of the v4 material-ui number input.
|
25
38
|
const handleTransformCoefficientChange = (event) => {
|
@@ -36,6 +49,30 @@ export default function CellSetExpressionPlotOptions(props) {
|
|
36
49
|
|
37
50
|
return (
|
38
51
|
<OptionsContainer>
|
52
|
+
{setFeatureValueColormap ? (
|
53
|
+
<TableRow>
|
54
|
+
<TableCell className={classes.labelCell} variant="head" scope="row">
|
55
|
+
<label htmlFor={`cellset-expression-feature-value-colormap-${cellSetExpressionPlotOptionsId}`}>
|
56
|
+
Feature Value Colormap
|
57
|
+
</label>
|
58
|
+
</TableCell>
|
59
|
+
<TableCell className={classes.inputCell} variant="body">
|
60
|
+
<OptionSelect
|
61
|
+
className={classes.select}
|
62
|
+
value={featureValueColormap}
|
63
|
+
onChange={handleFeatureValueColormapChange}
|
64
|
+
inputProps={{
|
65
|
+
'aria-label': 'Select feature value colormap',
|
66
|
+
id: `cellset-expression-feature-value-colormap-${cellSetExpressionPlotOptionsId}`,
|
67
|
+
}}
|
68
|
+
>
|
69
|
+
{GLSL_COLORMAPS.map(cmap => (
|
70
|
+
<option key={cmap} value={cmap}>{cmap}</option>
|
71
|
+
))}
|
72
|
+
</OptionSelect>
|
73
|
+
</TableCell>
|
74
|
+
</TableRow>
|
75
|
+
) : null}
|
39
76
|
<TableRow>
|
40
77
|
<TableCell className={classes.labelCell} variant="head" scope="row">
|
41
78
|
<label
|
@@ -82,6 +119,25 @@ export default function CellSetExpressionPlotOptions(props) {
|
|
82
119
|
/>
|
83
120
|
</TableCell>
|
84
121
|
</TableRow>
|
122
|
+
{setFeatureValuePositivityThreshold ? (
|
123
|
+
<TableRow key="transform-coefficient-option-row">
|
124
|
+
<TableCell className={classes.labelCell}>
|
125
|
+
Positivity Threshold
|
126
|
+
</TableCell>
|
127
|
+
<TableCell className={classes.inputCell}>
|
128
|
+
<Slider
|
129
|
+
classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
|
130
|
+
value={featureValuePositivityThreshold}
|
131
|
+
onChange={handlePositivityThresholdChange}
|
132
|
+
aria-labelledby="pos-threshold-slider"
|
133
|
+
valueLabelDisplay="auto"
|
134
|
+
step={1.0}
|
135
|
+
min={0.0}
|
136
|
+
max={100.0}
|
137
|
+
/>
|
138
|
+
</TableCell>
|
139
|
+
</TableRow>
|
140
|
+
) : null}
|
85
141
|
</OptionsContainer>
|
86
142
|
);
|
87
143
|
}
|