@vitessce/statistical-plots 3.3.3 → 3.3.5
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/index.js +16714 -12197
- package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlot.js +214 -219
- package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -1
- package/dist-tsc/CellSetExpressionPlotSubscriber.js +3 -3
- package/dist-tsc/ExpressionHistogram.d.ts.map +1 -1
- package/dist-tsc/ExpressionHistogram.js +7 -4
- package/dist-tsc/ExpressionHistogramSubscriber.d.ts.map +1 -1
- package/dist-tsc/ExpressionHistogramSubscriber.js +3 -4
- package/package.json +11 -6
- package/src/CellSetExpressionPlot.js +265 -233
- package/src/CellSetExpressionPlotSubscriber.js +8 -2
- package/src/ExpressionHistogram.js +11 -3
- package/src/ExpressionHistogramSubscriber.js +6 -4
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"CellSetExpressionPlot.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlot.js"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"CellSetExpressionPlot.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlot.js"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IAjB2B,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;gBAqOrB"}
|
@@ -1,9 +1,52 @@
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
-
|
3
|
-
|
4
|
-
import {
|
2
|
+
/* eslint-disable indent */
|
3
|
+
/* eslint-disable camelcase */
|
4
|
+
import React, { useMemo, useEffect, useRef } from 'react';
|
5
|
+
import { scaleLinear } from 'd3-scale';
|
6
|
+
import { scale as vega_scale } from 'vega-scale';
|
7
|
+
import { axisBottom, axisLeft } from 'd3-axis';
|
8
|
+
import { bin, min, max, rollup as d3_rollup, mean as d3_mean, deviation as d3_deviation, ascending as d3_ascending, map as d3_map, quantileSorted, } from 'd3-array';
|
9
|
+
import { area as d3_area, curveBasis } from 'd3-shape';
|
10
|
+
import { select } from 'd3-selection';
|
5
11
|
import { colorArrayToString } from '@vitessce/sets-utils';
|
6
12
|
import { capitalize } from '@vitessce/utils';
|
13
|
+
const scaleBand = vega_scale('band');
|
14
|
+
const GROUP_KEY = 'set';
|
15
|
+
const VALUE_KEY = 'value';
|
16
|
+
// Reference: https://github.com/d3/d3-array/issues/180#issuecomment-851378012
|
17
|
+
function summarize(iterable, keepZeros) {
|
18
|
+
const values = d3_map(iterable, d => d[VALUE_KEY])
|
19
|
+
.filter(d => keepZeros || d !== 0.0)
|
20
|
+
.sort(d3_ascending);
|
21
|
+
const minVal = values[0];
|
22
|
+
const maxVal = values[values.length - 1];
|
23
|
+
const q1 = quantileSorted(values, 0.25);
|
24
|
+
const q2 = quantileSorted(values, 0.5);
|
25
|
+
const q3 = quantileSorted(values, 0.75);
|
26
|
+
const iqr = q3 - q1; // interquartile range
|
27
|
+
const r0 = Math.max(minVal, q1 - iqr * 1.5);
|
28
|
+
const r1 = Math.min(maxVal, q3 + iqr * 1.5);
|
29
|
+
let i = -1;
|
30
|
+
while (values[++i] < r0)
|
31
|
+
;
|
32
|
+
const w0 = values[i];
|
33
|
+
while (values[++i] <= r1)
|
34
|
+
;
|
35
|
+
const w1 = values[i - 1];
|
36
|
+
// Chauvenet
|
37
|
+
// Reference: https://en.wikipedia.org/wiki/Chauvenet%27s_criterion
|
38
|
+
const mean = d3_mean(values);
|
39
|
+
const stdv = d3_deviation(values);
|
40
|
+
const c0 = mean - 3 * stdv;
|
41
|
+
const c1 = mean + 3 * stdv;
|
42
|
+
return {
|
43
|
+
quartiles: [q1, q2, q3],
|
44
|
+
range: [r0, r1],
|
45
|
+
whiskers: [w0, w1],
|
46
|
+
chauvenetRange: [c0, c1],
|
47
|
+
nonOutliers: values.filter(v => c0 <= v && v <= c1),
|
48
|
+
};
|
49
|
+
}
|
7
50
|
/**
|
8
51
|
* Gene expression histogram displayed as a bar chart,
|
9
52
|
* implemented with the VegaPlot component.
|
@@ -26,223 +69,175 @@ import { capitalize } from '@vitessce/utils';
|
|
26
69
|
* for the feature value transformation function.
|
27
70
|
*/
|
28
71
|
export default function CellSetExpressionPlot(props) {
|
29
|
-
const {
|
72
|
+
const { yMin: yMinProp, yUnits, jitter, colors, data, theme, width, height, marginTop = 5, marginRight = 5, marginLeft = 50, marginBottom, obsType, featureType, featureValueType, featureValueTransformName, } = props;
|
73
|
+
const svgRef = useRef();
|
30
74
|
// Get the max characters in an axis label for autsizing the bottom margin.
|
31
|
-
const maxCharactersForLabel = data.reduce((acc, val) => {
|
75
|
+
const maxCharactersForLabel = useMemo(() => data.reduce((acc, val) => {
|
32
76
|
// eslint-disable-next-line no-param-reassign
|
33
|
-
acc = acc === undefined || val.
|
77
|
+
acc = acc === undefined || val[GROUP_KEY].length > acc ? val[GROUP_KEY].length : acc;
|
34
78
|
return acc;
|
35
|
-
}, 0);
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
{
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
{
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
orient: 'vertical',
|
200
|
-
from: { data: 'violin' },
|
201
|
-
encode: {
|
202
|
-
enter: {
|
203
|
-
fill: { scale: 'color', field: { parent: 'set' } },
|
204
|
-
},
|
205
|
-
update: {
|
206
|
-
width: { scale: 'wscaleReversed', field: 'density' },
|
207
|
-
xc: { signal: 'bandWidth' },
|
208
|
-
y2: { scale: 'yscale', field: 'value' },
|
209
|
-
y: { scale: 'yscale', value: 0 },
|
210
|
-
},
|
211
|
-
},
|
212
|
-
},
|
213
|
-
{
|
214
|
-
type: 'rect',
|
215
|
-
from: { data: 'summary' },
|
216
|
-
encode: {
|
217
|
-
enter: {
|
218
|
-
fill: { value: rectColor },
|
219
|
-
width: { value: 2 },
|
220
|
-
},
|
221
|
-
update: {
|
222
|
-
y: { scale: 'yscale', field: 'q1' },
|
223
|
-
y2: { scale: 'yscale', field: 'q3' },
|
224
|
-
xc: { signal: 'bandWidth / 2' },
|
225
|
-
},
|
226
|
-
},
|
227
|
-
},
|
228
|
-
{
|
229
|
-
type: 'rect',
|
230
|
-
from: { data: 'summary' },
|
231
|
-
encode: {
|
232
|
-
enter: {
|
233
|
-
fill: { value: rectColor },
|
234
|
-
height: { value: 2 },
|
235
|
-
width: { value: 8 },
|
236
|
-
},
|
237
|
-
update: {
|
238
|
-
y: { scale: 'yscale', field: 'median' },
|
239
|
-
xc: { signal: 'bandWidth / 2' },
|
240
|
-
},
|
241
|
-
},
|
242
|
-
},
|
243
|
-
],
|
244
|
-
},
|
245
|
-
],
|
246
|
-
};
|
247
|
-
return (_jsx(VegaPlot, { data: data, spec: spec }));
|
79
|
+
}, 0), [data]);
|
80
|
+
useEffect(() => {
|
81
|
+
const domElement = svgRef.current;
|
82
|
+
const transformPrefix = (featureValueTransformName && featureValueTransformName !== 'None')
|
83
|
+
? `${featureValueTransformName}-Transformed `
|
84
|
+
: '';
|
85
|
+
const unitSuffix = yUnits ? ` (${yUnits})` : '';
|
86
|
+
const yTitle = `${transformPrefix}${capitalize(featureValueType)}${unitSuffix}`;
|
87
|
+
const xTitle = `${capitalize(obsType)} Set`;
|
88
|
+
// Use a square-root term because the angle of the labels is 45 degrees (see below)
|
89
|
+
// so the perpendicular distance to the bottom of the labels is proportional to the
|
90
|
+
// square root of the length of the labels along the imaginary hypotenuse.
|
91
|
+
// 30 is an estimate of the pixel size of a given character and seems to work well.
|
92
|
+
const autoMarginBottom = marginBottom
|
93
|
+
|| 30 + Math.sqrt(maxCharactersForLabel / 2) * 30;
|
94
|
+
const rectColor = (theme === 'dark' ? 'white' : 'black');
|
95
|
+
const svg = select(domElement);
|
96
|
+
svg.selectAll('g').remove();
|
97
|
+
svg
|
98
|
+
.attr('width', width)
|
99
|
+
.attr('height', height);
|
100
|
+
const g = svg
|
101
|
+
.append('g')
|
102
|
+
.attr('width', width)
|
103
|
+
.attr('height', height);
|
104
|
+
const groupNames = colors.map(d => d.name);
|
105
|
+
// Manually set the color scale so that Vega-Lite does
|
106
|
+
// not choose the colors automatically.
|
107
|
+
const colorScale = {
|
108
|
+
domain: colors.map(d => d.name),
|
109
|
+
range: colors.map(d => colorArrayToString(d.color)),
|
110
|
+
};
|
111
|
+
// Remove outliers on a per-group basis.
|
112
|
+
const groupedSummaries = Array.from(d3_rollup(data, groupData => summarize(groupData, true), d => d[GROUP_KEY]), ([key, value]) => ({ key, value }));
|
113
|
+
const groupedData = groupedSummaries
|
114
|
+
.map(({ key, value }) => ({ key, value: value.nonOutliers }));
|
115
|
+
const trimmedData = groupedData.map(kv => kv.value).flat();
|
116
|
+
const innerWidth = width - marginLeft;
|
117
|
+
const innerHeight = height - autoMarginBottom;
|
118
|
+
const xGroup = scaleBand()
|
119
|
+
.range([marginLeft, width - marginRight])
|
120
|
+
.domain(groupNames)
|
121
|
+
.padding(0.1);
|
122
|
+
const yMin = (yMinProp === null ? Math.min(0, min(trimmedData)) : yMinProp);
|
123
|
+
// For the y domain, use the yMin prop
|
124
|
+
// to support a use case such as 'Aspect Ratio',
|
125
|
+
// where the domain minimum should be 1 rather than 0.
|
126
|
+
const y = scaleLinear()
|
127
|
+
.domain([yMin, max(trimmedData)])
|
128
|
+
.range([innerHeight, marginTop]);
|
129
|
+
const histogram = bin()
|
130
|
+
.thresholds(y.ticks(16))
|
131
|
+
.domain(y.domain());
|
132
|
+
const groupBins = groupedData.map(kv => ({ key: kv.key, value: histogram(kv.value) }));
|
133
|
+
const groupBinsMax = max(groupBins.flatMap(d => d.value.map(v => v.length)));
|
134
|
+
const x = scaleLinear()
|
135
|
+
.domain([-groupBinsMax, groupBinsMax])
|
136
|
+
.range([0, xGroup.bandwidth()]);
|
137
|
+
const area = d3_area()
|
138
|
+
.x0(d => (jitter ? x(0) : x(-d.length)))
|
139
|
+
.x1(d => x(d.length))
|
140
|
+
.y(d => y(d.x0))
|
141
|
+
.curve(curveBasis);
|
142
|
+
// Violin areas
|
143
|
+
g
|
144
|
+
.selectAll('violin')
|
145
|
+
.data(groupBins)
|
146
|
+
.enter()
|
147
|
+
.append('g')
|
148
|
+
.attr('transform', d => `translate(${xGroup(d.key)},0)`)
|
149
|
+
.style('fill', d => colorScale.range[groupNames.indexOf(d.key)])
|
150
|
+
.append('path')
|
151
|
+
.datum(d => d.value)
|
152
|
+
.style('stroke', 'none')
|
153
|
+
.attr('d', d => area(d));
|
154
|
+
// Whiskers
|
155
|
+
const whiskerGroups = g.selectAll('whiskers')
|
156
|
+
.data(groupedSummaries)
|
157
|
+
.enter()
|
158
|
+
.append('g')
|
159
|
+
.attr('transform', d => `translate(${xGroup(d.key)},0)`);
|
160
|
+
whiskerGroups.append('line')
|
161
|
+
.datum(d => d.value)
|
162
|
+
.attr('stroke', rectColor)
|
163
|
+
.attr('x1', xGroup.bandwidth() / 2)
|
164
|
+
.attr('x2', xGroup.bandwidth() / 2)
|
165
|
+
.attr('y1', d => y(d.quartiles[0]))
|
166
|
+
.attr('y2', d => y(d.quartiles[2]))
|
167
|
+
.attr('stroke-width', 2);
|
168
|
+
whiskerGroups.append('line')
|
169
|
+
.datum(d => d.value)
|
170
|
+
.attr('stroke', rectColor)
|
171
|
+
.attr('x1', xGroup.bandwidth() / 2 - (jitter ? 0 : 4))
|
172
|
+
.attr('x2', xGroup.bandwidth() / 2 + 4)
|
173
|
+
.attr('y1', d => y(d.quartiles[1]))
|
174
|
+
.attr('y2', d => y(d.quartiles[1]))
|
175
|
+
.attr('stroke-width', 2);
|
176
|
+
// Jittered points
|
177
|
+
if (jitter) {
|
178
|
+
groupedData.forEach(({ key, value }) => {
|
179
|
+
const groupG = g.append('g');
|
180
|
+
groupG.selectAll('point')
|
181
|
+
.data(value)
|
182
|
+
.enter()
|
183
|
+
.append('circle')
|
184
|
+
.attr('transform', `translate(${xGroup(key)},0)`)
|
185
|
+
.style('stroke', 'none')
|
186
|
+
.style('fill', 'silver')
|
187
|
+
.style('opacity', '0.1')
|
188
|
+
.attr('cx', () => 5 + Math.random() * ((xGroup.bandwidth() / 2) - 10))
|
189
|
+
.attr('cy', d => y(d))
|
190
|
+
.attr('r', 2);
|
191
|
+
});
|
192
|
+
}
|
193
|
+
// Y-axis ticks
|
194
|
+
g
|
195
|
+
.append('g')
|
196
|
+
.attr('transform', `translate(${marginLeft},0)`)
|
197
|
+
.call(axisLeft(y))
|
198
|
+
.selectAll('text')
|
199
|
+
.style('font-size', '11px');
|
200
|
+
// X-axis ticks
|
201
|
+
g
|
202
|
+
.append('g')
|
203
|
+
.attr('transform', `translate(0,${innerHeight})`)
|
204
|
+
.style('font-size', '14px')
|
205
|
+
.call(axisBottom(xGroup))
|
206
|
+
.selectAll('text')
|
207
|
+
.style('font-size', '11px')
|
208
|
+
.attr('dx', '-6px')
|
209
|
+
.attr('dy', '6px')
|
210
|
+
.attr('transform', 'rotate(-45)')
|
211
|
+
.style('text-anchor', 'end');
|
212
|
+
// Y-axis title
|
213
|
+
g
|
214
|
+
.append('text')
|
215
|
+
.attr('text-anchor', 'middle')
|
216
|
+
.attr('x', -innerHeight / 2)
|
217
|
+
.attr('y', 15)
|
218
|
+
.attr('transform', 'rotate(-90)')
|
219
|
+
.text(yTitle)
|
220
|
+
.style('font-size', '12px')
|
221
|
+
.style('fill', 'white');
|
222
|
+
// X-axis title
|
223
|
+
g
|
224
|
+
.append('text')
|
225
|
+
.attr('text-anchor', 'middle')
|
226
|
+
.attr('x', marginLeft + innerWidth / 2)
|
227
|
+
.attr('y', height - 10)
|
228
|
+
.text(xTitle)
|
229
|
+
.style('font-size', '12px')
|
230
|
+
.style('fill', 'white');
|
231
|
+
}, [width, height, data, marginLeft, marginBottom, colors,
|
232
|
+
jitter, theme, yMinProp, marginTop, marginRight, featureType,
|
233
|
+
featureValueType, featureValueTransformName, yUnits, obsType,
|
234
|
+
maxCharactersForLabel,
|
235
|
+
]);
|
236
|
+
return (_jsx("svg", { ref: svgRef, style: {
|
237
|
+
top: 0,
|
238
|
+
left: 0,
|
239
|
+
width: `${width}px`,
|
240
|
+
height: `${height}px`,
|
241
|
+
position: 'relative',
|
242
|
+
} }));
|
248
243
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"CellSetExpressionPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlotSubscriber.js"],"names":[],"mappings":"AAiGA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;
|
1
|
+
{"version":3,"file":"CellSetExpressionPlotSubscriber.d.ts","sourceRoot":"","sources":["../src/CellSetExpressionPlotSubscriber.js"],"names":[],"mappings":"AAiGA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;gBAgIhB"}
|
@@ -77,7 +77,7 @@ function useExpressionByCellSet(expressionData, obsIndex, cellSets, additionalCe
|
|
77
77
|
* @param {string} props.theme The name of the current Vitessce theme.
|
78
78
|
*/
|
79
79
|
export function CellSetExpressionPlotSubscriber(props) {
|
80
|
-
const { coordinationScopes, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, } = props;
|
80
|
+
const { coordinationScopes, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, jitter = false, yMin = null, yUnits = null, } = props;
|
81
81
|
const classes = useStyles();
|
82
82
|
const loaders = useLoaders();
|
83
83
|
// Get "props" from the coordination space.
|
@@ -102,10 +102,10 @@ export function CellSetExpressionPlotSubscriber(props) {
|
|
102
102
|
matrixIndicesUrls,
|
103
103
|
obsSetsUrls,
|
104
104
|
]);
|
105
|
-
const [expressionArr, setArr
|
105
|
+
const [expressionArr, setArr] = useExpressionByCellSet(expressionData, obsIndex, cellSets, additionalCellSets, geneSelection, cellSetSelection, cellSetColor, featureValueTransform, featureValueTransformCoefficient, theme);
|
106
106
|
const firstGeneSelected = geneSelection && geneSelection.length >= 1
|
107
107
|
? (featureLabelsMap?.get(geneSelection[0]) || geneSelection[0])
|
108
108
|
: null;
|
109
109
|
const selectedTransformName = transformOptions.find(o => o.value === featureValueTransform)?.name;
|
110
|
-
return (_jsx(TitleInfo, { title: `Expression by ${capitalize(obsType)} Set${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, options: (_jsx(CellSetExpressionPlotOptions, { featureValueTransform: featureValueTransform, setFeatureValueTransform: setFeatureValueTransform, featureValueTransformCoefficient: featureValueTransformCoefficient, setFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, transformOptions: transformOptions })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: expressionArr ? (_jsx(CellSetExpressionPlot, {
|
110
|
+
return (_jsx(TitleInfo, { title: `Expression by ${capitalize(obsType)} Set${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, options: (_jsx(CellSetExpressionPlotOptions, { featureValueTransform: featureValueTransform, setFeatureValueTransform: setFeatureValueTransform, featureValueTransformCoefficient: featureValueTransformCoefficient, setFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, transformOptions: transformOptions })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: expressionArr ? (_jsx(CellSetExpressionPlot, { yMin: yMin, yUnits: yUnits, jitter: jitter, colors: setArr, data: expressionArr, theme: theme, width: width, height: height, obsType: obsType, featureType: featureType, featureValueType: featureValueType, featureValueTransformName: selectedTransformName })) : (_jsxs("span", { children: ["Select a ", featureType, "."] })) }) }));
|
111
111
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"ExpressionHistogram.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogram.js"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"ExpressionHistogram.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogram.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH;IAd2B,aAAa,EAA7B,MAAM,EAAE;IAEQ,IAAI,EAApB,MAAM,EAAE;IAEM,KAAK,EAAnB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,WAAW,EAAzB,MAAM;IAGQ,YAAY,EAA1B,MAAM;gBAqGhB"}
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useEffect, useCallback } from 'react';
|
3
3
|
import { clamp, debounce } from 'lodash-es';
|
4
4
|
import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
|
5
|
+
import { capitalize, pluralize } from '@vitessce/utils';
|
5
6
|
/**
|
6
7
|
* We use debounce, so that onSelect is called only after the user has finished the selection.
|
7
8
|
* Due to vega-lite limitations, we cannot use the vega-lite signals to implement this.
|
@@ -27,11 +28,13 @@ import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
|
|
27
28
|
* By default, 50.
|
28
29
|
*/
|
29
30
|
export default function ExpressionHistogram(props) {
|
30
|
-
const { geneSelection, data, theme, width, height, marginRight = 90, marginBottom = 50, onSelect, } = props;
|
31
|
+
const { geneSelection, obsType, featureType, featureValueType, data, theme, width, height, marginRight = 90, marginBottom = 50, onSelect, } = props;
|
31
32
|
const [selectedRanges, setSelectedRanges] = useState([]);
|
33
|
+
const isExpression = (featureType === 'gene' && featureValueType === 'expression');
|
34
|
+
// eslint-disable-next-line no-nested-ternary
|
32
35
|
const xTitle = geneSelection && geneSelection.length >= 1
|
33
|
-
?
|
34
|
-
: 'Total
|
36
|
+
? (isExpression ? `Expression Value (${geneSelection[0]})` : `${geneSelection[0]}`)
|
37
|
+
: (isExpression ? 'Total Transcript Count' : 'Sum of Feature Values');
|
35
38
|
const spec = {
|
36
39
|
data: { values: data },
|
37
40
|
mark: 'bar',
|
@@ -45,7 +48,7 @@ export default function ExpressionHistogram(props) {
|
|
45
48
|
y: {
|
46
49
|
type: 'quantitative',
|
47
50
|
aggregate: 'count',
|
48
|
-
title:
|
51
|
+
title: `Number of ${capitalize(pluralize(obsType, 2))}`,
|
49
52
|
},
|
50
53
|
color: { value: 'gray' },
|
51
54
|
opacity: {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"ExpressionHistogramSubscriber.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogramSubscriber.js"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;
|
1
|
+
{"version":3,"file":"ExpressionHistogramSubscriber.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogramSubscriber.js"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;gBA2HhB"}
|
@@ -44,8 +44,7 @@ export function ExpressionHistogramSubscriber(props) {
|
|
44
44
|
return obsIndex.map((cellId, cellIndex) => {
|
45
45
|
const value = expressionData[0][cellIndex];
|
46
46
|
// Create new cellColors map based on the selected gene.
|
47
|
-
const
|
48
|
-
const newItem = { value: normValue, gene: firstGeneSelected, cellId };
|
47
|
+
const newItem = { value, gene: firstGeneSelected, cellId };
|
49
48
|
return newItem;
|
50
49
|
});
|
51
50
|
}
|
@@ -54,7 +53,7 @@ export function ExpressionHistogramSubscriber(props) {
|
|
54
53
|
return obsIndex.map((cellId, cellIndex) => {
|
55
54
|
const values = obsFeatureMatrix.data
|
56
55
|
.subarray(cellIndex * numGenes, (cellIndex + 1) * numGenes);
|
57
|
-
const sumValue = sum(values)
|
56
|
+
const sumValue = sum(values);
|
58
57
|
const newItem = { value: sumValue, gene: null, cellId };
|
59
58
|
return newItem;
|
60
59
|
});
|
@@ -68,5 +67,5 @@ export function ExpressionHistogramSubscriber(props) {
|
|
68
67
|
}, [additionalCellSets, cellSetColor, data, setAdditionalCellSets,
|
69
68
|
setCellColorEncoding, setCellSetColor, setCellSetSelection, firstGeneSelected,
|
70
69
|
]);
|
71
|
-
return (_jsx(TitleInfo, { title: `
|
70
|
+
return (_jsx(TitleInfo, { title: `Histogram${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(ExpressionHistogram, { geneSelection: geneSelection, obsType: obsType, featureType: featureType, featureValueType: featureValueType, onSelect: onSelect, data: data, theme: theme, width: width, height: height }) }) }));
|
72
71
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitessce/statistical-plots",
|
3
|
-
"version": "3.3.
|
3
|
+
"version": "3.3.5",
|
4
4
|
"author": "Gehlenborg Lab",
|
5
5
|
"homepage": "http://vitessce.io",
|
6
6
|
"repository": {
|
@@ -18,13 +18,18 @@
|
|
18
18
|
"dependencies": {
|
19
19
|
"@material-ui/core": "~4.12.3",
|
20
20
|
"d3-array": "^2.4.0",
|
21
|
+
"d3-scale": "^4.0.0",
|
22
|
+
"d3-shape": "^3.2.0",
|
23
|
+
"d3-axis": "^3.0.0",
|
24
|
+
"d3-selection": "^3.0.0",
|
25
|
+
"vega-scale": "^6.0.0",
|
21
26
|
"lodash-es": "^4.17.21",
|
22
27
|
"react-aria": "^3.28.0",
|
23
|
-
"@vitessce/
|
24
|
-
"@vitessce/
|
25
|
-
"@vitessce/utils": "3.3.
|
26
|
-
"@vitessce/vega": "3.3.
|
27
|
-
"@vitessce/vit-s": "3.3.
|
28
|
+
"@vitessce/sets-utils": "3.3.5",
|
29
|
+
"@vitessce/constants-internal": "3.3.5",
|
30
|
+
"@vitessce/utils": "3.3.5",
|
31
|
+
"@vitessce/vega": "3.3.5",
|
32
|
+
"@vitessce/vit-s": "3.3.5"
|
28
33
|
},
|
29
34
|
"devDependencies": {
|
30
35
|
"react": "^18.0.0",
|