diff --git a/browser/help/topics/mitochondrial-constraint.md b/browser/help/topics/mitochondrial-constraint.md new file mode 100644 index 000000000..1efd7a74a --- /dev/null +++ b/browser/help/topics/mitochondrial-constraint.md @@ -0,0 +1,42 @@ +--- +id: mitochondrial-constraint +title: 'Mitochondrial gene constraint' +--- + +Variants in the mitochondrial genome (mtDNA) are available for 56,434 genome samples from gnomAD v4.1. Assessment of [constraint](/help/constraint) within the mtDNA requires a different approach than the nuclear genome, as its unique features make nuclear constraint models unsuitable. To measure intolerance to variation within the mtDNA, we developed a mitochondrial mutational model that predicts the level of variation expected to be seen in the gnomAD dataset for a given gene based on local sequence context. We then compare the expected values for each gene to the observed amount of variation and consider genes that are significantly depleted of their expected variation to be constrained, or intolerant of this variation. Our gene-level constraint metrics for the mtDNA are detailed below. + +The sections below will review: + +- [Methods](/help/mitochondrial-constraint#methods) +- [Observed/expected (oe) metric](/help/mitochondrial-constraint#oe) +- [Differences between mitochondrial and nuclear gene constraint metrics](/help/mitochondrial-constraint#differences-from-nuclear) + +More details on these methods can be found in the main article and supplement of [Lake et al. Nature 2024](https://www.nature.com/articles/s41586-024-08048-x). + +### Methods + +#### Genes, transcripts, and variant classes included in the analyses + +We provide gene constraint metrics for all protein-coding, ribosomal RNA (rRNA), and transfer RNA (tRNA) genes in the mitochondrial genome. Since each human mtDNA gene has only one transcript, distinction between canonical and non-canonical transcripts was not required. In the protein-coding genes, metrics are provided for (i) synonymous, (ii) missense and (iii) stop gain variants caused by single nucleotide changes. Note that splice site variants are not applicable to genes in mtDNA. In the rRNA and tRNA genes, metrics are provided for all single nucleotide variants. + +#### Observed value + +The observed value is the sum of the maximum observed heteroplasmy level (‘maximum heteroplasmy’) of every possible single nucleotide variant in the gene. Heteroplasmy refers to the proportion of mtDNA copies that carry the variant. Every possible variant is assigned a maximum heteroplasmy value between 0.0 and 1.0, representing the highest level at which the variant is observed across all individuals in gnomAD. Heteroplasmy is important to account for when detecting selection in mtDNA, as most pathogenic variants have maximum heteroplasmy levels below 1.0 due to selection, reflecting that individuals can carry pathogenic variants but be asymptomatic if heteroplasmy levels are low enough. + +#### Expected value + +We calculated the expected sum maximum heteroplasmy of single nucleotide variants in each gene using a mitochondrial mutational model that accounts for trinucleotide sequence context. While nuclear constraint models include corrections for coverage and methylation, we do not apply these given the high and even mtDNA coverage in gnomAD and lack of robust data on mtDNA methylation. + +### Observed / expected (oe) metric + +We calculated the ratio of observed to expected (oe) sum maximum heteroplasmy of variants in each gene in the mitochondrial genome and the 90% confidence interval (CI) around these ratios. These values provide an inference on the strength of selection against variation in each gene. Observed/expected (oe) ratios are a continuous measure of how tolerant a gene is to a certain class of variation (e.g. missense). Genes with lower oe values are under stronger selection pressure, while higher oe values indicate greater tolerance. + +We calculated the 90% CI around each oe ratio using a beta distribution, adapting methods previously used for nuclear genome constraint. The CI captures uncertainty around the ratio estimate, which can vary depending on sample size. When evaluating how constrained a gene is, it is important to take the 90% CI into consideration. We suggest using the upper bound of this confidence interval, termed the OEUF (observed to expected upper bound fraction), which provides a conservative measure of constraint. A lower OEUF indicates stronger selection, while a higher value suggests greater tolerance. + +### Differences between mitochondrial and nuclear gene constraint metrics + +The gene constraint metrics for the mitochondrial and nuclear genome differ due to the unique characteristics of the mtDNA. This includes its smaller size, lack of introns, high copy number, presence of heteroplasmy, distinct mutational mechanisms, and higher rate of mutation. These unique features precluded the application of nuclear constraint models to the mtDNA, necessitating a mitochondrial genome constraint model. Key differences between the mitochondrial and nuclear constraint models include: + +- Mutational Model: A mitochondrial mutational model was developed and applied, as nuclear mutational models could not be used due to distinct mutational mechanisms and signatures between the genomes. +- Observed/Expected Calculation: Nuclear constraint models assess the number of unique variants, while mitochondrial constraint models evaluate the sum of maximum heteroplasmy for variants. +- Confidence Interval: A beta distribution, rather than a Poisson distribution, is used to calculate confidence intervals for the observed/expected ratios based on maximum heteroplasmy values. diff --git a/browser/help/topics/mitochondrial-regional-constraint.md b/browser/help/topics/mitochondrial-regional-constraint.md new file mode 100644 index 000000000..85365dc4c --- /dev/null +++ b/browser/help/topics/mitochondrial-regional-constraint.md @@ -0,0 +1,40 @@ +--- +id: mitochondrial-regional-constraint +title: 'Mitochondrial gene regional constraint' +--- + +Regional constraint identifies regions within each gene that are more constrained than the entire gene. Knowing if a variant falls within an interval of regional constraint can help prioritize variants most likely to have a deleterious functional impact. Assessment of regional constraint within the mitochondrial genome (mtDNA) requires a different approach than the nuclear genome, as its distinct features make [nuclear regional constraint](/help/regional-constraint) models unsuitable. Variants in the mtDNA are available for 56,434 genome samples from gnomAD v4.1. For the mitochondrial genome, we provide regional missense constraint for protein-coding genes and regional constraint for single nucleotide variants in ribosomal RNA (rRNA) genes. Our mtDNA regional constraint metrics are detailed below. + +The sections below will review: + +- [Methods](/help/mitochondrial-regional-constraint#methods) +- [Observed/expected (oe) metric](/help/mitochondrial-regional-constraint#oe) +- [Differences between mitochondrial and nuclear gene constraint metrics](/help/mitochondrial-regional-constraint#differences-from-nuclear) + +More details on these methods can be found in the main article and supplement of [Lake et al. Nature 2024](https://www.nature.com/articles/s41586-024-08048-x). + +### Methods + +To identify regional missense constraint within protein-coding genes, we calculated the observed / expected (oe) missense ratio for all possible regions ≥ 30 bp within each gene. Regions with an oe ratio significantly lower than the gene’s overall oe ratio were identified using a beta distribution. A greedy algorithm was then applied to prioritize regions with the most significant p-values to produce a list of non-overlapping intervals. The false discovery rate (FDR) of each interval was estimated by applying the same method to 1,000 random permutations of each gene, retaining only regions with FDR <0.1 as high-confidence intervals. Regional constraint in the rRNA genes was evaluated using the same process with minor modifications. We provide the oe ratio of each interval of regional constraint and the 90% confidence interval around these ratios. + +#### Genes, transcripts, and variant classes included in the analyses + +We provide regional constraint metrics for all protein-coding and ribosomal RNA (rRNA) genes in the mitochondrial genome. Since each human mtDNA gene has only one transcript, distinction between canonical and non-canonical transcripts was not required. For protein-coding genes, we measure regional intolerance to missense variants. For rRNA genes, we assess regional intolerance to all single nucleotide variants. Note regional constraint metrics are not provided for transfer RNA (tRNA) genes due to their small size. + +### Observed/expected (oe) metric + +#### Observed values + +The observed value is the sum of the maximum observed heteroplasmy level (‘maximum heteroplasmy’) of every possible single nucleotide variant in the gene (or just missense for protein genes). Heteroplasmy refers to the proportion of mtDNA copies that carry the variant. Every possible variant is assigned a maximum heteroplasmy value between 0.0 and 1.0, representing the highest level at which the variant is observed across all individuals in gnomAD. Heteroplasmy is important to account for when detecting selection in mtDNA, as most pathogenic variants have maximum heteroplasmy levels below 1.0 due to selection, reflecting that individuals can carry pathogenic variants but be asymptomatic if heteroplasmy levels are low enough. + +#### Expected values + +We calculated the expected sum maximum heteroplasmy of single nucleotide variants in each gene using a mitochondrial mutational model that accounts for trinucleotide sequence context. While nuclear constraint models include corrections for coverage and methylation, we do not apply these given the high and even mtDNA coverage in gnomAD and lack of robust data on mtDNA methylation. + +### Differences between mitochondrial and nuclear regional constraint metrics + +The methods for identifying regional constraint differ between the mitochondrial and nuclear genomes due to the unique characteristics of mtDNA. This includes the lack of exons and introns in mtDNA, and using heteroplasmy instead of unique variant counts for calculating observed and expected values. These differences required the development of a specialized method for mtDNA regional constraint analysis. Key differences between mitochondrial and nuclear methods include: + +- Observed/Expected Values: Nuclear models assess constraint based on the number of unique variants, whereas mitochondrial models use the sum of maximum heteroplasmy values for every variant. +- Statistical Model: A beta distribution is used to identify regions that are significantly more constrained than the gene, in contrast to the Poisson distribution used for nuclear models. +- Application to RNA Genes: While nuclear regional constraint methods focus on protein-coding genes, the mitochondrial approach extends to RNA genes in the mtDNA. diff --git a/browser/package.json b/browser/package.json index 77803e4e3..6fa0a5d3b 100644 --- a/browser/package.json +++ b/browser/package.json @@ -23,6 +23,7 @@ "@gnomad/ui": "2.0.0", "@hot-loader/react-dom": "^17.0.0", "@visx/axis": "^3.0.0", + "@visx/group": "^3.0.0", "core-js": "3.5.0", "css-loader": "^6.7.3", "d3-array": "^1.2.4", diff --git a/browser/src/ConstraintTable/ConstraintTable.spec.tsx b/browser/src/ConstraintTable/ConstraintTable.spec.tsx index 6b5776d8b..469c246dd 100644 --- a/browser/src/ConstraintTable/ConstraintTable.spec.tsx +++ b/browser/src/ConstraintTable/ConstraintTable.spec.tsx @@ -11,6 +11,10 @@ import { BrowserRouter } from 'react-router-dom' import ConstraintTable from './ConstraintTable' import { ExacConstraint } from './ExacConstraintTable' import { GnomadConstraint } from './GnomadConstraintTable' +import { + ProteinMitochondrialGeneConstraint, + RNAMitochondrialGeneConstraint, +} from '../GenePage/GenePage' const exacConstraintFactory = Factory.define(() => ({ exp_lof: 0.123, @@ -42,6 +46,34 @@ const gnomadConstraintFactory = Factory.define(() => ({ oe_syn_upper: 0.95, })) +const proteinMitochondrialConstraintFactory = Factory.define( + () => ({ + exp_lof: 0.123, + exp_syn: 0.234, + exp_mis: 0.345, + oe_lof: 0.789, + oe_lof_lower: 0.6, + oe_lof_upper: 0.9, + oe_mis: 0.891, + oe_mis_lower: 0.8, + oe_mis_upper: 0.99, + oe_syn: 0.912, + oe_syn_lower: 0.8, + oe_syn_upper: 0.95, + obs_lof: 0.111, + obs_syn: 0.222, + obs_mis: 0.333, + }) +) + +const rnaMitochondrialConstraintFactory = Factory.define(() => ({ + observed: 1.1, + expected: 22.2, + oe: 0.33, + oe_lower: 0.31, + oe_upper: 0.35, +})) + forAllDatasets('ConstraintTable with "%s" dataset selected', (datasetId) => { describe('with a minimal gene', () => { test('has no unexpected changes', () => { @@ -65,13 +97,35 @@ forAllDatasets('ConstraintTable with "%s" dataset selected', (datasetId) => { }) }) - describe('with a mitochondrial gene', () => { + describe('with a mitochondrial protein gene', () => { + test('has no unexpected changes', () => { + const constraint = proteinMitochondrialConstraintFactory.build() + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) + }) + + describe('with a mitochondrial RNA gene', () => { test('has no unexpected changes', () => { + const constraint = rnaMitochondrialConstraintFactory.build() const tree = renderer.create( ) @@ -79,6 +133,20 @@ forAllDatasets('ConstraintTable with "%s" dataset selected', (datasetId) => { }) }) + describe('with a mitochondrial gene missing constraint data', () => { + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) + describe('with a mitochondrial transcript', () => { test('has no unexpected changes', () => { const tree = renderer.create( diff --git a/browser/src/ConstraintTable/ConstraintTable.tsx b/browser/src/ConstraintTable/ConstraintTable.tsx index 659665ef6..87f18865e 100644 --- a/browser/src/ConstraintTable/ConstraintTable.tsx +++ b/browser/src/ConstraintTable/ConstraintTable.tsx @@ -8,6 +8,7 @@ import Link from '../Link' import ExacConstraintTable from './ExacConstraintTable' import GnomadConstraintTable from './GnomadConstraintTable' +import MitochondrialConstraintTable from './MitochondrialConstraintTable' type Props = { datasetId: DatasetId @@ -65,18 +66,16 @@ const ConstraintTable = ({ datasetId, geneOrTranscript }: Props) => { const { transcriptId, transcriptVersion, transcriptDescription } = transcriptDetails(geneOrTranscript) - const gnomadConstraint = geneOrTranscript.gnomad_constraint - const exacConstraint = geneOrTranscript.exac_constraint - if (geneOrTranscript.chrom === 'M') { - return ( -

- Constraint is not available for mitochondrial{' '} - {isGene(geneOrTranscript) ? 'genes' : 'transcripts'} -

- ) + if (isGene(geneOrTranscript)) { + return + } + return

Constraint is not available for mitochondrial transcripts

} + const gnomadConstraint = geneOrTranscript.gnomad_constraint + const exacConstraint = geneOrTranscript.exac_constraint + if (datasetId === 'exac') { if (!exacConstraint) { return ( diff --git a/browser/src/ConstraintTable/MitochondrialConstraintTable.tsx b/browser/src/ConstraintTable/MitochondrialConstraintTable.tsx new file mode 100644 index 000000000..3b09fc9e4 --- /dev/null +++ b/browser/src/ConstraintTable/MitochondrialConstraintTable.tsx @@ -0,0 +1,136 @@ +import React from 'react' +import { + MitochondrialGeneConstraint, + ProteinMitochondrialGeneConstraint, + RNAMitochondrialGeneConstraint, +} from '../GenePage/GenePage' +import { BaseTable } from '@gnomad/ui' + +const isProteinMitochondrialGeneConstraint = ( + constraint: MitochondrialGeneConstraint +): constraint is ProteinMitochondrialGeneConstraint => + Object.prototype.hasOwnProperty.call(constraint, 'exp_lof') + +const ConstraintRow = ({ + category, + expected, + observed, + oe, + oeLower, + oeUpper, +}: { + category: string + expected: number + observed: number + oe: number + oeLower: number + oeUpper: number +}) => ( + + {category} + {expected < 10 ? expected.toFixed(2) : expected.toFixed(1)} + {observed < 10 ? observed.toFixed(2) : observed.toFixed(1)} + + o/e = {oe.toFixed(2)} ({oeLower.toFixed(2)} - {oeUpper.toFixed(2)}) + + +) + +const ProteinConstraintMetrics = ({ + constraint, +}: { + constraint: ProteinMitochondrialGeneConstraint +}) => { + const { + exp_lof, + exp_mis, + exp_syn, + obs_lof, + obs_mis, + obs_syn, + oe_lof, + oe_lof_lower, + oe_lof_upper, + oe_mis, + oe_mis_lower, + oe_mis_upper, + oe_syn, + oe_syn_lower, + oe_syn_upper, + } = constraint + return ( + + + + + + ) +} + +const RNAConstraintMetrics = ({ constraint }: { constraint: RNAMitochondrialGeneConstraint }) => { + const { expected, observed, oe, oe_lower, oe_upper } = constraint + return ( + + + + ) +} + +const MitochondrialConstraintTable = ({ + constraint, +}: { + constraint: MitochondrialGeneConstraint | null +}) => { + if (constraint === null) { + return

Constraint is not available on this gene

+ } + + return ( + // @ts-expect-error + + + + Category + Expected SNVs + Observed SNVs + Constraint metrics + + + {isProteinMitochondrialGeneConstraint(constraint) ? ( + + ) : ( + + )} + + ) +} + +export default MitochondrialConstraintTable diff --git a/browser/src/ConstraintTable/__snapshots__/ConstraintTable.spec.tsx.snap b/browser/src/ConstraintTable/__snapshots__/ConstraintTable.spec.tsx.snap index 98f96d45f..c8d73e61a 100644 --- a/browser/src/ConstraintTable/__snapshots__/ConstraintTable.spec.tsx.snap +++ b/browser/src/ConstraintTable/__snapshots__/ConstraintTable.spec.tsx.snap @@ -1,5 +1,131 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[` 1`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 2`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 3`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 4`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 5`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 6`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 7`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 + . +

+`; + +exports[` 8`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (controls/biobanks) + . +

+`; + +exports[` 9`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-cancer) + . +

+`; + +exports[` 10`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-neuro) + . +

+`; + +exports[` 11`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-TOPMed) + . +

+`; + +exports[` 12`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-v2) + . +

+`; + +exports[` 13`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 14`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 15`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 16`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 17`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 18`] = ` +

+ Constraint is not available on this gene +

+`; + +exports[` 19`] = ` +

+ Constraint is not available on this gene +

+`; + exports[`ConstraintTable with "exac" dataset selected with a minimal gene has no unexpected changes 1`] = `

Constraint not available for this @@ -14,19 +140,163 @@ exports[`ConstraintTable with "exac" dataset selected with a minimal transcript

`; -exports[`ConstraintTable with "exac" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "exac" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "exac" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "exac" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -791,19 +1061,163 @@ exports[`ConstraintTable with "gnomad_cnv_r4" dataset selected with a minimal tr

`; -exports[`ConstraintTable with "gnomad_cnv_r4" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_cnv_r4" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_cnv_r4" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_cnv_r4" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -1568,19 +1982,163 @@ exports[`ConstraintTable with "gnomad_r2_1" dataset selected with a minimal tran

`; -exports[`ConstraintTable with "gnomad_r2_1" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r2_1" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r2_1" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r2_1" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -2353,19 +2911,163 @@ exports[`ConstraintTable with "gnomad_r2_1_controls" dataset selected with a min

`; -exports[`ConstraintTable with "gnomad_r2_1_controls" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r2_1_controls" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r2_1_controls" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r2_1_controls" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -3138,19 +3840,163 @@ exports[`ConstraintTable with "gnomad_r2_1_non_cancer" dataset selected with a m

`; -exports[`ConstraintTable with "gnomad_r2_1_non_cancer" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r2_1_non_cancer" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r2_1_non_cancer" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r2_1_non_cancer" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -3923,19 +4769,163 @@ exports[`ConstraintTable with "gnomad_r2_1_non_neuro" dataset selected with a mi

`; -exports[`ConstraintTable with "gnomad_r2_1_non_neuro" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r2_1_non_neuro" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r2_1_non_neuro" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r2_1_non_neuro" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -4708,19 +5698,163 @@ exports[`ConstraintTable with "gnomad_r2_1_non_topmed" dataset selected with a m

`; -exports[`ConstraintTable with "gnomad_r2_1_non_topmed" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r2_1_non_topmed" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r2_1_non_topmed" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r2_1_non_topmed" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -4756,7 +5890,15 @@ exports[`ConstraintTable with "gnomad_r3" dataset selected with a minimal transc

`; -exports[`ConstraintTable with "gnomad_r3" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 + . +

+`; + +exports[`ConstraintTable with "gnomad_r3" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 @@ -4804,7 +5946,15 @@ exports[`ConstraintTable with "gnomad_r3_controls_and_biobanks" dataset selected

`; -exports[`ConstraintTable with "gnomad_r3_controls_and_biobanks" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3_controls_and_biobanks" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (controls/biobanks) + . +

+`; + +exports[`ConstraintTable with "gnomad_r3_controls_and_biobanks" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 (controls/biobanks) @@ -4852,7 +6002,15 @@ exports[`ConstraintTable with "gnomad_r3_non_cancer" dataset selected with a min

`; -exports[`ConstraintTable with "gnomad_r3_non_cancer" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3_non_cancer" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-cancer) + . +

+`; + +exports[`ConstraintTable with "gnomad_r3_non_cancer" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 (non-cancer) @@ -4900,7 +6058,15 @@ exports[`ConstraintTable with "gnomad_r3_non_neuro" dataset selected with a mini

`; -exports[`ConstraintTable with "gnomad_r3_non_neuro" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3_non_neuro" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-neuro) + . +

+`; + +exports[`ConstraintTable with "gnomad_r3_non_neuro" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 (non-neuro) @@ -4948,7 +6114,15 @@ exports[`ConstraintTable with "gnomad_r3_non_topmed" dataset selected with a min

`; -exports[`ConstraintTable with "gnomad_r3_non_topmed" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3_non_topmed" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-TOPMed) + . +

+`; + +exports[`ConstraintTable with "gnomad_r3_non_topmed" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 (non-TOPMed) @@ -4996,7 +6170,15 @@ exports[`ConstraintTable with "gnomad_r3_non_v2" dataset selected with a minimal

`; -exports[`ConstraintTable with "gnomad_r3_non_v2" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` +exports[`ConstraintTable with "gnomad_r3_non_v2" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` +

+ Constraint not yet available for + gnomAD v3.1.2 (non-v2) + . +

+`; + +exports[`ConstraintTable with "gnomad_r3_non_v2" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = `

Constraint not yet available for gnomAD v3.1.2 (non-v2) @@ -5773,19 +6955,163 @@ exports[`ConstraintTable with "gnomad_r4" dataset selected with a minimal transc

`; -exports[`ConstraintTable with "gnomad_r4" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r4" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r4" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r4" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -6550,19 +7876,163 @@ exports[`ConstraintTable with "gnomad_r4_non_ukb" dataset selected with a minima

`; -exports[`ConstraintTable with "gnomad_r4_non_ukb" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_r4_non_ukb" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_r4_non_ukb" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_r4_non_ukb" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -7327,19 +8797,163 @@ exports[`ConstraintTable with "gnomad_sv_r2_1" dataset selected with a minimal t

`; -exports[`ConstraintTable with "gnomad_sv_r2_1" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_sv_r2_1" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_sv_r2_1" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_sv_r2_1" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -8112,19 +9726,163 @@ exports[`ConstraintTable with "gnomad_sv_r2_1_controls" dataset selected with a

`; -exports[`ConstraintTable with "gnomad_sv_r2_1_controls" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_sv_r2_1_controls" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_sv_r2_1_controls" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_sv_r2_1_controls" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -8897,19 +10655,163 @@ exports[`ConstraintTable with "gnomad_sv_r2_1_non_neuro" dataset selected with a

`; -exports[`ConstraintTable with "gnomad_sv_r2_1_non_neuro" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_sv_r2_1_non_neuro" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_sv_r2_1_non_neuro" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_sv_r2_1_non_neuro" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; @@ -9674,19 +11576,163 @@ exports[`ConstraintTable with "gnomad_sv_r4" dataset selected with a minimal tra

`; -exports[`ConstraintTable with "gnomad_sv_r4" dataset selected with a mitochondrial gene has no unexpected changes 1`] = ` -

- Constraint is not available for mitochondrial - - genes -

+exports[`ConstraintTable with "gnomad_sv_r4" dataset selected with a mitochondrial RNA gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ RNA variant + + 22.2 + + 1.10 + + o/e = + 0.33 + ( + 0.31 + - + 0.35 + ) +
+`; + +exports[`ConstraintTable with "gnomad_sv_r4" dataset selected with a mitochondrial protein gene has no unexpected changes 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Category + + Expected SNVs + + Observed SNVs + + Constraint metrics +
+ Synonymous + + 0.23 + + 0.22 + + o/e = + 0.91 + ( + 0.80 + - + 0.95 + ) +
+ Missense + + 0.34 + + 0.33 + + o/e = + 0.89 + ( + 0.80 + - + 0.99 + ) +
+ pLoF + + 0.12 + + 0.11 + + o/e = + 0.79 + ( + 0.60 + - + 0.90 + ) +
`; exports[`ConstraintTable with "gnomad_sv_r4" dataset selected with a mitochondrial transcript has no unexpected changes 1`] = `

- Constraint is not available for mitochondrial - - transcripts + Constraint is not available for mitochondrial transcripts

`; diff --git a/browser/src/ConstraintTrack.spec.tsx b/browser/src/ConstraintTrack.spec.tsx new file mode 100644 index 000000000..0f58feabc --- /dev/null +++ b/browser/src/ConstraintTrack.spec.tsx @@ -0,0 +1,87 @@ +import { regionsInExons, GenericRegion } from './ConstraintTrack' +import { exonFactory } from './__factories__/Gene' +import { Exon } from './TranscriptPage/TranscriptPage' + +test('regionsInExons', () => { + const testCases = [ + { + regions: [ + { start: 2, stop: 4, i: 1 }, + { start: 6, stop: 8, i: 2 }, + ], + exons: [exonFactory.build({ start: 2, stop: 6 }), exonFactory.build({ start: 6, stop: 9 })], + expected: [ + { start: 2, stop: 4, i: 1, unclamped_start: 2, unclamped_stop: 4 }, + { start: 6, stop: 8, i: 2, unclamped_start: 6, unclamped_stop: 8 }, + ], + }, + { + regions: [ + { start: 2, stop: 4, i: 1 }, + { start: 6, stop: 8, i: 2 }, + ], + exons: [exonFactory.build({ start: 1, stop: 8 })], + expected: [ + { start: 2, stop: 4, i: 1, unclamped_start: 2, unclamped_stop: 4 }, + { start: 6, stop: 8, i: 2, unclamped_start: 6, unclamped_stop: 8 }, + ], + }, + { + regions: [ + { start: 2, stop: 4, i: 1 }, + { start: 6, stop: 8, i: 2 }, + ], + exons: [ + exonFactory.build({ start: 1, stop: 3 }), + exonFactory.build({ start: 3, stop: 5 }), + exonFactory.build({ start: 5, stop: 9 }), + ], + expected: [ + { start: 2, stop: 3, i: 1, unclamped_start: 2, unclamped_stop: 4 }, + { start: 3, stop: 4, i: 1, unclamped_start: 2, unclamped_stop: 4 }, + { start: 6, stop: 8, i: 2, unclamped_start: 6, unclamped_stop: 8 }, + ], + }, + { + regions: [ + { start: 2, stop: 4, misc_field_1: 2, misc_field_2: 4 }, + { start: 6, stop: 8, misc_field_1: 6, misc_field_2: 8 }, + ], + exons: [ + exonFactory.build({ start: 1, stop: 3 }), + exonFactory.build({ start: 3, stop: 5 }), + exonFactory.build({ start: 5, stop: 9 }), + ], + expected: [ + { + start: 2, + stop: 3, + misc_field_1: 2, + misc_field_2: 4, + unclamped_start: 2, + unclamped_stop: 4, + }, + { + start: 3, + stop: 4, + misc_field_1: 2, + misc_field_2: 4, + unclamped_start: 2, + unclamped_stop: 4, + }, + { + start: 6, + stop: 8, + misc_field_1: 6, + misc_field_2: 8, + unclamped_start: 6, + unclamped_stop: 8, + }, + ], + }, + ] + + testCases.forEach(({ regions, exons, expected }) => { + expect(regionsInExons(regions as GenericRegion[], exons as Exon[])).toEqual(expected) + }) +}) diff --git a/browser/src/ConstraintTrack.tsx b/browser/src/ConstraintTrack.tsx new file mode 100644 index 000000000..463f51cc9 --- /dev/null +++ b/browser/src/ConstraintTrack.tsx @@ -0,0 +1,241 @@ +import React, { ReactNode } from 'react' +import styled from 'styled-components' +// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@gno... Remove this comment to see the full error message +import { Track } from '@gnomad/region-viewer' +import InfoButton from './help/InfoButton' +import { TooltipAnchor } from '@gnomad/ui' +import { Exon } from './TranscriptPage/TranscriptPage' + +export const PlotWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; +` + +type TrackProps = { + scalePosition: (input: number) => number + width: number +} + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + margin-bottom: 1em; +` + +export const SidePanel = styled.div` + display: flex; + align-items: center; + height: 100%; +` + +const TopPanel = styled.div` + display: flex; + justify-content: flex-end; + width: 100%; + margin-bottom: 5px; +` + +const LegendWrapper = styled.div` + display: flex; + + @media (max-width: 600px) { + flex-direction: column; + align-items: center; + } +` + +export const RegionAttributeList = styled.dl` + margin: 0; + + div { + margin-bottom: 0.25em; + } + + dt { + display: inline; + font-weight: bold; + } + + dd { + display: inline; + margin-left: 0.5em; + } +` + +export interface GenericRegion { + start: number + stop: number +} + +// When clamping constrained regions to exons, we remember the original +// boundaries of the region for display in the region tooltip +export type RegionWithUnclamped = R & { + unclamped_start: number + unclamped_stop: number +} + +type Props = { + trackTitle: string + allRegions: R[] | null + constrainedRegions: RegionWithUnclamped[] + infobuttonTopic: string + legend: ReactNode + tooltipComponent: React.ElementType + colorFn: (region: R) => string + valueFn: (region: R) => string +} + +export const regionsInExons = ( + regions: R[], + exons: Exon[] +): RegionWithUnclamped[] => { + const sortedRegions = regions.sort((a, b) => a.start - b.start) + const sortedExons = exons.sort((a, b) => a.start - b.start) + + const intersections = [] + + let regionIndex = 0 + let exonIndex = 0 + + while (regionIndex < regions.length && exonIndex < exons.length) { + const region = sortedRegions[regionIndex] + const exon = sortedExons[exonIndex] + const maxStart = Math.max(region.start, exon.start) + const minStop = Math.min(region.stop, exon.stop) + + if (maxStart < minStop) { + const next: RegionWithUnclamped = { + ...region, + start: maxStart, + stop: minStop, + unclamped_start: region.start, + unclamped_stop: region.stop, + } + intersections.push(next) + } + + if (region.stop === minStop) { + regionIndex += 1 + } + if (exon.stop === minStop) { + exonIndex += 1 + } + } + return intersections +} + +const ConstraintTrack = ({ + trackTitle, + allRegions, + constrainedRegions, + infobuttonTopic, + legend, + tooltipComponent, + colorFn, + valueFn, +}: Props) => ( + + ( + + {trackTitle} + + + )} + > + {({ scalePosition, width }: TrackProps) => ( + <> + + {legend} + + + + {!allRegions && } + {constrainedRegions.map((region: RegionWithUnclamped) => { + const startX = scalePosition(region.start) + const stopX = scalePosition(region.stop) + const regionWidth = stopX - startX + + return ( + + + + + + ) + })} + {allRegions && ( + + {allRegions.map((region: R, index: number) => { + const startX = scalePosition(region.start) + const stopX = scalePosition(region.stop) + const regionWidth = stopX - startX + const midX = (startX + stopX) / 2 + const offset = index * 0 + + return ( + + + + + {regionWidth > 40 && ( + <> + + + {valueFn(region)} + + + )} + + ) + })} + + )} + + + + )} + + +) + +export default ConstraintTrack diff --git a/browser/src/GenePage/GenePage.tsx b/browser/src/GenePage/GenePage.tsx index f93a8712b..333bf16f1 100644 --- a/browser/src/GenePage/GenePage.tsx +++ b/browser/src/GenePage/GenePage.tsx @@ -10,6 +10,9 @@ import { Track } from '@gnomad/region-viewer' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@gno... Remove this comment to see the full error message import { TranscriptPlot } from '@gnomad/track-transcripts' import { Badge, Button } from '@gnomad/ui' +import MitochondrialRegionConstraintTrack, { + MitochondrialConstraintRegion, +} from './MitochondrialRegionConstraintTrack' import { DatasetId, @@ -73,18 +76,52 @@ import { import { logButtonClick } from '../analytics' import { GtexTissueExpression } from './TranscriptsTissueExpression' +export type ProteinMitochondrialGeneConstraint = { + exp_lof: number + exp_mis: number + exp_syn: number + + obs_lof: number + obs_mis: number + obs_syn: number + + oe_lof: number + oe_lof_lower: number + oe_lof_upper: number + + oe_mis: number + oe_mis_lower: number + oe_mis_upper: number + + oe_syn: number + oe_syn_lower: number + oe_syn_upper: number +} + +export type RNAMitochondrialGeneConstraint = { + observed: number + expected: number + oe: number + oe_upper: number + oe_lower: number +} + +export type MitochondrialGeneConstraint = + | ProteinMitochondrialGeneConstraint + | RNAMitochondrialGeneConstraint + export type Strand = '+' | '-' export type GeneMetadata = { gene_id: string gene_version: string symbol: string - mane_select_transcript?: { + mane_select_transcript: { ensembl_id: string ensembl_version: string refseq_id: string refseq_version: string - } + } | null canonical_transcript_id: string | null flags: string[] } @@ -115,7 +152,7 @@ export type Pext = { export type Gene = GeneMetadata & { reference_genome: ReferenceGenome - name?: string + name: string | null chrom: string strand: Strand start: number @@ -127,20 +164,24 @@ export type Gene = GeneMetadata & { }[] transcripts: GeneTranscript[] flags: string[] - gnomad_constraint?: GnomadConstraint - exac_constraint?: ExacConstraint - pext?: Pext - short_tandem_repeats?: { - id: string - }[] - exac_regional_missense_constraint_regions?: any - gnomad_v2_regional_missense_constraint?: RegionalMissenseConstraint + gnomad_constraint: GnomadConstraint | null + exac_constraint: ExacConstraint | null + pext: Pext | null + short_tandem_repeats: + | { + id: string + }[] + | null + exac_regional_missense_constraint_regions: any | null + gnomad_v2_regional_missense_constraint: RegionalMissenseConstraint | null variants: Variant[] structural_variants: StructuralVariant[] copy_number_variants: CopyNumberVariant[] clinvar_variants: ClinvarVariant[] homozygous_variant_cooccurrence_counts: HomozygousVariantCooccurrenceCountsPerSeverityAndAf heterozygous_variant_cooccurrence_counts: HeterozygousVariantCooccurrenceCountsPerSeverityAndAf + mitochondrial_constraint: MitochondrialGeneConstraint | null + mitochondrial_missense_constraint_regions: MitochondrialConstraintRegion[] | null } const GeneName = styled.span` @@ -356,7 +397,10 @@ const GenePage = ({ datasetId, gene, geneId }: Props) => { ownTableName="constraint" setSelectedTableName={setSelectedTableName} > - Constraint {gene.chrom !== 'M' && } + Constraint + { )} + {gene.chrom.startsWith('M') && ( + + )} + {hasCodingExons && gene.chrom !== 'M' && gene.pext && ( i, +} + +const exons: Exon[] = [{ feature_type: 'CDS', start: 123, stop: 1999 }] +const constraintRegions: MitochondrialConstraintRegion[] = [ + { start: 555, stop: 666, oe: 0.45, oe_lower: 0.37, oe_upper: 0.47 }, + { start: 777, stop: 888, oe: 0.56, oe_lower: 0.52, oe_upper: 0.59 }, +] + +describe('MitochondrialRegionConstraintTrack for a protein gene', () => { + test('track has no unexpected changes when gene has constraint', () => { + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) + + test('track has no unexpected changes when no constraint for gene', () => { + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) +}) + +describe('MitochondrialRegionConstraintTrack for an RNA gene', () => { + test('track has no unexpected changes when gene has constraint', () => { + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) + + test('track has no unexpected changes when no constraint for gene', () => { + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() + }) +}) diff --git a/browser/src/GenePage/MitochondrialRegionConstraintTrack.tsx b/browser/src/GenePage/MitochondrialRegionConstraintTrack.tsx new file mode 100644 index 000000000..ff69c68c8 --- /dev/null +++ b/browser/src/GenePage/MitochondrialRegionConstraintTrack.tsx @@ -0,0 +1,75 @@ +import React from 'react' +import { Exon } from '../TranscriptPage/TranscriptPage' +import ConstraintTrack, { regionsInExons, RegionAttributeList } from '../ConstraintTrack' + +export type MitochondrialConstraintRegion = { + start: number + stop: number + oe: number + oe_upper: number + oe_lower: number +} + +type Props = { + geneSymbol: string + constraintRegions: MitochondrialConstraintRegion[] | null + exons: Exon[] +} + +const constraintColor = '#9b001f' + +const Legend = () => ( + <> + Regionally constrained interval + + + + +) + +type TooltipProps = { + region: MitochondrialConstraintRegion +} + +const Tooltip = ({ region }: TooltipProps) => { + return ( + +
+
Coordinates:
+
{`M:${region.start}-${region.stop}`}
+
+
+
Missense observed/expected:
+
+ {region.oe.toFixed(3)} ({region.oe_lower.toFixed(3)}-{region.oe_upper.toFixed(3)}) +
+
+
+ ) +} + +const formattedOE = (region: MitochondrialConstraintRegion) => region.oe.toFixed(3) + +const MitochondrialConstraintRegionTrack = ({ geneSymbol, constraintRegions, exons }: Props) => { + if (constraintRegions === null) { + return null + } + + const isRNAGene = geneSymbol.startsWith('MT-R') + const trackTitle = isRNAGene ? 'Regional constraint' : 'Regional missense constraint' + + return ( + } + valueFn={formattedOE} + colorFn={() => constraintColor} + tooltipComponent={Tooltip} + allRegions={null} + constrainedRegions={regionsInExons(constraintRegions, exons)} + /> + ) +} + +export default MitochondrialConstraintRegionTrack diff --git a/browser/src/GenePage/VariantsInGene.tsx b/browser/src/GenePage/VariantsInGene.tsx index da367cf91..cf58afb8c 100644 --- a/browser/src/GenePage/VariantsInGene.tsx +++ b/browser/src/GenePage/VariantsInGene.tsx @@ -11,7 +11,7 @@ import filterVariantsInZoomRegion from '../RegionViewer/filterVariantsInZoomRegi import { TrackPageSection } from '../TrackPage' import annotateVariantsWithClinvar from '../VariantList/annotateVariantsWithClinvar' import Variants from '../VariantList/Variants' -import { Pext } from './GenePage' +import { Gene } from './GenePage' type TranscriptsModalProps = { gene: { @@ -325,10 +325,7 @@ const annotateVariantsWithPext = (variants: any, pext: any) => { type ConnectedVariantsInGeneProps = { datasetId: DatasetId - gene: { - gene_id: string - pext?: Pext - } + gene: Gene } & VariantsInGeneProps const ConnectedVariantsInGene = ({ diff --git a/browser/src/GenePage/__snapshots__/GenePage.spec.tsx.snap b/browser/src/GenePage/__snapshots__/GenePage.spec.tsx.snap index 85c86cb51..4ecdf8fd1 100644 --- a/browser/src/GenePage/__snapshots__/GenePage.spec.tsx.snap +++ b/browser/src/GenePage/__snapshots__/GenePage.spec.tsx.snap @@ -654,7 +654,7 @@ exports[`GenePage with CNV dataset "gnomad_cnv_r4" has no unexpected changes 1`] className="sc-bdVaJa dDlceV" onClick={[Function]} > - Constraint + Constraint + + +
+
+
+ + Regionally constrained interval + + + + +
+
+
+ + + + + + + + + +
+
+ + + +`; + +exports[`MitochondrialRegionConstraintTrack for a protein gene track has no unexpected changes when no constraint for gene 1`] = `null`; + +exports[`MitochondrialRegionConstraintTrack for an RNA gene track has no unexpected changes when gene has constraint 1`] = ` +
+
+
+
+
+ + Regional constraint + + +
+
+
+
+
+ + Regionally constrained interval + + + + +
+
+
+ + + + + + + + + +
+
+
+
+
+`; + +exports[`MitochondrialRegionConstraintTrack for an RNA gene track has no unexpected changes when no constraint for gene 1`] = `null`; diff --git a/browser/src/RegionalMissenseConstraintTrack.spec.tsx b/browser/src/RegionalMissenseConstraintTrack.spec.tsx index ba8121a78..362af2599 100644 --- a/browser/src/RegionalMissenseConstraintTrack.spec.tsx +++ b/browser/src/RegionalMissenseConstraintTrack.spec.tsx @@ -1,72 +1,77 @@ +import React from 'react' +import renderer from 'react-test-renderer' import { describe, expect, test } from '@jest/globals' +import RegionalMissenseConstraintTrack, { + RegionalMissenseConstraint, + RegionalMissenseConstraintRegion, +} from './RegionalMissenseConstraintTrack' +import { Gene } from './GenePage/GenePage' +import geneFactory from './__factories__/Gene' +// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@gno... Remove this comment to see the full error message +import { RegionViewerContext } from '@gnomad/region-viewer' -// eslint-disable-next-line import/no-unresolved, import/extensions -import { regionIntersections } from './RegionalMissenseConstraintTrack' +describe('RegionalMissenseConstraint', () => { + const childProps = { + centerPanelWidth: 500, + isPositionDefined: true, + leftPanelWidth: 100, + rightPanelWidth: 50, + scalePosition: (i: number) => i, + } -describe('RegionaMissenseConstraintTrack', () => { - test('regionIntersections', () => { - const testCases = [ + test('has no unexpected changes when the RMC has evidence and passed QC', () => { + const regions: RegionalMissenseConstraintRegion[] = [ { - regions1: [ - { start: 2, stop: 4, i: 1 }, - { start: 6, stop: 8, i: 2 }, - ], - regions2: [ - { start: 2, stop: 6, j: 1 }, - { start: 6, stop: 9, j: 2 }, - ], - expected: [ - { start: 2, stop: 4, i: 1, j: 1 }, - { start: 6, stop: 8, i: 2, j: 2 }, - ], + chrom: '1', + start: 50, + stop: 180, + aa_start: null, + aa_stop: null, + obs_mis: 12, + exp_mis: 34, + obs_exp: 12.0 / 34.0, + p_value: 0.45, + z_score: 0.56, + chisq_diff_null: undefined, }, { - regions1: [ - { start: 2, stop: 4, i: 1 }, - { start: 6, stop: 8, i: 2 }, - ], - regions2: [{ start: 1, stop: 8, j: 1 }], - expected: [ - { start: 2, stop: 4, i: 1, j: 1 }, - { start: 6, stop: 8, i: 2, j: 1 }, - ], - }, - { - regions1: [ - { start: 2, stop: 4, i: 1 }, - { start: 6, stop: 8, i: 2 }, - ], - regions2: [ - { start: 1, stop: 3, j: 1 }, - { start: 3, stop: 5, j: 2 }, - { start: 5, stop: 9, j: 3 }, - ], - expected: [ - { start: 2, stop: 3, i: 1, j: 1 }, - { start: 3, stop: 4, i: 1, j: 2 }, - { start: 6, stop: 8, i: 2, j: 3 }, - ], - }, - { - regions1: [ - { start: 2, stop: 4, region_start: 2, region_stop: 4 }, - { start: 6, stop: 8, region_start: 6, region_stop: 8 }, - ], - regions2: [ - { start: 1, stop: 3, j: 1 }, - { start: 3, stop: 5, j: 2 }, - { start: 5, stop: 9, j: 3 }, - ], - expected: [ - { start: 2, stop: 3, region_start: 2, region_stop: 4, j: 1 }, - { start: 3, stop: 4, region_start: 2, region_stop: 4, j: 2 }, - { start: 6, stop: 8, region_start: 6, region_stop: 8, j: 3 }, - ], + chrom: '1', + start: 250, + stop: 400, + aa_start: null, + aa_stop: null, + obs_mis: 13, + exp_mis: 35, + obs_exp: 13.0 / 35.0, + p_value: 0.46, + z_score: 0.57, + chisq_diff_null: undefined, }, ] + const rmc: RegionalMissenseConstraint = { regions, has_no_rmc_evidence: false, passed_qc: true } - testCases.forEach(({ regions1, regions2, expected }) => { - expect(regionIntersections([regions1, regions2])).toEqual(expected) + const gene: Gene = geneFactory.build({ + chrom: '1', + exons: [ + { + feature_type: 'UTR', + start: 1, + stop: 123, + }, + { feature_type: 'CDS', start: 124, stop: 234 }, + { feature_type: 'UTR', start: 235, stop: 345 }, + { feature_type: 'CDS', start: 346, stop: 456 }, + { feature_type: 'UTR', start: 457, stop: 567 }, + { feature_type: 'CDS', start: 568, stop: 678 }, + { feature_type: 'UTR', start: 679, stop: 789 }, + ], }) + + const tree = renderer.create( + + + + ) + expect(tree).toMatchSnapshot() }) }) diff --git a/browser/src/RegionalMissenseConstraintTrack.tsx b/browser/src/RegionalMissenseConstraintTrack.tsx index bbd4c684c..8515b9105 100644 --- a/browser/src/RegionalMissenseConstraintTrack.tsx +++ b/browser/src/RegionalMissenseConstraintTrack.tsx @@ -1,21 +1,24 @@ import React from 'react' -import styled from 'styled-components' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@gno... Remove this comment to see the full error message import { Track } from '@gnomad/region-viewer' -import { TooltipAnchor } from '@gnomad/ui' import Link from './Link' import InfoButton from './help/InfoButton' import { Gene } from './GenePage/GenePage' - -type RegionalMissenseConstraintRegion = { +import ConstraintTrack, { + SidePanel, + PlotWrapper, + regionsInExons, + RegionAttributeList, + RegionWithUnclamped, +} from './ConstraintTrack' + +export type RegionalMissenseConstraintRegion = { chrom: string start: number stop: number - region_start: number - region_stop: number aa_start: string | null aa_stop: string | null obs_mis: number | undefined @@ -26,78 +29,6 @@ type RegionalMissenseConstraintRegion = { z_score: number | undefined } -const Wrapper = styled.div` - display: flex; - flex-direction: column; - margin-bottom: 1em; -` - -const PlotWrapper = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; -` - -const RegionAttributeList = styled.dl` - margin: 0; - - div { - margin-bottom: 0.25em; - } - - dt { - display: inline; - font-weight: bold; - } - - dd { - display: inline; - margin-left: 0.5em; - } -` - -export const regionIntersections = ( - regionArrays: { start: number; stop: number }[][] -): RegionalMissenseConstraintRegion[] => { - const sortedRegionsArrays = regionArrays.map((regions) => - [...regions].sort((a, b) => a.start - b.start) - ) - - const intersections = [] - - const indices = sortedRegionsArrays.map(() => 0) - - while (sortedRegionsArrays.every((regions, i) => indices[i] < regions.length)) { - const maxStart = Math.max(...sortedRegionsArrays.map((regions, i) => regions[indices[i]].start)) - const minStop = Math.min(...sortedRegionsArrays.map((regions, i) => regions[indices[i]].stop)) - - if (maxStart < minStop) { - const next = Object.assign( - // @ts-ignore TS2556: A spread argument must either have a tuple type or be ... - ...[ - {}, - ...sortedRegionsArrays.map((regions: { [x: string]: any }, i) => regions[indices[i]]), - { - start: maxStart, - stop: minStop, - }, - ] - ) - - intersections.push(next) - } - - sortedRegionsArrays.forEach((regions, i) => { - if (regions[indices[i]].stop === minStop) { - indices[i] += 1 - } - }) - } - - return intersections -} - // https://colorbrewer2.org/#type=sequential&scheme=YlOrRd&n=5 const colorScale = { not_significant: '#e2e2e2', @@ -129,25 +60,16 @@ function regionColor(region: RegionalMissenseConstraintRegion) { return region.p_value > 0.001 ? colorScale.not_significant : color } -const LegendWrapper = styled.div` - display: flex; - - @media (max-width: 600px) { - flex-direction: column; - align-items: center; - } -` - const Legend = () => { return ( - + <> Missense observed/expected - - - - - + + + + + 0.0 @@ -168,12 +90,12 @@ const Legend = () => { - - + + Not significant (p > 1e-3) - + ) } @@ -193,11 +115,11 @@ const printAAorNA = (aa: string | null) => { } type RegionTooltipProps = { - region: RegionalMissenseConstraintRegion + region: RegionWithUnclamped isTranscriptWide: boolean } -const RegionTooltip = ({ region, isTranscriptWide }: RegionTooltipProps) => { +export const RegionTooltip = ({ region, isTranscriptWide }: RegionTooltipProps) => { if (isTranscriptWide) { return ( @@ -216,7 +138,7 @@ const RegionTooltip = ({ region, isTranscriptWide }: RegionTooltipProps) => {
Coordinates:
-
{`${region.chrom}:${region.region_start}-${region.region_stop}`}
+
{`${region.chrom}:${region.unclamped_start}-${region.unclamped_stop}`}
Amino acids:
@@ -239,34 +161,47 @@ const RegionTooltip = ({ region, isTranscriptWide }: RegionTooltipProps) => { ) } -const SidePanel = styled.div` - display: flex; - align-items: center; - height: 100%; -` - -const TopPanel = styled.div` - display: flex; - justify-content: flex-end; - width: 100%; - margin-bottom: 5px; -` - export type RegionalMissenseConstraint = { has_no_rmc_evidence: boolean passed_qc: boolean regions: RegionalMissenseConstraintRegion[] } +const formattedOE = (region: RegionalMissenseConstraintRegion) => region.obs_exp.toFixed(2) + type Props = { - regionalMissenseConstraint?: RegionalMissenseConstraint + regionalMissenseConstraint: RegionalMissenseConstraint | null gene: Gene } -type TrackProps = { - scalePosition: (input: number) => number - width: number -} +const NoRMCConstraint = () => ( + ( + + Regional missense constraint + + + )} + > + {({ width }: { width: number }) => ( + <> + + + + + This gene was not searched for evidence of regional missense constraint. See our{' '} + + + help page + + for additional information. + + + + + )} + +) const RegionalMissenseConstraintTrack = ({ regionalMissenseConstraint, gene }: Props) => { if ( @@ -275,34 +210,7 @@ const RegionalMissenseConstraintTrack = ({ regionalMissenseConstraint, gene }: P (regionalMissenseConstraint.passed_qc === false && regionalMissenseConstraint.has_no_rmc_evidence === false) ) { - return ( - ( - - Regional missense constraint - - - )} - > - {({ width }: { width: number }) => ( - <> - - - - - This gene was not searched for evidence of regional missense constraint. See our{' '} - - - help page - - for additional information. - - - - - )} - - ) + return } // This transcript was searched, but no RMC evidence was found @@ -319,8 +227,6 @@ const RegionalMissenseConstraintTrack = ({ regionalMissenseConstraint, gene }: P chrom: gene.chrom, start: Math.min(gene.start, gene.stop), stop: Math.max(gene.start, gene.stop), - region_start: Math.min(gene.start, gene.stop), - region_stop: Math.max(gene.start, gene.stop), obs_mis: gene.gnomad_constraint.obs_mis, exp_mis: gene.gnomad_constraint.exp_mis, obs_exp: gene.gnomad_constraint.oe_mis, @@ -334,118 +240,22 @@ const RegionalMissenseConstraintTrack = ({ regionalMissenseConstraint, gene }: P } } - const constrainedExons = regionIntersections([ - regionalMissenseConstraint.regions.map((region) => { - return { - ...region, - region_start: region.start, - region_stop: region.stop, - } - }), - gene.exons.filter((exon) => exon.feature_type === 'CDS'), - ]) + const constraintInCodingSections = regionsInExons( + regionalMissenseConstraint.regions, + gene.exons.filter((exon) => exon.feature_type === 'CDS') + ) return ( - - ( - - Regional missense constraint - - - )} - > - {({ scalePosition, width }: TrackProps) => ( - <> - - - - - - {constrainedExons.map((region: RegionalMissenseConstraintRegion) => { - const startX = scalePosition(region.start) - const stopX = scalePosition(region.stop) - const regionWidth = stopX - startX - - return ( - - - - - - ) - })} - - {regionalMissenseConstraint.regions.map( - (region: RegionalMissenseConstraintRegion, index: number) => { - const startX = scalePosition(region.start) - const stopX = scalePosition(region.stop) - const regionWidth = stopX - startX - const midX = (startX + stopX) / 2 - // const offset = index * 15 - const offset = index * 0 - - return ( - - - - - {regionWidth > 40 && ( - <> - - - {region.obs_exp.toFixed(2)} - - - )} - - ) - } - )} - - - - - )} - - + } + tooltipComponent={RegionTooltip} + valueFn={formattedOE} + /> ) } diff --git a/browser/src/__factories__/Gene.ts b/browser/src/__factories__/Gene.ts index 5dc208164..5ebd783e6 100644 --- a/browser/src/__factories__/Gene.ts +++ b/browser/src/__factories__/Gene.ts @@ -1,6 +1,6 @@ import { Factory } from 'fishery' import { Gene, GeneMetadata } from '../GenePage/GenePage' -import { Transcript } from '../TranscriptPage/TranscriptPage' +import { Transcript, Exon } from '../TranscriptPage/TranscriptPage' import transcriptFactory from './Transcript' import { HeterozygousVariantCooccurrenceCountsPerSeverityAndAfFactory, @@ -8,6 +8,11 @@ import { } from './VariantCooccurrenceCountsPerSeverityAndAf' import { pextFactory } from './TissueExpression' +export const exonFactory = Factory.define(({ params }) => { + const { feature_type = 'CDS', start = 100, stop = 200 } = params + return { feature_type, start, stop } +}) + const geneFactory = Factory.define(({ params, associations }) => { const { gene_id = 'dummy_gene-1', @@ -24,15 +29,36 @@ const geneFactory = Factory.define(({ params, associations }) => { structural_variants = [], clinvar_variants = [], copy_number_variants = [], + name = null, } = params + const { + mane_select_transcript = null, + gnomad_constraint = null, + exac_constraint = null, + pext = pextFactory.build(), + short_tandem_repeats = null, + exac_regional_missense_constraint_regions = null, + gnomad_v2_regional_missense_constraint = null, + mitochondrial_constraint = null, + mitochondrial_missense_constraint_regions = null, + } = associations + const heterozygous_variant_cooccurrence_counts = associations.heterozygous_variant_cooccurrence_counts || HeterozygousVariantCooccurrenceCountsPerSeverityAndAfFactory.build() const homozygous_variant_cooccurrence_counts = associations.homozygous_variant_cooccurrence_counts || HomozygousVariantCooccurrenceCountsPerSeverityAndAfFactory.build() - const metadata: GeneMetadata = { gene_id, gene_version, symbol, canonical_transcript_id, flags } + + const metadata: GeneMetadata = { + gene_id, + gene_version, + symbol, + canonical_transcript_id, + flags, + mane_select_transcript, + } const transcripts: Transcript[] = canonical_transcript_id !== null @@ -51,8 +77,6 @@ const geneFactory = Factory.define(({ params, associations }) => { ] : [] - const pext = pextFactory.build() - return { gene_id, gene_version, @@ -72,7 +96,16 @@ const geneFactory = Factory.define(({ params, associations }) => { structural_variants, clinvar_variants, copy_number_variants, + mane_select_transcript, + name, + gnomad_constraint, + exac_constraint, pext, + short_tandem_repeats, + exac_regional_missense_constraint_regions, + gnomad_v2_regional_missense_constraint, + mitochondrial_constraint, + mitochondrial_missense_constraint_regions, } }) diff --git a/browser/src/__factories__/GeneMetadata.ts b/browser/src/__factories__/GeneMetadata.ts index de83a8744..a736f9d95 100644 --- a/browser/src/__factories__/GeneMetadata.ts +++ b/browser/src/__factories__/GeneMetadata.ts @@ -7,6 +7,7 @@ const geneMetadataFactory = Factory.define(() => ({ symbol: 'FAKEGENE', canonical_transcript_id: 'some-transcript', flags: [], + mane_select_transcript: null, })) export default geneMetadataFactory diff --git a/browser/src/__snapshots__/RegionalMissenseConstraintTrack.spec.tsx.snap b/browser/src/__snapshots__/RegionalMissenseConstraintTrack.spec.tsx.snap new file mode 100644 index 000000000..321aabf30 --- /dev/null +++ b/browser/src/__snapshots__/RegionalMissenseConstraintTrack.spec.tsx.snap @@ -0,0 +1,316 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RegionalMissenseConstraint has no unexpected changes when the RMC has evidence and passed QC 1`] = ` +
+
+
+
+
+ + Regional missense constraint + + +
+
+
+
+
+ + Missense observed/expected + + + + + + + + + 0.0 + + + 0.2 + + + 0.4 + + + 0.6 + + + 0.8 + + + 1.0+ + + + + + + Not significant (p > 1e-3) + + +
+
+
+ + + + + + + + + + + + + + + 0.35 + + + + + + + + + 0.37 + + + + +
+
+
+
+
+`; diff --git a/deploy/dockerfiles/browser/api.dockerfile b/deploy/dockerfiles/browser/api.dockerfile index 60ff45fbe..5a0ee2d8e 100644 --- a/deploy/dockerfiles/browser/api.dockerfile +++ b/deploy/dockerfiles/browser/api.dockerfile @@ -24,4 +24,8 @@ COPY --chown=node:node tsconfig.build.json /app/graphql-api/tsconfig.build.json # Build JS from TS source RUN pnpm tsc -p /app/graphql-api/tsconfig.build.json + +# Copy static data into place +COPY --chown=node:node graphql-api/static_data /app/static_data + CMD ["node", "graphql-api/src/app.js"] diff --git a/deploy/dockerfiles/browser/api.dockerfile.dockerignore b/deploy/dockerfiles/browser/api.dockerfile.dockerignore index a3de25893..779200d8f 100644 --- a/deploy/dockerfiles/browser/api.dockerfile.dockerignore +++ b/deploy/dockerfiles/browser/api.dockerfile.dockerignore @@ -7,3 +7,4 @@ !pnpm-lock.yaml !pnpm-workspace-api-docker.yaml !package.json +!graphql-api/static_data diff --git a/graphql-api/src/graphql/resolvers/gene-fields.ts b/graphql-api/src/graphql/resolvers/gene-fields.ts index 597856bc7..df51b100f 100644 --- a/graphql-api/src/graphql/resolvers/gene-fields.ts +++ b/graphql-api/src/graphql/resolvers/gene-fields.ts @@ -1,4 +1,9 @@ import { isEmpty } from 'lodash' +import { + resolveMitochondrialGeneConstraint, + resolveMitochondialGeneConstraintType, + resolveMitochondrialRegionConstraint, +} from './mitochondrial-constraint' const nullifyEmptyObject = (field: any) => { return (obj: any) => { @@ -14,6 +19,8 @@ const resolvers = { pext: nullifyEmptyObject('pext'), gnomad_constraint: nullifyEmptyObject('gnomad_constraint'), exac_constraint: nullifyEmptyObject('exac_constraint'), + mitochondrial_constraint: resolveMitochondrialGeneConstraint, + mitochondrial_missense_constraint_regions: resolveMitochondrialRegionConstraint, }, TranscriptGene: { // Elasticsearch documents may contain an empty object instead of null @@ -22,6 +29,9 @@ const resolvers = { gnomad_constraint: nullifyEmptyObject('gnomad_constraint'), exac_constraint: nullifyEmptyObject('exac_constraint'), }, + MitochondrialGeneConstraint: { + __resolveType: resolveMitochondialGeneConstraintType, + }, } export default resolvers diff --git a/graphql-api/src/graphql/resolvers/mitochondrial-constraint.ts b/graphql-api/src/graphql/resolvers/mitochondrial-constraint.ts new file mode 100644 index 000000000..80f46d6c5 --- /dev/null +++ b/graphql-api/src/graphql/resolvers/mitochondrial-constraint.ts @@ -0,0 +1,214 @@ +import { readFileSync } from 'node:fs' + +type ProteinMitochondrialGeneConstraint = { + exp_lof: number + exp_mis: number + exp_syn: number + + obs_lof: number + obs_mis: number + obs_syn: number + + oe_lof: number + oe_lof_lower: number + oe_lof_upper: number + + oe_mis: number + oe_mis_lower: number + oe_mis_upper: number + + oe_syn: number + oe_syn_lower: number + oe_syn_upper: number +} + +type RNAMitochondrialGeneConstraint = { + observed: number + expected: number + oe: number + oe_upper: number + oe_lower: number +} + +type MitochondrialGeneConstraint = + | ProteinMitochondrialGeneConstraint + | RNAMitochondrialGeneConstraint + +type MitochondrialRegionConstraint = { + start: number + stop: number + oe: number + oe_upper: number + oe_lower: number +} + +const emptyConstraint: MitochondrialGeneConstraint = { + exp_lof: 0, + exp_mis: 0, + exp_syn: 0, + + obs_lof: 0, + obs_mis: 0, + obs_syn: 0, + + oe_lof: 0, + oe_lof_lower: 0, + oe_lof_upper: 0, + + oe_mis: 0, + oe_mis_lower: 0, + oe_mis_upper: 0, + + oe_syn: 0, + oe_syn_lower: 0, + oe_syn_upper: 0, +} + +const updateProteinMitochondrialGeneConstraint = ( + previousConstraint: ProteinMitochondrialGeneConstraint, + consequence: string, + observed: string, + expected: string, + oe: string, + lowerCI: string, + upperCI: string +): ProteinMitochondrialGeneConstraint => { + if (consequence === 'synonymous') { + return { + ...previousConstraint, + exp_syn: Number(expected), + obs_syn: Number(observed), + oe_syn: Number(oe), + oe_syn_lower: Number(lowerCI), + oe_syn_upper: Number(upperCI), + } + } + if (consequence === 'missense') { + return { + ...previousConstraint, + exp_mis: Number(expected), + obs_mis: Number(observed), + oe_mis: Number(oe), + oe_mis_lower: Number(lowerCI), + oe_mis_upper: Number(upperCI), + } + } + return { + ...previousConstraint, + exp_lof: Number(expected), + obs_lof: Number(observed), + oe_lof: Number(oe), + oe_lof_lower: Number(lowerCI), + oe_lof_upper: Number(upperCI), + } +} + +const parseMitochondrialGeneConstraintTSV = ( + rawTSV: string +): Record => + rawTSV + .split(/\n/) + .slice(1, -1) + .reduce((result, rowText) => { + const [ + geneSymbol, + _startPosition, + _endPosition, + consequence, + observed, + expected, + oe, + lowerCI, + upperCI, + ] = rowText.split('\t') + if (consequence === 'RNA_variant') { + const rnaConstraint: RNAMitochondrialGeneConstraint = { + observed: Number(observed), + expected: Number(expected), + oe: Number(oe), + oe_upper: Number(upperCI), + oe_lower: Number(lowerCI), + } + return { ...result, [geneSymbol]: rnaConstraint } + } + + const previousConstraint: ProteinMitochondrialGeneConstraint = result[geneSymbol] + ? (result[geneSymbol] as ProteinMitochondrialGeneConstraint) + : emptyConstraint + + const newConstraint = updateProteinMitochondrialGeneConstraint( + previousConstraint, + consequence, + observed, + expected, + oe, + lowerCI, + upperCI + ) + + return { ...result, [geneSymbol]: newConstraint } + }, {} as Record) + +const parseMitochondrialRegionConstraintTSV = ( + rawTSV: string, + geneSymbols: string[] +): Record => { + const nonTRNASymbols = geneSymbols.filter((geneSymbol) => !geneSymbol.startsWith('MT-T')) + const seed: Record = nonTRNASymbols.reduce( + (result, geneSymbol) => ({ ...result, [geneSymbol]: [] }), + {} as Record + ) + + return rawTSV + .split(/\n/) + .slice(1, -1) + .reduce((result, rowText) => { + const [ + geneSymbol, + startPosition, + endPosition, + _proteinResidueStart, + _proteinResidueEnd, + _observed, + _expected, + oe, + lowerCI, + upperCI, + ] = rowText.split('\t') + + const previousConstraints = result[geneSymbol] || [] + + const newConstraint: MitochondrialRegionConstraint = { + start: Number(startPosition), + stop: Number(endPosition), + oe: Number(oe), + oe_upper: Number(upperCI), + oe_lower: Number(lowerCI), + } + return { ...result, [geneSymbol]: [...previousConstraints, newConstraint] } + }, seed) +} + +const GENE_CONSTRAINT_FILENAME = `${process.env.PWD}/static_data/mito_gene_constraint_metrics.tsv` +const geneConstraint = parseMitochondrialGeneConstraintTSV( + readFileSync(GENE_CONSTRAINT_FILENAME, { encoding: 'utf8' }) +) + +const allGeneSymbols = Object.keys(geneConstraint) + +const REGION_CONSTRAINT_FILENAME = `${process.env.PWD}/static_data/mito_region_constraint_metrics.tsv` +const regionConstraint = parseMitochondrialRegionConstraintTSV( + readFileSync(REGION_CONSTRAINT_FILENAME, { encoding: 'utf8' }), + allGeneSymbols +) +export const resolveMitochondialGeneConstraintType = (constraint: any): string => + Object.prototype.hasOwnProperty.call(constraint, 'exp_lof') + ? 'ProteinMitochondrialGeneConstraint' + : 'RNAMitochondrialGeneConstraint' + +export const resolveMitochondrialGeneConstraint = (gene: any) => { + return geneConstraint[gene.symbol] +} + +export const resolveMitochondrialRegionConstraint = (gene: any): MitochondrialRegionConstraint[] => + regionConstraint[gene.symbol] diff --git a/graphql-api/src/graphql/types/constraint/mitochondrial-constraint.graphql b/graphql-api/src/graphql/types/constraint/mitochondrial-constraint.graphql new file mode 100644 index 000000000..8579ecbd7 --- /dev/null +++ b/graphql-api/src/graphql/types/constraint/mitochondrial-constraint.graphql @@ -0,0 +1,41 @@ +type ProteinMitochondrialGeneConstraint { + exp_lof: Float! + exp_mis: Float! + exp_syn: Float! + + obs_lof: Float! + obs_mis: Float! + obs_syn: Float! + + oe_lof: Float! + oe_lof_lower: Float! + oe_lof_upper: Float! + + oe_mis: Float! + oe_mis_lower: Float! + oe_mis_upper: Float! + + oe_syn: Float! + oe_syn_lower: Float! + oe_syn_upper: Float! +} + +type RNAMitochondrialGeneConstraint { + observed: Float! + expected: Float! + oe: Float! + oe_upper: Float! + oe_lower: Float! +} + +union MitochondrialGeneConstraint = + ProteinMitochondrialGeneConstraint + | RNAMitochondrialGeneConstraint + +type MitochondrialRegionConstraint { + start: Int! + stop: Int! + oe: Float! + oe_upper: Float! + oe_lower: Float! +} diff --git a/graphql-api/src/graphql/types/gene.graphql b/graphql-api/src/graphql/types/gene.graphql index e41c6bda5..67c967f5b 100644 --- a/graphql-api/src/graphql/types/gene.graphql +++ b/graphql-api/src/graphql/types/gene.graphql @@ -69,6 +69,9 @@ type Gene { exac_constraint: ExacConstraint exac_regional_missense_constraint_regions: [ExacRegionalMissenseConstraintRegion!] + mitochondrial_constraint: MitochondrialGeneConstraint + mitochondrial_missense_constraint_regions: [MitochondrialRegionConstraint] + variants(dataset: DatasetId!): [Variant!]! @cost(value: 10) structural_variants(dataset: StructuralVariantDatasetId!): [StructuralVariant!]! @cost(value: 10) mitochondrial_variants(dataset: DatasetId!): [MitochondrialVariant!]! @cost(value: 10) diff --git a/graphql-api/static_data/mito_gene_constraint_metrics.tsv b/graphql-api/static_data/mito_gene_constraint_metrics.tsv new file mode 100644 index 000000000..45297bf7b --- /dev/null +++ b/graphql-api/static_data/mito_gene_constraint_metrics.tsv @@ -0,0 +1,64 @@ +Symbol Start_position End_position Consequence Observed Expected obs:exp Lower_CI Upper_CI +MT-TF 577 647 RNA_variant 25.53 85.59 0.298 0.209 0.392 +MT-RNR1 648 1601 RNA_variant 315.935 1229.075 0.257 0.234 0.28 +MT-TV 1602 1670 RNA_variant 30.14 89.475 0.337 0.245 0.432 +MT-RNR2 1671 3229 RNA_variant 568.854 2001.866 0.284 0.265 0.303 +MT-TL1 3230 3304 RNA_variant 13.634 92.721 0.147 0.085 0.213 +MT-ND1 3307 4262 synonymous 386.228 381.239 1.013 0.954 1.071 +MT-ND1 3307 4262 missense 189.988 745.431 0.255 0.225 0.284 +MT-ND1 3307 4262 stop_gain 0 35.947 0 0 0.032 +MT-TI 4263 4331 RNA_variant 16.024 88.245 0.182 0.112 0.256 +MT-TQ 4329 4400 RNA_variant 30.153 83.308 0.362 0.263 0.465 +MT-TM 4402 4469 RNA_variant 11.738 84.913 0.138 0.076 0.206 +MT-ND2 4470 5511 synonymous 393.149 414.48 0.949 0.893 1.003 +MT-ND2 4470 5511 missense 199.414 776.566 0.257 0.228 0.286 +MT-ND2 4470 5511 stop_gain 0.997 46.099 0.022 0.002 0.065 +MT-TW 5512 5579 RNA_variant 28.684 84.668 0.339 0.244 0.438 +MT-TA 5587 5655 RNA_variant 28.553 82.395 0.347 0.249 0.448 +MT-TN 5657 5729 RNA_variant 15.479 90.574 0.171 0.104 0.242 +MT-TC 5761 5826 RNA_variant 37.535 77.28 0.486 0.37 0.605 +MT-TY 5826 5891 RNA_variant 13.651 78.404 0.174 0.102 0.252 +MT-CO1 5904 7445 synonymous 616.941 596.473 1.034 0.988 1.08 +MT-CO1 5904 7445 missense 196.443 1238.397 0.159 0.14 0.177 +MT-CO1 5904 7445 stop_gain 0.876 66.802 0.013 0.001 0.042 +MT-TS1 7446 7514 RNA_variant 23.9 79.543 0.3 0.207 0.398 +MT-TD 7518 7585 RNA_variant 26.981 81.254 0.332 0.236 0.433 +MT-CO2 7586 8269 synonymous 258.031 263.3 0.98 0.911 1.048 +MT-CO2 7586 8269 missense 139.714 553.415 0.252 0.218 0.286 +MT-CO2 7586 8269 stop_gain 0 25.056 0 0 0.046 +MT-TK 8295 8364 RNA_variant 29.83 88.812 0.336 0.244 0.432 +MT-ATP8 8366 8572 synonymous 55.915 65.156 0.858 0.725 0.991 +MT-ATP8 8366 8572 missense 113.565 131.226 0.865 0.751 0.98 +MT-ATP8 8366 8572 stop_gain 0.282 13.71 0.021 0.002 0.12 +MT-ATP6 8527 9207 synonymous 259.191 259.196 1 0.929 1.07 +MT-ATP6 8527 9207 missense 341.214 512.933 0.665 0.613 0.717 +MT-ATP6 8527 9207 stop_gain 0 22.15 0 0 0.052 +MT-CO3 9207 9990 synonymous 291.72 298.907 0.976 0.91 1.041 +MT-CO3 9207 9990 missense 180.732 609.607 0.296 0.262 0.331 +MT-CO3 9207 9990 stop_gain 1.499 37.947 0.04 0.004 0.101 +MT-TG 9991 10058 RNA_variant 31.061 87.541 0.355 0.26 0.453 +MT-ND3 10059 10404 synonymous 126.104 149.164 0.845 0.758 0.931 +MT-ND3 10059 10404 missense 48.469 253.692 0.191 0.147 0.236 +MT-ND3 10059 10404 stop_gain 0 13.903 0 0 0.082 +MT-TR 10405 10469 RNA_variant 16.146 78.79 0.205 0.127 0.288 +MT-ND4L 10470 10766 synonymous 105.674 125.436 0.842 0.743 0.941 +MT-ND4L 10470 10766 missense 26.989 223.68 0.121 0.083 0.159 +MT-ND4L 10470 10766 stop_gain 0 3.098 0 0 0.367 +MT-ND4 10760 12137 synonymous 540.717 555.503 0.973 0.925 1.021 +MT-ND4 10760 12137 missense 156.343 1047.858 0.149 0.129 0.169 +MT-ND4 10760 12137 stop_gain 0 52.597 0 0 0.022 +MT-TH 12138 12206 RNA_variant 39.485 88.312 0.447 0.343 0.554 +MT-TS2 12207 12265 RNA_variant 24.474 69.66 0.351 0.245 0.463 +MT-TL2 12266 12336 RNA_variant 28.734 85.365 0.337 0.242 0.435 +MT-ND5 12337 14148 synonymous 697.227 684.692 1.018 0.974 1.062 +MT-ND5 12337 14148 missense 335.183 1410.227 0.238 0.216 0.258 +MT-ND5 12337 14148 stop_gain 0 76.547 0 0 0.015 +MT-ND6 14149 14673 synonymous 194.58 202.362 0.962 0.883 1.039 +MT-ND6 14149 14673 missense 105.249 383.502 0.274 0.232 0.317 +MT-ND6 14149 14673 stop_gain 0.133 29.782 0.004 0 0.042 +MT-TE 14674 14742 RNA_variant 19.147 89.468 0.214 0.139 0.293 +MT-CYB 14747 15887 synonymous 466.867 419.878 1.112 1.055 1.167 +MT-CYB 14747 15887 missense 350.202 896.021 0.391 0.358 0.423 +MT-CYB 14747 15887 stop_gain 0 48.498 0 0 0.024 +MT-TT 15888 15953 RNA_variant 62.231 83.619 0.744 0.616 0.874 +MT-TP 15956 16023 RNA_variant 20.54 79.526 0.258 0.171 0.35 diff --git a/graphql-api/static_data/mito_region_constraint_metrics.tsv b/graphql-api/static_data/mito_region_constraint_metrics.tsv new file mode 100644 index 000000000..e391cadc2 --- /dev/null +++ b/graphql-api/static_data/mito_region_constraint_metrics.tsv @@ -0,0 +1,42 @@ +Symbol Start_position End_position Protein_residue_start Protein_residue_end Observed Expected obs:exp Lower_CI Upper_CI +MT-ATP6 9001 9036 159 170 10.206 29.619 0.345 0.186 0.517 +MT-CO1 6129 6227 76 108 3.041 77.242 0.039 0.008 0.08 +MT-CO1 6573 6644 224 247 0.1 52.21 0.002 0 0.022 +MT-CO1 6759 6809 286 302 0 45.398 0 0 0.025 +MT-CO1 7176 7235 425 444 0.467 46.784 0.01 0.001 0.044 +MT-CO2 8036 8074 151 163 0.417 30.306 0.014 0.001 0.064 +MT-CO2 8159 8209 192 208 0.282 41.187 0.007 0 0.04 +MT-CO3 9237 9266 11 20 0.325 18 0.018 0.002 0.097 +MT-CO3 9393 9422 63 72 0.362 23.256 0.016 0.001 0.079 +MT-CO3 9762 9800 186 198 2.557 34.116 0.075 0.013 0.159 +MT-CO3 9912 9947 236 247 2.043 29.384 0.07 0.01 0.159 +MT-CYB 14804 14857 20 37 6.999 41.328 0.169 0.073 0.277 +MT-CYB 15134 15199 130 151 9.066 48.581 0.187 0.092 0.29 +MT-CYB 15332 15376 196 210 5.2 31.095 0.167 0.058 0.292 +MT-CYB 15536 15574 264 276 0 21.884 0 0 0.052 +MT-ND1 3436 3486 44 60 2.552 33.677 0.076 0.013 0.161 +MT-ND1 3664 3708 120 134 2.214 37.251 0.059 0.009 0.132 +MT-ND1 3868 3990 188 228 8.704 104.911 0.083 0.039 0.131 +MT-ND2 4515 4595 16 42 7.25 61.697 0.118 0.051 0.192 +MT-ND2 4773 4811 102 114 0.303 24.998 0.012 0.001 0.068 +MT-ND2 4845 4883 126 138 0 22.104 0 0 0.052 +MT-ND2 4968 5000 167 177 0 20.517 0 0 0.056 +MT-ND2 5214 5261 249 264 0.364 29.123 0.012 0.001 0.063 +MT-ND2 5334 5417 289 316 2.036 62.913 0.032 0.004 0.074 +MT-ND3 10260 10304 68 82 0.269 27.926 0.01 0.001 0.058 +MT-ND4 10946 11002 63 81 0 37.999 0 0 0.03 +MT-ND4 11381 11431 208 224 0.101 33.803 0.003 0 0.034 +MT-ND4L 10554 10598 29 43 0 25.956 0 0 0.044 +MT-ND5 12970 13062 212 242 2.352 60.78 0.039 0.006 0.085 +MT-ND5 13390 13428 352 364 0.104 23.879 0.004 0 0.049 +MT-ND5 13480 13515 382 393 0.139 26.144 0.005 0.001 0.049 +MT-ND6 14437 14478 79 66 2.512 34 0.074 0.013 0.158 +MT-ND6 14584 14625 30 17 1.207 29.575 0.041 0.004 0.114 +MT-RNR1 871 920 NA NA 7.939 59.668 0.133 0.061 0.213 +MT-RNR1 1054 1093 NA NA 2.623 50.311 0.052 0.009 0.11 +MT-RNR1 1132 1151 NA NA 0.612 25.179 0.024 0.002 0.092 +MT-RNR1 1557 1596 NA NA 2.182 53.304 0.041 0.006 0.092 +MT-RNR2 1767 1803 NA NA 6.748 48.14 0.14 0.059 0.231 +MT-RNR2 2001 2043 NA NA 4.476 53.79 0.083 0.025 0.152 +MT-RNR2 2448 2601 NA NA 21.045 194.698 0.108 0.071 0.147 +MT-RNR2 3028 3081 NA NA 2.202 70.922 0.031 0.004 0.069 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a44903aa7..180a3cfff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,6 +183,9 @@ importers: '@visx/axis': specifier: ^3.0.0 version: 3.5.0(react@17.0.2) + '@visx/group': + specifier: ^3.0.0 + version: 3.3.0(react@17.0.2) core-js: specifier: 3.5.0 version: 3.5.0