Skip to content

Commit

Permalink
Merge pull request #37 from molgenis/feat/inheritanceMatchMissing
Browse files Browse the repository at this point in the history
Feat: Inheritance Match 'missing' instead of '0' if Gene modes are unknown.
  • Loading branch information
dennishendriksen authored Sep 11, 2023
2 parents c084389 + c92e70f commit e998b0d
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 52 deletions.
33 changes: 21 additions & 12 deletions src/main/java/org/molgenis/vcf/inheritance/matcher/Annotator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.molgenis.vcf.inheritance.matcher;

import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMatch.TRUE;
import static org.molgenis.vcf.utils.utils.HeaderUtils.fixVcfFilterHeaderLines;
import static org.molgenis.vcf.utils.utils.HeaderUtils.fixVcfFormatHeaderLines;
import static org.molgenis.vcf.utils.utils.HeaderUtils.fixVcfInfoHeaderLines;
Expand All @@ -19,10 +20,9 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.molgenis.vcf.inheritance.matcher.model.Annotation;
import org.molgenis.vcf.inheritance.matcher.model.Inheritance;
import org.molgenis.vcf.inheritance.matcher.model.InheritanceMode;
import org.molgenis.vcf.inheritance.matcher.model.SubInheritanceMode;

import org.molgenis.vcf.inheritance.matcher.model.*;
import org.molgenis.vcf.utils.UnexpectedEnumException;
import org.molgenis.vcf.utils.sample.model.Pedigree;
import org.molgenis.vcf.utils.sample.model.Sample;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -51,10 +51,10 @@ VCFHeader annotateHeader(VCFHeader vcfHeader) {
"Possible Compound hetrozygote variants."));
vcfHeader.addMetaDataLine(new VCFFormatHeaderLine(DENOVO, 1,
VCFHeaderLineType.Integer,
"Inheritance Denovo status."));
"De novo variant."));
vcfHeader.addMetaDataLine(new VCFFormatHeaderLine(INHERITANCE_MATCH, 1,
VCFHeaderLineType.Integer,
"Inheritance Match status."));
"Inheritance Match: Genotypes, affected statuses and known gene inheritance patterns match."));
vcfHeader.addMetaDataLine(new VCFFormatHeaderLine(MATCHING_GENES, VCFHeaderLineCount.UNBOUNDED,
VCFHeaderLineType.String,
"Genes with an inheritance match."));
Expand Down Expand Up @@ -108,13 +108,11 @@ private void annotateGenotype(VariantContext vc, Annotation annotation,
.join(",", annotation.getInheritance().getCompounds());
genotypeBuilder.attribute(POSSIBLE_COMPOUND, compounds);
genotypeBuilder.attribute(DENOVO, annotation.getInheritance().isDenovo() ? "1" : "0");
Set<String> genes = annotation.getMatchingGenes();
boolean isMatch = !(genes == null || genes.isEmpty());
InheritanceMatch match = annotation.getInheritance().getMatch();
String inheritanceMatch = mapInheritanceMatch(match);
genotypeBuilder
.attribute(INHERITANCE_MATCH,
isMatch
? "1" : "0");
if (isMatch) {
.attribute(INHERITANCE_MATCH, inheritanceMatch);
if (match == TRUE) {
genotypeBuilder
.attribute(MATCHING_GENES, annotation.getMatchingGenes().stream().sorted().collect(
Collectors.joining(",")));
Expand All @@ -124,6 +122,17 @@ private void annotateGenotype(VariantContext vc, Annotation annotation,
}
}

private static String mapInheritanceMatch(InheritanceMatch match) {
String inheritanceMatch;
switch (match){
case TRUE -> inheritanceMatch = "1";
case FALSE -> inheritanceMatch = "0";
case UNKNOWN -> inheritanceMatch = null;
default -> throw new UnexpectedEnumException(match);
}
return inheritanceMatch;
}

private Set<String> mapSubinheritanceModes(Inheritance inheritance) {
Set<String> result = new HashSet<>();
for (SubInheritanceMode inheritanceModeEnum : inheritance.getSubInheritanceModes()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.molgenis.vcf.inheritance.matcher.model.Annotation;
import org.molgenis.vcf.inheritance.matcher.model.Gene;
import org.molgenis.vcf.inheritance.matcher.model.Inheritance;
import org.molgenis.vcf.inheritance.matcher.model.InheritanceMode;

import org.molgenis.vcf.inheritance.matcher.model.*;

import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMatch.*;

public class InheritanceMatcher {

Expand All @@ -23,18 +23,47 @@ public static Map<String, Annotation> matchInheritance(
Set<String> matchingGenes = new HashSet<>();
String sample = entry.getKey();
Inheritance inheritance = entry.getValue();
for (Gene gene : genes) {
Set<InheritanceMode> geneInheritanceModes = gene
.getInheritanceModes();
if (geneInheritanceModes.stream()
.anyMatch(mode -> inheritance.getInheritanceModes().contains(mode))) {
matchingGenes.add(gene.getId());
}
//If no inheritance pattern is suitable for the sample, regardless of the gene: inheritance match is false.
if(inheritance.getInheritanceModes().isEmpty()){
inheritance.setMatch(FALSE);
}
else {
matchGeneInheritance(genes, matchingGenes, inheritance);
}
Annotation annotation = Annotation.builder().matchingGenes(matchingGenes)
.inheritance(inheritance).build();
sampleAnnotationMap.put(sample, annotation);
}
return sampleAnnotationMap;
}

/**
* If there are one or more matches between sample inheritance modes and gene inheritance modes:
* - inheritance match is true
* If there are no matches between sample inheritance modes and gene inheritance modes:
* - inheritance match is unknown if any genes for the variant have unknown inheritance pattern.
* - inheritance match is false if all genes for the variant have known (but mismatching) inheritance pattern.
*/
private static void matchGeneInheritance(Collection<Gene> genes, Set<String> matchingGenes, Inheritance inheritance) {
boolean containsUnknownGene = false;
for (Gene gene : genes) {
Set<InheritanceMode> geneInheritanceModes = gene
.getInheritanceModes();
if( geneInheritanceModes.isEmpty() ){
containsUnknownGene = true;
}
if (geneInheritanceModes.stream()
.anyMatch(mode -> inheritance.getInheritanceModes().contains(mode))) {
matchingGenes.add(gene.getId());
inheritance.setMatch(TRUE);
}
}
if(matchingGenes.isEmpty()) {
if (containsUnknownGene) {
inheritance.setMatch(UNKNOWN);
} else {
inheritance.setMatch(FALSE);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
@Data
@Builder
public class Inheritance {

@Builder.Default
Set<InheritanceMode> inheritanceModes = new HashSet<>();
@Builder.Default
Set<SubInheritanceMode> subInheritanceModes = new HashSet<>();
@Builder.Default
Set<String> compounds = new HashSet<>();

@Builder.Default
InheritanceMatch match = InheritanceMatch.UNKNOWN;

@Builder.Default
boolean denovo = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.molgenis.vcf.inheritance.matcher.model;

public enum InheritanceMatch {
TRUE, FALSE, UNKNOWN
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.molgenis.vcf.inheritance.matcher;

import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand All @@ -15,6 +15,8 @@
import static org.molgenis.vcf.inheritance.matcher.Annotator.POSSIBLE_COMPOUND;
import static org.molgenis.vcf.inheritance.matcher.Annotator.SUBINHERITANCE_MODES;
import static org.molgenis.vcf.inheritance.matcher.checker.PedigreeTestUtil.createFamily;
import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMatch.FALSE;
import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMatch.TRUE;
import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMode.AR;
import static org.molgenis.vcf.inheritance.matcher.model.InheritanceMode.AD;
import static org.molgenis.vcf.inheritance.matcher.util.VariantContextTestUtil.createGenotype;
Expand All @@ -27,7 +29,6 @@
import htsjdk.variant.vcf.VCFHeaderLineCount;
import htsjdk.variant.vcf.VCFHeaderLineType;
import htsjdk.variant.vcf.VCFInfoHeaderLine;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -71,10 +72,10 @@ void annotateHeader() {
"Possible Compound hetrozygote variants.")),
() -> verify(vcfHeader).addMetaDataLine(new VCFFormatHeaderLine(DENOVO, 1,
VCFHeaderLineType.Integer,
"Inheritance Denovo status.")),
"De novo variant.")),
() -> verify(vcfHeader).addMetaDataLine(new VCFFormatHeaderLine(INHERITANCE_MATCH, 1,
VCFHeaderLineType.Integer,
"Inheritance Match status.")),
"Inheritance Match: Genotypes, affected statuses and known gene inheritance patterns match.")),
() -> verify(vcfHeader)
.addMetaDataLine(new VCFFormatHeaderLine(MATCHING_GENES, VCFHeaderLineCount.UNBOUNDED,
VCFHeaderLineType.String,
Expand Down Expand Up @@ -151,7 +152,7 @@ void annotateInheritance() {
AffectedStatus.UNAFFECTED, "FAM");
Map<String, Pedigree> families = Map.of("FAM", familyMap);

Inheritance inheritance = Inheritance.builder().denovo(true).inheritanceModes(
Inheritance inheritance = Inheritance.builder().match(TRUE).denovo(true).inheritanceModes(
Set.of(AD, AR))
.subInheritanceModes(Set.of(SubInheritanceMode.AD_IP, SubInheritanceMode.AR_C))
.compounds(singleton("OTHER_VARIANT")).build();
Expand All @@ -175,4 +176,65 @@ void annotateInheritance() {
actual.getGenotype("Patient").getExtendedAttribute(POSSIBLE_COMPOUND))
);
}

@Test
void annotateInheritanceMissingGeneInheritancePattern() {
Genotype genotype = createGenotype("Patient", "1/1");
VariantContext vc = new VariantContextBuilder().chr("1").start(1).stop(1).alleles("T", "A")
.genotypes(genotype)
.make();
Pedigree familyMap = createFamily(Sex.MALE, AffectedStatus.AFFECTED, AffectedStatus.UNAFFECTED,
AffectedStatus.UNAFFECTED, "FAM");
Map<String, Pedigree> families = Map.of("FAM", familyMap);

Inheritance inheritance = Inheritance.builder().denovo(true).inheritanceModes(
Set.of(AD, AR))
.subInheritanceModes(Set.of(SubInheritanceMode.AD_IP, SubInheritanceMode.AR_C))
.compounds(singleton("OTHER_VARIANT")).build();
Annotation annotation = Annotation.builder().inheritance(inheritance).build();
Map<String, Annotation> annotationMap = Map.of("Patient", annotation);

VariantContext actual = annotator.annotateInheritance(vc, families, annotationMap);

assertAll(
() -> assertEquals("AR,AD",
actual.getGenotype("Patient").getExtendedAttribute(INHERITANCE_MODES)),
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(INHERITANCE_MATCH)),
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(MATCHING_GENES)),
() -> assertEquals("1", actual.getGenotype("Patient").getExtendedAttribute(DENOVO)),
() -> assertEquals("AD_IP,AR_C",
actual.getGenotype("Patient").getExtendedAttribute(SUBINHERITANCE_MODES)),
() -> assertEquals("OTHER_VARIANT",
actual.getGenotype("Patient").getExtendedAttribute(POSSIBLE_COMPOUND))
);
}

@Test
void annotateInheritanceNoMatch() {
Genotype genotype = createGenotype("Patient", "1/1");
VariantContext vc = new VariantContextBuilder().chr("1").start(1).stop(1).alleles("T", "A")
.genotypes(genotype)
.make();
Pedigree familyMap = createFamily(Sex.MALE, AffectedStatus.AFFECTED, AffectedStatus.UNAFFECTED,
AffectedStatus.UNAFFECTED, "FAM");
Map<String, Pedigree> families = Map.of("FAM", familyMap);

Inheritance inheritance = Inheritance.builder().match(FALSE).denovo(true).inheritanceModes(
emptySet())
.subInheritanceModes(emptySet())
.compounds(emptySet()).build();
Annotation annotation = Annotation.builder().inheritance(inheritance).build();
Map<String, Annotation> annotationMap = Map.of("Patient", annotation);

VariantContext actual = annotator.annotateInheritance(vc, families, annotationMap);

assertAll(
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(INHERITANCE_MODES)),
() -> assertEquals("0", actual.getGenotype("Patient").getExtendedAttribute(INHERITANCE_MATCH)),
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(MATCHING_GENES)),
() -> assertEquals("1", actual.getGenotype("Patient").getExtendedAttribute(DENOVO)),
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(SUBINHERITANCE_MODES)),
() -> assertNull(actual.getGenotype("Patient").getExtendedAttribute(POSSIBLE_COMPOUND))
);
}
}
Loading

0 comments on commit e998b0d

Please sign in to comment.