Skip to content

Commit

Permalink
Merge pull request #651 from TheJacksonLaboratory/do-not-show-differe…
Browse files Browse the repository at this point in the history
…ntials-with-no-good-variants

Do not show the candidate disease unless there is a (predicted) pathogenic variant(s) in the associated gene
  • Loading branch information
ielis authored Mar 4, 2024
2 parents ca0f65d + 03b0bee commit 4809f03
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 7 deletions.
3 changes: 2 additions & 1 deletion docs/running.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ The configuration options tweak the analysis.
* ``--sdwndv``: show diseases even if no deleterious variants are found in the gene associated with the disease.
The option is a flag (takes no value) and its presence will lead to showing *all* diseases,
even those with no deleterious variants.
Only applicable to the HTML report when running with a VCF file (genotype-aware mode).
Only applicable to the HTML and TSV reports when running with a VCF file (genotype-aware mode).
The JSON report will include *all* diseases all the time.
* ``--transcript-db``: transcript database (default: ``RefSeq``), see :ref:`rsttx-dbs` for more info.
* ``--use-orphanet``: use `Orphanet <https://www.orpha.net/consor/cgi-bin/index.php>`_ annotations (default: ``false``).
* ``--strict``: use strict penalties if the genotype does not match the disease model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ protected OutputOptions createOutputOptions(String prefix) {
LrThreshold lrThreshold = output.lrThreshold == null ? LrThreshold.notInitialized() : LrThreshold.setToUserDefinedThreshold(output.lrThreshold);
MinDiagnosisCount minDiagnosisCount = output.minDifferentialsToShow == null ? MinDiagnosisCount.notInitialized() : MinDiagnosisCount.setToUserDefinedMinCount(output.minDifferentialsToShow);
return new OutputOptions(lrThreshold, minDiagnosisCount, runConfiguration.pathogenicityThreshold,
output.displayAllVariants, output.outdir, prefix);
output.displayAllVariants, runConfiguration.showDiseasesWithNoDeleteriousVariants,
output.outdir, prefix);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,22 @@ public enum GenotypeLrMatchType {
*/
@Deprecated(forRemoval = true)
// REMOVE(v3.0.0)
UNKNOWN
UNKNOWN;

/**
* Returns {@code true} if the genotype LR match indicates that the enclosing {@link GenotypeLrWithExplanation}
* was created for a gene that contains deleterious variants. This includes the genotype LRs generated
* for ClinVar variants, using the LIRICAL genotype model, etc...
* <p>
* Returns {@code false} if the genotype LR represents state with no deleterious variants in a gene.
*/
public boolean hasDeleteriousVariants() {
return switch (this) {
case NO_VARIANTS_DETECTED_AD, NO_VARIANTS_DETECTED_AR, UNKNOWN -> false;
case ONE_P_OR_LP_CLINVAR_ALLELE_IN_AD, LIRICAL_GT_MODEL,
ONE_DELETERIOUS_CLINVAR_VARIANT_IN_AD, TWO_DELETERIOUS_CLINVAR_VARIANTS_IN_AR,
TWO_P_OR_LP_CLINVAR_ALLELES_IN_AR, ONE_DELETERIOUS_VARIANT_IN_AR,
HIGH_NUMBER_OF_OBSERVED_PREDICTED_PATHOGENIC_VARIANTS -> true;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,26 @@ public final class OutputOptions {
private final LrThreshold lrThreshold;
private final MinDiagnosisCount minDiagnosisCount;
private final float pathogenicityThreshold;
// If set to true, all variants, pathogenic, VUSs, and benign will be shown in the report.
private final boolean displayAllVariants;
// If set to true, all differential diagnoses, even those with no deleterious variants in the associated gene
// will be shown in the report.
private final boolean showDiseasesWithNoDeleteriousVariants;
private final Path outputDirectory;
private final String prefix;

public OutputOptions(LrThreshold lrThreshold,
MinDiagnosisCount minDiagnosisCount,
float pathogenicityThreshold,
boolean displayAllVariants,
boolean showDiseasesWithNoDeleteriousVariants,
Path outputDirectory,
String prefix) {
this.lrThreshold = lrThreshold;
this.minDiagnosisCount = minDiagnosisCount;
this.pathogenicityThreshold = pathogenicityThreshold;
this.displayAllVariants = displayAllVariants;
this.showDiseasesWithNoDeleteriousVariants = showDiseasesWithNoDeleteriousVariants;
this.outputDirectory = outputDirectory;
this.prefix = prefix;
}
Expand All @@ -41,6 +47,10 @@ public boolean displayAllVariants() {
return displayAllVariants;
}

public boolean showDiseasesWithNoDeleteriousVariants() {
return showDiseasesWithNoDeleteriousVariants;
}

public Path outputDirectory() {
return outputDirectory;
}
Expand All @@ -58,13 +68,14 @@ public boolean equals(Object obj) {
Objects.equals(this.minDiagnosisCount, that.minDiagnosisCount) &&
Float.floatToIntBits(this.pathogenicityThreshold) == Float.floatToIntBits(that.pathogenicityThreshold) &&
this.displayAllVariants == that.displayAllVariants &&
this.showDiseasesWithNoDeleteriousVariants == that.showDiseasesWithNoDeleteriousVariants &&
Objects.equals(this.outputDirectory, that.outputDirectory) &&
Objects.equals(this.prefix, that.prefix);
}

@Override
public int hashCode() {
return Objects.hash(lrThreshold, minDiagnosisCount, pathogenicityThreshold, displayAllVariants, outputDirectory, prefix);
return Objects.hash(lrThreshold, minDiagnosisCount, pathogenicityThreshold, displayAllVariants, showDiseasesWithNoDeleteriousVariants, outputDirectory, prefix);
}

@Override
Expand All @@ -74,6 +85,7 @@ public String toString() {
"minDiagnosisCount=" + minDiagnosisCount + ", " +
"pathogenicityThreshold=" + pathogenicityThreshold + ", " +
"displayAllVariants=" + displayAllVariants + ", " +
"showDiseasesWithNoDeleteriousVariants=" + showDiseasesWithNoDeleteriousVariants + ", " +
"outputDirectory=" + outputDirectory + ", " +
"prefix=" + prefix + ']';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.monarchinitiative.lirical.core.likelihoodratio;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class GenotypeLrMatchTypeTest {

@ParameterizedTest
@CsvSource(
{
"NO_VARIANTS_DETECTED_AD, false",
"NO_VARIANTS_DETECTED_AR, false",
"UNKNOWN, false",
"ONE_P_OR_LP_CLINVAR_ALLELE_IN_AD, true",
"LIRICAL_GT_MODEL, true",
"ONE_DELETERIOUS_CLINVAR_VARIANT_IN_AD, true",
"TWO_DELETERIOUS_CLINVAR_VARIANTS_IN_AR, true",
"TWO_P_OR_LP_CLINVAR_ALLELES_IN_AR, true",
"ONE_DELETERIOUS_VARIANT_IN_AR, true",
"HIGH_NUMBER_OF_OBSERVED_PREDICTED_PATHOGENIC_VARIANTS, true",
}
)
public void hasDeleteriousVariants(String value, boolean expected) {
// We depend on correctness of `hasDeleteriousVariants` in HTML and TSV report generators,
// where we may choose to only show the diff dgs with a deleterious variant.
GenotypeLrMatchType glmt = GenotypeLrMatchType.valueOf(value);

assertThat(glmt.hasDeleteriousVariants(), equalTo(expected));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import org.monarchinitiative.lirical.core.analysis.AnalysisResults;
import org.monarchinitiative.lirical.core.likelihoodratio.GenotypeLrWithExplanation;
import org.monarchinitiative.lirical.core.model.Gene2Genotype;
import org.monarchinitiative.lirical.core.output.*;
import org.monarchinitiative.lirical.core.output.AnalysisResultsMetadata;
import org.monarchinitiative.lirical.core.output.LrThreshold;
import org.monarchinitiative.lirical.core.output.MinDiagnosisCount;
import org.monarchinitiative.lirical.core.output.OutputOptions;
import org.monarchinitiative.lirical.io.output.svg.Lr2Svg;
import org.monarchinitiative.phenol.annotations.formats.GeneIdentifier;
import org.monarchinitiative.phenol.annotations.formats.hpo.HpoDisease;
Expand Down Expand Up @@ -70,7 +73,8 @@ public class HtmlTemplate extends LiricalTemplate {
cfg.setClassForTemplateLoading(HtmlTemplate.class, "");
templateData.put("postprobthreshold", String.format("%.1f%%", 100 * this.lrThreshold.getThreshold()));
int N = totalDetailedDiagnosesToShow(analysisResults);
List<SparklinePacket> sparklinePackets = SparklinePacket.sparklineFactory(analysisResults, diseases, hpo, N);
List<SparklinePacket> sparklinePackets = SparklinePacket.sparklineFactory(analysisResults, diseases, hpo,
outputOptions.showDiseasesWithNoDeleteriousVariants(), N);
this.templateData.put("sparkline", sparklinePackets);
if (symbolsWithoutGeneIds != null && !symbolsWithoutGeneIds.isEmpty()) {
this.templateData.put("geneSymbolsWithoutIds", symbolsWithoutGeneIds);
Expand All @@ -81,6 +85,7 @@ public class HtmlTemplate extends LiricalTemplate {
List<ImprobableDifferential> improbableDifferentials = new ArrayList<>();
AtomicInteger rank = new AtomicInteger();
analysisResults.resultsWithDescendingPostTestProbability().sequential()
.filter(handleCasesWithNoDeleteriousVariants(outputOptions.showDiseasesWithNoDeleteriousVariants()))
.forEachOrdered(result -> {
int current = rank.incrementAndGet();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import freemarker.template.Configuration;
import freemarker.template.Version;
import org.monarchinitiative.lirical.core.analysis.AnalysisData;
import org.monarchinitiative.lirical.core.analysis.TestResult;
import org.monarchinitiative.lirical.core.exception.LiricalRuntimeException;
import org.monarchinitiative.lirical.core.model.Gene2Genotype;
import org.monarchinitiative.lirical.core.model.LiricalVariant;
Expand All @@ -17,6 +18,7 @@
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -126,4 +128,21 @@ private boolean isPassingPathogenicThreshold(LiricalVariant lv) {
return lv.pathogenicityScore().orElse(0f) >= pathogenicityThreshold;
}

/**
* Include the result from the differential diagnoses
* IF we are interested the diseases with no deleterious variants
* OR if the genotype LR represents a situation where some pathogenic variants were found in the associated gene
* OR if genotype LR is missing (phenotype only mode).
*
* @param showDiseasesWithNoDeleteriousVariants set to {@code true} if you wish to see the differential diagnoses
* with no deleterious variants regardless of anything.
*/
static Predicate<TestResult> handleCasesWithNoDeleteriousVariants(boolean showDiseasesWithNoDeleteriousVariants) {
return r ->
showDiseasesWithNoDeleteriousVariants
|| r.genotypeLr()
.map(g -> g.matchType().hasDeleteriousVariants())
.orElse(true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class SparklinePacket {
public static List<SparklinePacket> sparklineFactory(AnalysisResults results,
HpoDiseases diseases,
MinimalOntology ontology,
boolean showDiseasesWithNoDeleteriousVariants,
int N) {
if (results.isEmpty())
return List.of();
Expand All @@ -53,6 +54,7 @@ public static List<SparklinePacket> sparklineFactory(AnalysisResults results,
Map<TermId, HpoDisease> diseaseById = diseases.diseaseById();
Sparkline2Svg sparkline2Svg = new Sparkline2Svg(topResult, true, ontology);
results.resultsWithDescendingPostTestProbability()
.filter(LiricalTemplate.handleCasesWithNoDeleteriousVariants(showDiseasesWithNoDeleteriousVariants))
.limit(N)
.forEachOrdered(result -> {
double posttestProb = result.posttestProbability();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TsvTemplate extends LiricalTemplate {
Map<TermId, HpoDisease> diseaseById = diseases.diseaseById();
List<TsvDifferential> diff = new ArrayList<>();
analysisResults.resultsWithDescendingPostTestProbability().sequential()
.filter(handleCasesWithNoDeleteriousVariants(outputOptions.showDiseasesWithNoDeleteriousVariants()))
.forEachOrdered(result -> {
int current = rank.incrementAndGet();
List<VisualizableVariant> variants = result.genotypeLr()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void writeExampleAnalysisOutput() throws Exception {
AnalysisResults results = createTestAnalysisResults();
AnalysisResultsMetadata metadata = createTestMetadata();
Path current = Path.of(".");
OutputOptions oo = new OutputOptions(LrThreshold.notInitialized(), MinDiagnosisCount.setToUserDefinedMinCount(2), 1.f, true, current, "test");
OutputOptions oo = new OutputOptions(LrThreshold.notInitialized(), MinDiagnosisCount.setToUserDefinedMinCount(2), 1.f, true, false, current, "test");
writer.process(analysisData, results, metadata, oo);

String output = readFileIntoString(TEST_OUTPUT);
Expand Down

0 comments on commit 4809f03

Please sign in to comment.