@jbrowse/plugin-variants 3.0.5 → 3.1.0
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/MultiLinearVariantDisplay/components/Crosshair.js +1 -1
- package/dist/MultiLinearVariantMatrixDisplay/components/Crosshair.js +1 -1
- package/dist/MultiLinearVariantMatrixRenderer/makeImageData.d.ts +1 -0
- package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +1 -1
- package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.js +33 -10
- package/dist/VcfAdapter/configSchema.js +14 -1
- package/dist/VcfTabixAdapter/configSchema.js +24 -2
- package/dist/shared/components/ClusterDialog.js +7 -3
- package/dist/shared/minorAlleleFrequencyUtils.d.ts +3 -5
- package/dist/shared/minorAlleleFrequencyUtils.js +13 -8
- package/esm/MultiLinearVariantDisplay/components/Crosshair.js +1 -1
- package/esm/MultiLinearVariantMatrixDisplay/components/Crosshair.js +1 -1
- package/esm/MultiLinearVariantMatrixRenderer/makeImageData.d.ts +1 -0
- package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +1 -1
- package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.js +33 -10
- package/esm/VcfAdapter/configSchema.js +14 -1
- package/esm/VcfTabixAdapter/configSchema.js +24 -2
- package/esm/shared/components/ClusterDialog.js +8 -4
- package/esm/shared/minorAlleleFrequencyUtils.d.ts +3 -5
- package/esm/shared/minorAlleleFrequencyUtils.js +13 -8
- package/package.json +6 -6
|
@@ -19,21 +19,19 @@ class MultiVariantGetGenotypeMatrix extends RpcMethodTypeWithFiltersAndRenameReg
|
|
|
19
19
|
const { sources, minorAlleleFrequencyFilter, regions, adapterConfig, sessionId, } = deserializedArgs;
|
|
20
20
|
const adapter = await (0, dataAdapterCache_1.getAdapter)(pm, sessionId, adapterConfig);
|
|
21
21
|
const dataAdapter = adapter.dataAdapter;
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const feats = await (0, rxjs_1.firstValueFrom)(dataAdapter
|
|
23
|
+
.getFeaturesInMultipleRegions(regions, deserializedArgs)
|
|
24
|
+
.pipe((0, rxjs_1.toArray)()));
|
|
24
25
|
const genotypeFactor = new Set();
|
|
25
26
|
const mafs = (0, minorAlleleFrequencyUtils_1.getFeaturesThatPassMinorAlleleFrequencyFilter)(feats, minorAlleleFrequencyFilter);
|
|
26
|
-
for (const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const s = samp[name];
|
|
30
|
-
genotypeFactor.add(s);
|
|
27
|
+
for (const { alleleCounts } of mafs) {
|
|
28
|
+
for (const alt of alleleCounts.keys()) {
|
|
29
|
+
genotypeFactor.add(alt);
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
|
-
const genotypeFactorMap = Object.fromEntries([...genotypeFactor].map((type, idx) => [type, idx]));
|
|
34
32
|
const rows = {};
|
|
35
33
|
for (const { feature } of mafs) {
|
|
36
|
-
const
|
|
34
|
+
const genotypes = feature.get('genotypes');
|
|
37
35
|
for (const { name } of sources) {
|
|
38
36
|
if (!rows[name]) {
|
|
39
37
|
rows[name] = {
|
|
@@ -41,7 +39,32 @@ class MultiVariantGetGenotypeMatrix extends RpcMethodTypeWithFiltersAndRenameReg
|
|
|
41
39
|
genotypes: [],
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
|
-
|
|
42
|
+
const val = genotypes[name];
|
|
43
|
+
const alleles = val.split(/[/|]/);
|
|
44
|
+
let genotypeStatus = '0';
|
|
45
|
+
let nonRefCount = 0;
|
|
46
|
+
let uncalledCount = 0;
|
|
47
|
+
for (const l of alleles) {
|
|
48
|
+
if (l === '.') {
|
|
49
|
+
uncalledCount++;
|
|
50
|
+
}
|
|
51
|
+
else if (l !== '0') {
|
|
52
|
+
nonRefCount++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (uncalledCount === alleles.length) {
|
|
56
|
+
genotypeStatus = '-1';
|
|
57
|
+
}
|
|
58
|
+
else if (nonRefCount === 0) {
|
|
59
|
+
genotypeStatus = '0';
|
|
60
|
+
}
|
|
61
|
+
else if (nonRefCount === alleles.length) {
|
|
62
|
+
genotypeStatus = '2';
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
genotypeStatus = '1';
|
|
66
|
+
}
|
|
67
|
+
rows[name].genotypes.push(genotypeStatus);
|
|
45
68
|
}
|
|
46
69
|
}
|
|
47
70
|
return rows;
|
|
@@ -18,5 +18,18 @@ const VcfAdapter = (0, configuration_1.ConfigurationSchema)('VcfAdapter', {
|
|
|
18
18
|
locationType: 'UriLocation',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
|
-
}, {
|
|
21
|
+
}, {
|
|
22
|
+
explicitlyTyped: true,
|
|
23
|
+
preProcessSnapshot: snap => {
|
|
24
|
+
return snap.uri
|
|
25
|
+
? {
|
|
26
|
+
...snap,
|
|
27
|
+
vcfLocation: {
|
|
28
|
+
uri: snap.uri,
|
|
29
|
+
baseUri: snap.baseUri,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
: snap;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
22
35
|
exports.default = VcfAdapter;
|
|
@@ -6,7 +6,10 @@ function x() { }
|
|
|
6
6
|
const VcfTabixAdapter = (0, configuration_1.ConfigurationSchema)('VcfTabixAdapter', {
|
|
7
7
|
vcfGzLocation: {
|
|
8
8
|
type: 'fileLocation',
|
|
9
|
-
defaultValue: {
|
|
9
|
+
defaultValue: {
|
|
10
|
+
uri: '/path/to/my.vcf.gz',
|
|
11
|
+
locationType: 'UriLocation',
|
|
12
|
+
},
|
|
10
13
|
},
|
|
11
14
|
index: (0, configuration_1.ConfigurationSchema)('VcfIndex', {
|
|
12
15
|
indexType: {
|
|
@@ -30,5 +33,24 @@ const VcfTabixAdapter = (0, configuration_1.ConfigurationSchema)('VcfTabixAdapte
|
|
|
30
33
|
locationType: 'UriLocation',
|
|
31
34
|
},
|
|
32
35
|
},
|
|
33
|
-
}, {
|
|
36
|
+
}, {
|
|
37
|
+
explicitlyTyped: true,
|
|
38
|
+
preProcessSnapshot: snap => {
|
|
39
|
+
return snap.uri
|
|
40
|
+
? {
|
|
41
|
+
...snap,
|
|
42
|
+
vcfGzLocation: {
|
|
43
|
+
uri: snap.uri,
|
|
44
|
+
baseUri: snap.baseUri,
|
|
45
|
+
},
|
|
46
|
+
index: {
|
|
47
|
+
location: {
|
|
48
|
+
uri: `${snap.uri}.tbi`,
|
|
49
|
+
baseUri: snap.baseUri,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
: snap;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
34
56
|
exports.default = VcfTabixAdapter;
|
|
@@ -29,6 +29,7 @@ function ClusterDialog({ model, handleClose, }) {
|
|
|
29
29
|
const [results, setResults] = (0, react_1.useState)();
|
|
30
30
|
const [error, setError] = (0, react_1.useState)();
|
|
31
31
|
const [paste, setPaste] = (0, react_1.useState)('');
|
|
32
|
+
const [useCompleteMethod, setUseCompleteMethod] = (0, react_1.useState)(false);
|
|
32
33
|
(0, react_1.useEffect)(() => {
|
|
33
34
|
;
|
|
34
35
|
(async () => {
|
|
@@ -50,11 +51,12 @@ function ClusterDialog({ model, handleClose, }) {
|
|
|
50
51
|
}));
|
|
51
52
|
const entries = Object.values(ret);
|
|
52
53
|
const keys = Object.keys(ret);
|
|
54
|
+
const clusterMethod = useCompleteMethod ? 'complete' : 'single';
|
|
53
55
|
const text = `try(library(fastcluster), silent=TRUE)
|
|
54
56
|
inputMatrix<-matrix(c(${entries.map(val => val.genotypes.join(',')).join(',\n')}
|
|
55
57
|
),nrow=${entries.length},byrow=TRUE)
|
|
56
58
|
rownames(inputMatrix)<-c(${keys.map(key => `'${key}'`).join(',')})
|
|
57
|
-
resultClusters<-hclust(dist(inputMatrix), method='
|
|
59
|
+
resultClusters<-hclust(dist(inputMatrix), method='${clusterMethod}')
|
|
58
60
|
cat(resultClusters$order,sep='\\n')`;
|
|
59
61
|
setResults(text);
|
|
60
62
|
}
|
|
@@ -65,14 +67,16 @@ cat(resultClusters$order,sep='\\n')`;
|
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
})();
|
|
68
|
-
}, [model]);
|
|
70
|
+
}, [model, useCompleteMethod]);
|
|
69
71
|
return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { open: true, title: "Cluster by genotype", onClose: handleClose, children: [(0, jsx_runtime_1.jsx)(material_1.DialogContent, { children: (0, jsx_runtime_1.jsxs)("div", { className: classes.mgap, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { children: "This page will produce an R script that will perform hierarchical clustering on the visible genotype data using `hclust`." }), (0, jsx_runtime_1.jsx)(material_1.Typography, { children: "You can then paste the results in this form to specify the row ordering." }), results ? ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsxs)("div", { children: ["Step 1:", ' ', (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
70
72
|
(0, file_saver_1.saveAs)(new Blob([results || ''], {
|
|
71
73
|
type: 'text/plain;charset=utf-8',
|
|
72
74
|
}), 'cluster.R');
|
|
73
75
|
}, children: "Download Rscript" }), ' ', "or", ' ', (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
74
76
|
(0, copy_to_clipboard_1.default)(results || '');
|
|
75
|
-
}, children: "Copy Rscript to clipboard" }), (0, jsx_runtime_1.jsx)(
|
|
77
|
+
}, children: "Copy Rscript to clipboard" }), (0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Checkbox, { checked: useCompleteMethod, onChange: e => {
|
|
78
|
+
setUseCompleteMethod(e.target.checked);
|
|
79
|
+
} }), label: "Use 'complete' linkage method instead of 'single'" }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(material_1.TextField, { multiline: true, fullWidth: true, variant: "outlined", placeholder: "Step 2. Paste results from Rscript here (sequence of numbers, one per line, specifying the new ordering)", rows: 10, value: paste, onChange: event => {
|
|
76
80
|
setPaste(event.target.value);
|
|
77
81
|
}, slotProps: {
|
|
78
82
|
input: {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { Feature } from '@jbrowse/core/util';
|
|
2
2
|
export declare function findSecondLargestNumber(arr: Iterable<number>): number;
|
|
3
|
-
export declare function calculateAlleleCounts(feat: Feature):
|
|
4
|
-
alleleCounts: Map<any, any>;
|
|
5
|
-
mostFrequentAlt: any;
|
|
6
|
-
};
|
|
3
|
+
export declare function calculateAlleleCounts(feat: Feature): Map<any, any>;
|
|
7
4
|
export declare function calculateMinorAlleleFrequency(alleleCounts: Map<string, number>): number;
|
|
8
|
-
export declare function getFeaturesThatPassMinorAlleleFrequencyFilter(feats: Iterable<Feature>, minorAlleleFrequencyFilter: number): {
|
|
5
|
+
export declare function getFeaturesThatPassMinorAlleleFrequencyFilter(feats: Iterable<Feature>, minorAlleleFrequencyFilter: number, lengthCutoffFilter?: number): {
|
|
9
6
|
feature: Feature;
|
|
10
7
|
mostFrequentAlt: string;
|
|
8
|
+
alleleCounts: Map<string, number>;
|
|
11
9
|
}[];
|
|
@@ -28,6 +28,13 @@ function calculateAlleleCounts(feat) {
|
|
|
28
28
|
alleleCounts.set(allele, (alleleCounts.get(allele) || 0) + 1);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
return alleleCounts;
|
|
32
|
+
}
|
|
33
|
+
function calculateMinorAlleleFrequency(alleleCounts) {
|
|
34
|
+
return (findSecondLargestNumber(alleleCounts.values()) /
|
|
35
|
+
((0, util_1.sum)(alleleCounts.values()) || 1));
|
|
36
|
+
}
|
|
37
|
+
function getMostFrequentAlt(alleleCounts) {
|
|
31
38
|
let mostFrequentAlt;
|
|
32
39
|
let max = 0;
|
|
33
40
|
for (const [alt, altCount] of alleleCounts.entries()) {
|
|
@@ -38,22 +45,20 @@ function calculateAlleleCounts(feat) {
|
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
function calculateMinorAlleleFrequency(alleleCounts) {
|
|
44
|
-
return (findSecondLargestNumber(alleleCounts.values()) /
|
|
45
|
-
((0, util_1.sum)(alleleCounts.values()) || 1));
|
|
48
|
+
return mostFrequentAlt;
|
|
46
49
|
}
|
|
47
|
-
function getFeaturesThatPassMinorAlleleFrequencyFilter(feats, minorAlleleFrequencyFilter) {
|
|
50
|
+
function getFeaturesThatPassMinorAlleleFrequencyFilter(feats, minorAlleleFrequencyFilter, lengthCutoffFilter = 10) {
|
|
48
51
|
const results = [];
|
|
49
52
|
for (const feature of feats) {
|
|
50
|
-
if (feature.get('end') - feature.get('start') <=
|
|
51
|
-
const
|
|
53
|
+
if (feature.get('end') - feature.get('start') <= lengthCutoffFilter) {
|
|
54
|
+
const alleleCounts = calculateAlleleCounts(feature);
|
|
52
55
|
if (calculateMinorAlleleFrequency(alleleCounts) >=
|
|
53
56
|
minorAlleleFrequencyFilter) {
|
|
57
|
+
const mostFrequentAlt = getMostFrequentAlt(alleleCounts);
|
|
54
58
|
results.push({
|
|
55
59
|
feature,
|
|
56
60
|
mostFrequentAlt,
|
|
61
|
+
alleleCounts,
|
|
57
62
|
});
|
|
58
63
|
}
|
|
59
64
|
}
|
|
@@ -13,21 +13,19 @@ export class MultiVariantGetGenotypeMatrix extends RpcMethodTypeWithFiltersAndRe
|
|
|
13
13
|
const { sources, minorAlleleFrequencyFilter, regions, adapterConfig, sessionId, } = deserializedArgs;
|
|
14
14
|
const adapter = await getAdapter(pm, sessionId, adapterConfig);
|
|
15
15
|
const dataAdapter = adapter.dataAdapter;
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const feats = await firstValueFrom(dataAdapter
|
|
17
|
+
.getFeaturesInMultipleRegions(regions, deserializedArgs)
|
|
18
|
+
.pipe(toArray()));
|
|
18
19
|
const genotypeFactor = new Set();
|
|
19
20
|
const mafs = getFeaturesThatPassMinorAlleleFrequencyFilter(feats, minorAlleleFrequencyFilter);
|
|
20
|
-
for (const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const s = samp[name];
|
|
24
|
-
genotypeFactor.add(s);
|
|
21
|
+
for (const { alleleCounts } of mafs) {
|
|
22
|
+
for (const alt of alleleCounts.keys()) {
|
|
23
|
+
genotypeFactor.add(alt);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
|
-
const genotypeFactorMap = Object.fromEntries([...genotypeFactor].map((type, idx) => [type, idx]));
|
|
28
26
|
const rows = {};
|
|
29
27
|
for (const { feature } of mafs) {
|
|
30
|
-
const
|
|
28
|
+
const genotypes = feature.get('genotypes');
|
|
31
29
|
for (const { name } of sources) {
|
|
32
30
|
if (!rows[name]) {
|
|
33
31
|
rows[name] = {
|
|
@@ -35,7 +33,32 @@ export class MultiVariantGetGenotypeMatrix extends RpcMethodTypeWithFiltersAndRe
|
|
|
35
33
|
genotypes: [],
|
|
36
34
|
};
|
|
37
35
|
}
|
|
38
|
-
|
|
36
|
+
const val = genotypes[name];
|
|
37
|
+
const alleles = val.split(/[/|]/);
|
|
38
|
+
let genotypeStatus = '0';
|
|
39
|
+
let nonRefCount = 0;
|
|
40
|
+
let uncalledCount = 0;
|
|
41
|
+
for (const l of alleles) {
|
|
42
|
+
if (l === '.') {
|
|
43
|
+
uncalledCount++;
|
|
44
|
+
}
|
|
45
|
+
else if (l !== '0') {
|
|
46
|
+
nonRefCount++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (uncalledCount === alleles.length) {
|
|
50
|
+
genotypeStatus = '-1';
|
|
51
|
+
}
|
|
52
|
+
else if (nonRefCount === 0) {
|
|
53
|
+
genotypeStatus = '0';
|
|
54
|
+
}
|
|
55
|
+
else if (nonRefCount === alleles.length) {
|
|
56
|
+
genotypeStatus = '2';
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
genotypeStatus = '1';
|
|
60
|
+
}
|
|
61
|
+
rows[name].genotypes.push(genotypeStatus);
|
|
39
62
|
}
|
|
40
63
|
}
|
|
41
64
|
return rows;
|
|
@@ -16,5 +16,18 @@ const VcfAdapter = ConfigurationSchema('VcfAdapter', {
|
|
|
16
16
|
locationType: 'UriLocation',
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
|
-
}, {
|
|
19
|
+
}, {
|
|
20
|
+
explicitlyTyped: true,
|
|
21
|
+
preProcessSnapshot: snap => {
|
|
22
|
+
return snap.uri
|
|
23
|
+
? {
|
|
24
|
+
...snap,
|
|
25
|
+
vcfLocation: {
|
|
26
|
+
uri: snap.uri,
|
|
27
|
+
baseUri: snap.baseUri,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
: snap;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
20
33
|
export default VcfAdapter;
|
|
@@ -4,7 +4,10 @@ function x() { }
|
|
|
4
4
|
const VcfTabixAdapter = ConfigurationSchema('VcfTabixAdapter', {
|
|
5
5
|
vcfGzLocation: {
|
|
6
6
|
type: 'fileLocation',
|
|
7
|
-
defaultValue: {
|
|
7
|
+
defaultValue: {
|
|
8
|
+
uri: '/path/to/my.vcf.gz',
|
|
9
|
+
locationType: 'UriLocation',
|
|
10
|
+
},
|
|
8
11
|
},
|
|
9
12
|
index: ConfigurationSchema('VcfIndex', {
|
|
10
13
|
indexType: {
|
|
@@ -28,5 +31,24 @@ const VcfTabixAdapter = ConfigurationSchema('VcfTabixAdapter', {
|
|
|
28
31
|
locationType: 'UriLocation',
|
|
29
32
|
},
|
|
30
33
|
},
|
|
31
|
-
}, {
|
|
34
|
+
}, {
|
|
35
|
+
explicitlyTyped: true,
|
|
36
|
+
preProcessSnapshot: snap => {
|
|
37
|
+
return snap.uri
|
|
38
|
+
? {
|
|
39
|
+
...snap,
|
|
40
|
+
vcfGzLocation: {
|
|
41
|
+
uri: snap.uri,
|
|
42
|
+
baseUri: snap.baseUri,
|
|
43
|
+
},
|
|
44
|
+
index: {
|
|
45
|
+
location: {
|
|
46
|
+
uri: `${snap.uri}.tbi`,
|
|
47
|
+
baseUri: snap.baseUri,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
: snap;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
32
54
|
export default VcfTabixAdapter;
|
|
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
|
|
|
3
3
|
import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
|
|
4
4
|
import { getContainingView, getSession, isAbortException, } from '@jbrowse/core/util';
|
|
5
5
|
import { getRpcSessionId } from '@jbrowse/core/util/tracks';
|
|
6
|
-
import { Button, DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
|
|
6
|
+
import { Button, Checkbox, DialogActions, DialogContent, FormControlLabel, TextField, Typography, } from '@mui/material';
|
|
7
7
|
import copy from 'copy-to-clipboard';
|
|
8
8
|
import { saveAs } from 'file-saver';
|
|
9
9
|
import { isAlive } from 'mobx-state-tree';
|
|
@@ -23,6 +23,7 @@ export default function ClusterDialog({ model, handleClose, }) {
|
|
|
23
23
|
const [results, setResults] = useState();
|
|
24
24
|
const [error, setError] = useState();
|
|
25
25
|
const [paste, setPaste] = useState('');
|
|
26
|
+
const [useCompleteMethod, setUseCompleteMethod] = useState(false);
|
|
26
27
|
useEffect(() => {
|
|
27
28
|
;
|
|
28
29
|
(async () => {
|
|
@@ -44,11 +45,12 @@ export default function ClusterDialog({ model, handleClose, }) {
|
|
|
44
45
|
}));
|
|
45
46
|
const entries = Object.values(ret);
|
|
46
47
|
const keys = Object.keys(ret);
|
|
48
|
+
const clusterMethod = useCompleteMethod ? 'complete' : 'single';
|
|
47
49
|
const text = `try(library(fastcluster), silent=TRUE)
|
|
48
50
|
inputMatrix<-matrix(c(${entries.map(val => val.genotypes.join(',')).join(',\n')}
|
|
49
51
|
),nrow=${entries.length},byrow=TRUE)
|
|
50
52
|
rownames(inputMatrix)<-c(${keys.map(key => `'${key}'`).join(',')})
|
|
51
|
-
resultClusters<-hclust(dist(inputMatrix), method='
|
|
53
|
+
resultClusters<-hclust(dist(inputMatrix), method='${clusterMethod}')
|
|
52
54
|
cat(resultClusters$order,sep='\\n')`;
|
|
53
55
|
setResults(text);
|
|
54
56
|
}
|
|
@@ -59,14 +61,16 @@ cat(resultClusters$order,sep='\\n')`;
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
})();
|
|
62
|
-
}, [model]);
|
|
64
|
+
}, [model, useCompleteMethod]);
|
|
63
65
|
return (_jsxs(Dialog, { open: true, title: "Cluster by genotype", onClose: handleClose, children: [_jsx(DialogContent, { children: _jsxs("div", { className: classes.mgap, children: [_jsx(Typography, { children: "This page will produce an R script that will perform hierarchical clustering on the visible genotype data using `hclust`." }), _jsx(Typography, { children: "You can then paste the results in this form to specify the row ordering." }), results ? (_jsx("div", { children: _jsxs("div", { children: ["Step 1:", ' ', _jsx(Button, { variant: "contained", onClick: () => {
|
|
64
66
|
saveAs(new Blob([results || ''], {
|
|
65
67
|
type: 'text/plain;charset=utf-8',
|
|
66
68
|
}), 'cluster.R');
|
|
67
69
|
}, children: "Download Rscript" }), ' ', "or", ' ', _jsx(Button, { variant: "contained", onClick: () => {
|
|
68
70
|
copy(results || '');
|
|
69
|
-
}, children: "Copy Rscript to clipboard" }), _jsx(
|
|
71
|
+
}, children: "Copy Rscript to clipboard" }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { checked: useCompleteMethod, onChange: e => {
|
|
72
|
+
setUseCompleteMethod(e.target.checked);
|
|
73
|
+
} }), label: "Use 'complete' linkage method instead of 'single'" }), _jsx("div", { children: _jsx(TextField, { multiline: true, fullWidth: true, variant: "outlined", placeholder: "Step 2. Paste results from Rscript here (sequence of numbers, one per line, specifying the new ordering)", rows: 10, value: paste, onChange: event => {
|
|
70
74
|
setPaste(event.target.value);
|
|
71
75
|
}, slotProps: {
|
|
72
76
|
input: {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { Feature } from '@jbrowse/core/util';
|
|
2
2
|
export declare function findSecondLargestNumber(arr: Iterable<number>): number;
|
|
3
|
-
export declare function calculateAlleleCounts(feat: Feature):
|
|
4
|
-
alleleCounts: Map<any, any>;
|
|
5
|
-
mostFrequentAlt: any;
|
|
6
|
-
};
|
|
3
|
+
export declare function calculateAlleleCounts(feat: Feature): Map<any, any>;
|
|
7
4
|
export declare function calculateMinorAlleleFrequency(alleleCounts: Map<string, number>): number;
|
|
8
|
-
export declare function getFeaturesThatPassMinorAlleleFrequencyFilter(feats: Iterable<Feature>, minorAlleleFrequencyFilter: number): {
|
|
5
|
+
export declare function getFeaturesThatPassMinorAlleleFrequencyFilter(feats: Iterable<Feature>, minorAlleleFrequencyFilter: number, lengthCutoffFilter?: number): {
|
|
9
6
|
feature: Feature;
|
|
10
7
|
mostFrequentAlt: string;
|
|
8
|
+
alleleCounts: Map<string, number>;
|
|
11
9
|
}[];
|
|
@@ -22,6 +22,13 @@ export function calculateAlleleCounts(feat) {
|
|
|
22
22
|
alleleCounts.set(allele, (alleleCounts.get(allele) || 0) + 1);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
return alleleCounts;
|
|
26
|
+
}
|
|
27
|
+
export function calculateMinorAlleleFrequency(alleleCounts) {
|
|
28
|
+
return (findSecondLargestNumber(alleleCounts.values()) /
|
|
29
|
+
(sum(alleleCounts.values()) || 1));
|
|
30
|
+
}
|
|
31
|
+
function getMostFrequentAlt(alleleCounts) {
|
|
25
32
|
let mostFrequentAlt;
|
|
26
33
|
let max = 0;
|
|
27
34
|
for (const [alt, altCount] of alleleCounts.entries()) {
|
|
@@ -32,22 +39,20 @@ export function calculateAlleleCounts(feat) {
|
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
export function calculateMinorAlleleFrequency(alleleCounts) {
|
|
38
|
-
return (findSecondLargestNumber(alleleCounts.values()) /
|
|
39
|
-
(sum(alleleCounts.values()) || 1));
|
|
42
|
+
return mostFrequentAlt;
|
|
40
43
|
}
|
|
41
|
-
export function getFeaturesThatPassMinorAlleleFrequencyFilter(feats, minorAlleleFrequencyFilter) {
|
|
44
|
+
export function getFeaturesThatPassMinorAlleleFrequencyFilter(feats, minorAlleleFrequencyFilter, lengthCutoffFilter = 10) {
|
|
42
45
|
const results = [];
|
|
43
46
|
for (const feature of feats) {
|
|
44
|
-
if (feature.get('end') - feature.get('start') <=
|
|
45
|
-
const
|
|
47
|
+
if (feature.get('end') - feature.get('start') <= lengthCutoffFilter) {
|
|
48
|
+
const alleleCounts = calculateAlleleCounts(feature);
|
|
46
49
|
if (calculateMinorAlleleFrequency(alleleCounts) >=
|
|
47
50
|
minorAlleleFrequencyFilter) {
|
|
51
|
+
const mostFrequentAlt = getMostFrequentAlt(alleleCounts);
|
|
48
52
|
results.push({
|
|
49
53
|
feature,
|
|
50
54
|
mostFrequentAlt,
|
|
55
|
+
alleleCounts,
|
|
51
56
|
});
|
|
52
57
|
}
|
|
53
58
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-variants",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "JBrowse 2 variant adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"@gmod/bgzf-filehandle": "^2.0.1",
|
|
41
41
|
"@gmod/tabix": "^2.0.0",
|
|
42
42
|
"@gmod/vcf": "^6.0.8",
|
|
43
|
-
"@jbrowse/core": "^3.0
|
|
44
|
-
"@jbrowse/plugin-circular-view": "^3.0
|
|
45
|
-
"@jbrowse/plugin-linear-genome-view": "^3.0
|
|
46
|
-
"@jbrowse/sv-core": "^3.0
|
|
43
|
+
"@jbrowse/core": "^3.1.0",
|
|
44
|
+
"@jbrowse/plugin-circular-view": "^3.1.0",
|
|
45
|
+
"@jbrowse/plugin-linear-genome-view": "^3.1.0",
|
|
46
|
+
"@jbrowse/sv-core": "^3.1.0",
|
|
47
47
|
"@mui/icons-material": "^6.0.0",
|
|
48
48
|
"@mui/material": "^6.0.0",
|
|
49
49
|
"@mui/x-data-grid": "^7.0.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"distModule": "esm/index.js",
|
|
63
63
|
"srcModule": "src/index.ts",
|
|
64
64
|
"module": "esm/index.js",
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "91492049ddea0aed90eb24d3c066c2d9f5a6b189"
|
|
66
66
|
}
|