@vitessce/statistical-plots 3.4.5 → 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
@@ -0,0 +1,170 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
2
|
+
import { InternMap } from 'internmap';
|
3
|
+
import { scaleLinear } from 'd3-scale';
|
4
|
+
import {
|
5
|
+
bin,
|
6
|
+
min,
|
7
|
+
max,
|
8
|
+
mean as d3_mean,
|
9
|
+
deviation as d3_deviation,
|
10
|
+
ascending as d3_ascending,
|
11
|
+
quantileSorted,
|
12
|
+
} from 'd3-array';
|
13
|
+
|
14
|
+
// Reference: https://github.com/d3/d3-array/issues/180#issuecomment-851378012
|
15
|
+
function summarize(iterable, keepZeros) {
|
16
|
+
const values = iterable
|
17
|
+
.filter(d => keepZeros || d !== 0.0)
|
18
|
+
.sort(d3_ascending);
|
19
|
+
const minVal = values[0];
|
20
|
+
const maxVal = values[values.length - 1];
|
21
|
+
const q1 = quantileSorted(values, 0.25);
|
22
|
+
const q2 = quantileSorted(values, 0.5);
|
23
|
+
const q3 = quantileSorted(values, 0.75);
|
24
|
+
const iqr = q3 - q1; // interquartile range
|
25
|
+
const r0 = Math.max(minVal, q1 - iqr * 1.5);
|
26
|
+
const r1 = Math.min(maxVal, q3 + iqr * 1.5);
|
27
|
+
let i = -1;
|
28
|
+
while (values[++i] < r0);
|
29
|
+
const w0 = values[i];
|
30
|
+
while (values[++i] <= r1);
|
31
|
+
const w1 = values[i - 1];
|
32
|
+
|
33
|
+
// Chauvenet
|
34
|
+
// Reference: https://en.wikipedia.org/wiki/Chauvenet%27s_criterion
|
35
|
+
const mean = d3_mean(values);
|
36
|
+
const stdv = d3_deviation(values);
|
37
|
+
const c0 = mean - 3 * stdv;
|
38
|
+
const c1 = mean + 3 * stdv;
|
39
|
+
|
40
|
+
return {
|
41
|
+
quartiles: [q1, q2, q3],
|
42
|
+
range: [r0, r1],
|
43
|
+
whiskers: [w0, w1],
|
44
|
+
chauvenetRange: [c0, c1],
|
45
|
+
nonOutliers: values.filter(v => c0 <= v && v <= c1),
|
46
|
+
};
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Supports three-level stratified input
|
51
|
+
* (cell set, sample set, gene).
|
52
|
+
* @param {*} stratifiedResult
|
53
|
+
* @param {*} posThreshold
|
54
|
+
* @returns
|
55
|
+
*/
|
56
|
+
export function dotStratifiedExpressionData(
|
57
|
+
stratifiedResult, posThreshold,
|
58
|
+
) {
|
59
|
+
const result = new InternMap([], JSON.stringify);
|
60
|
+
([...stratifiedResult.keys()]).forEach((cellSetKey) => {
|
61
|
+
result.set(cellSetKey, new InternMap([], JSON.stringify));
|
62
|
+
([...stratifiedResult.get(cellSetKey).keys()]).forEach((sampleSetKey) => {
|
63
|
+
result.get(cellSetKey).set(sampleSetKey, new InternMap([], JSON.stringify));
|
64
|
+
|
65
|
+
const allGenes = stratifiedResult.get(cellSetKey).get(sampleSetKey);
|
66
|
+
|
67
|
+
([...allGenes.keys()]).forEach((geneKey) => {
|
68
|
+
const values = allGenes.get(geneKey);
|
69
|
+
|
70
|
+
const exprMean = d3_mean(values);
|
71
|
+
const numPos = values.reduce((acc, val) => (val > posThreshold ? acc + 1 : acc), 0);
|
72
|
+
const fracPos = numPos / values.length;
|
73
|
+
|
74
|
+
const dotSummary = {
|
75
|
+
meanExpInGroup: exprMean,
|
76
|
+
fracPosInGroup: fracPos,
|
77
|
+
};
|
78
|
+
|
79
|
+
result.get(cellSetKey).get(sampleSetKey).set(geneKey, dotSummary);
|
80
|
+
});
|
81
|
+
});
|
82
|
+
});
|
83
|
+
return result;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Supports two-level stratified input
|
88
|
+
* (cell set, sample set).
|
89
|
+
* @param {*} stratifiedResult
|
90
|
+
* @param {*} keepZeros
|
91
|
+
* @returns
|
92
|
+
*/
|
93
|
+
export function summarizeStratifiedExpressionData(
|
94
|
+
stratifiedResult, keepZeros,
|
95
|
+
) {
|
96
|
+
const summarizedResult = new InternMap([], JSON.stringify);
|
97
|
+
|
98
|
+
([...stratifiedResult.keys()]).forEach((cellSetKey) => {
|
99
|
+
summarizedResult.set(cellSetKey, new InternMap([], JSON.stringify));
|
100
|
+
([...stratifiedResult.get(cellSetKey).keys()]).forEach((sampleSetKey) => {
|
101
|
+
const values = stratifiedResult.get(cellSetKey).get(sampleSetKey);
|
102
|
+
const summary = summarize(values, keepZeros);
|
103
|
+
summarizedResult.get(cellSetKey).set(sampleSetKey, summary);
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
return summarizedResult;
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Supports two-level summarized input
|
112
|
+
* (cell set, sample set),
|
113
|
+
* the output from summarizeStratifiedExpressionData.
|
114
|
+
* @param {*} summarizedResult
|
115
|
+
* @param {*} binCount
|
116
|
+
* @param {*} yMinProp
|
117
|
+
* @returns
|
118
|
+
*/
|
119
|
+
export function histogramStratifiedExpressionData(
|
120
|
+
summarizedResult, binCount, yMinProp,
|
121
|
+
) {
|
122
|
+
const groupSummaries = ([...summarizedResult.keys()]).map(cellSetKey => ({
|
123
|
+
key: cellSetKey,
|
124
|
+
value: ([...summarizedResult.get(cellSetKey).keys()]).map(sampleSetKey => ({
|
125
|
+
key: sampleSetKey,
|
126
|
+
value: summarizedResult.get(cellSetKey).get(sampleSetKey),
|
127
|
+
})),
|
128
|
+
}));
|
129
|
+
|
130
|
+
const groupData = groupSummaries
|
131
|
+
.map(({ key, value }) => ({
|
132
|
+
key,
|
133
|
+
value: value.map(({ key: subKey, value: subValue }) => (
|
134
|
+
{ key: subKey, value: subValue.nonOutliers }
|
135
|
+
)),
|
136
|
+
}));
|
137
|
+
const trimmedData = groupData.map(kv => kv.value.map(subKv => subKv.value).flat()).flat();
|
138
|
+
|
139
|
+
const yMin = (yMinProp === null ? Math.min(0, min(trimmedData)) : yMinProp);
|
140
|
+
|
141
|
+
// For the y domain, use the yMin prop
|
142
|
+
// to support a use case such as 'Aspect Ratio',
|
143
|
+
// where the domain minimum should be 1 rather than 0.
|
144
|
+
const y = scaleLinear()
|
145
|
+
.domain([yMin, max(trimmedData)]);
|
146
|
+
|
147
|
+
const histogram = bin()
|
148
|
+
.thresholds(y.ticks(binCount))
|
149
|
+
.domain(y.domain());
|
150
|
+
|
151
|
+
const groupBins = groupData.map(kv => ({ key: kv.key,
|
152
|
+
value: kv.value.map(subKv => (
|
153
|
+
{ key: subKv.key, value: histogram(subKv.value) }
|
154
|
+
)) }));
|
155
|
+
const groupBinsMax = max(groupBins
|
156
|
+
.flatMap(d => d.value.flatMap(subKv => subKv.value.map(v => v.length))));
|
157
|
+
|
158
|
+
return {
|
159
|
+
// Array of [{ key, value: [
|
160
|
+
// { key, value: {
|
161
|
+
// quartiles, range, whiskers, chauvenetRange, nonOutliers }
|
162
|
+
// }
|
163
|
+
// ] }]
|
164
|
+
groupSummaries,
|
165
|
+
groupData, // Array of [{ key, value: [{ key, value: nonOutliers }] }]
|
166
|
+
groupBins, // Array of [{ key, value: [{ key, value: histogram(nonOutliers) }] }]
|
167
|
+
groupBinsMax, // Number
|
168
|
+
y, // d3.scaleLinear without a range set
|
169
|
+
};
|
170
|
+
}
|
@@ -0,0 +1,116 @@
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
2
|
+
import {
|
3
|
+
stratifyExpressionData,
|
4
|
+
aggregateStratifiedExpressionData,
|
5
|
+
} from '@vitessce/sets-utils';
|
6
|
+
import {
|
7
|
+
summarizeStratifiedExpressionData,
|
8
|
+
histogramStratifiedExpressionData,
|
9
|
+
} from './expr-hooks.js';
|
10
|
+
|
11
|
+
describe('Utility functions for processing expression data for statistical plots', () => {
|
12
|
+
describe('summarizeStratifiedExpressionData function', () => {
|
13
|
+
it('computes summarized information accurately', () => {
|
14
|
+
const sampleEdges = new Map([
|
15
|
+
['cell1-1', 'donor1'],
|
16
|
+
['cell1-2', 'donor1'],
|
17
|
+
['cell1-3', 'donor1'],
|
18
|
+
['cell1-4', 'donor1'],
|
19
|
+
|
20
|
+
['cell2-1', 'donor2'],
|
21
|
+
['cell2-2', 'donor2'],
|
22
|
+
['cell2-3', 'donor2'],
|
23
|
+
['cell2-4', 'donor2'],
|
24
|
+
]);
|
25
|
+
const sampleSets = {
|
26
|
+
tree: [
|
27
|
+
{
|
28
|
+
name: 'Clinical groups',
|
29
|
+
children: [
|
30
|
+
{
|
31
|
+
name: 'AKI',
|
32
|
+
set: [['donor1', null]],
|
33
|
+
},
|
34
|
+
{
|
35
|
+
name: 'CKD',
|
36
|
+
set: [['donor2', null]],
|
37
|
+
},
|
38
|
+
],
|
39
|
+
},
|
40
|
+
],
|
41
|
+
};
|
42
|
+
const sampleSetSelection = [
|
43
|
+
['Clinical groups', 'AKI'],
|
44
|
+
['Clinical groups', 'CKD'],
|
45
|
+
];
|
46
|
+
const expressionData = [
|
47
|
+
// Gene 1
|
48
|
+
[10, 20, 30, 40, 11, 21, 31, 41],
|
49
|
+
];
|
50
|
+
const obsIndex = ['cell1-1', 'cell1-2', 'cell1-3', 'cell1-4', 'cell2-1', 'cell2-2', 'cell2-3', 'cell2-4'];
|
51
|
+
const mergedCellSets = {
|
52
|
+
tree: [
|
53
|
+
{
|
54
|
+
name: 'Cell type',
|
55
|
+
children: [
|
56
|
+
{
|
57
|
+
name: 'T cell',
|
58
|
+
set: [['cell1-1', null], ['cell1-3', null], ['cell2-1', null], ['cell2-3', null]],
|
59
|
+
},
|
60
|
+
{
|
61
|
+
name: 'B cell',
|
62
|
+
set: [['cell1-2', null], ['cell1-4', null], ['cell2-2', null], ['cell2-4', null]],
|
63
|
+
},
|
64
|
+
],
|
65
|
+
},
|
66
|
+
],
|
67
|
+
};
|
68
|
+
const geneSelection = [
|
69
|
+
'Gene 1',
|
70
|
+
];
|
71
|
+
const cellSetSelection = [
|
72
|
+
['Cell type', 'T cell'],
|
73
|
+
['Cell type', 'B cell'],
|
74
|
+
];
|
75
|
+
const cellSetColor = [
|
76
|
+
{ set: ['Cell type', 'T cell'], color: [255, 0, 0] },
|
77
|
+
{ set: ['Cell type', 'B cell'], color: [0, 255, 0] },
|
78
|
+
];
|
79
|
+
const featureValueTransform = null;
|
80
|
+
const featureValueTransformCoefficient = 1;
|
81
|
+
|
82
|
+
const [result] = stratifyExpressionData(
|
83
|
+
sampleEdges, sampleSets, sampleSetSelection,
|
84
|
+
expressionData, obsIndex, mergedCellSets,
|
85
|
+
geneSelection, cellSetSelection, cellSetColor,
|
86
|
+
featureValueTransform, featureValueTransformCoefficient,
|
87
|
+
);
|
88
|
+
const aggregateData = aggregateStratifiedExpressionData(
|
89
|
+
result, geneSelection,
|
90
|
+
);
|
91
|
+
const summaryResult = summarizeStratifiedExpressionData(aggregateData, true);
|
92
|
+
|
93
|
+
expect([...summaryResult.keys()]).toEqual([['Cell type', 'T cell'], ['Cell type', 'B cell']]);
|
94
|
+
expect([...summaryResult.get(['Cell type', 'T cell']).keys()]).toEqual([['Clinical groups', 'AKI'], ['Clinical groups', 'CKD']]);
|
95
|
+
expect(Object.keys(summaryResult.get(['Cell type', 'T cell']).get(['Clinical groups', 'AKI']))).toEqual([
|
96
|
+
'quartiles',
|
97
|
+
'range',
|
98
|
+
'whiskers',
|
99
|
+
'chauvenetRange',
|
100
|
+
'nonOutliers',
|
101
|
+
]);
|
102
|
+
|
103
|
+
const histogramResult = histogramStratifiedExpressionData(summaryResult, 16, null);
|
104
|
+
|
105
|
+
expect(Object.keys(histogramResult)).toEqual([
|
106
|
+
'groupSummaries',
|
107
|
+
'groupData',
|
108
|
+
'groupBins',
|
109
|
+
'groupBinsMax',
|
110
|
+
'y',
|
111
|
+
]);
|
112
|
+
expect(histogramResult.groupSummaries.map(d => d.key)).toEqual([['Cell type', 'T cell'], ['Cell type', 'B cell']]);
|
113
|
+
expect(histogramResult.groupBinsMax).toEqual(1);
|
114
|
+
});
|
115
|
+
});
|
116
|
+
});
|
package/src/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';
|