use std::{fmt::{Display, Formatter}, fs::File, io::{BufWriter, Write}};
use anyhow::Result;
use crate::{aggregation::{AggregationResult, GeneAggregation}, utils::config::Configuration};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Method {
AlphaRRA,
IncProd,
IncPvalue,
}
impl Display for Method {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Method::AlphaRRA => write!(f, "rra"),
Method::IncProd => write!(f, "inc-product"),
Method::IncPvalue => write!(f, "inc-pvalue"),
}
}
}
pub struct Screenviz {
method: Method,
gene: String,
x: String,
y: String,
z: String,
threshold: Option<f64>,
threshold_low: Option<f64>,
threshold_high: Option<f64>,
ntc_token: Option<String>,
}
impl Screenviz {
pub fn new(results: &AggregationResult, config: &Configuration) -> Self {
let method = match config.aggregation() {
GeneAggregation::AlpaRRA { alpha: _, npermutations: _, adjust_alpha: _, fdr: _ } => {
Method::AlphaRRA
},
GeneAggregation::Inc { token: _, fdr: _, group_size: _, use_product } => {
if *use_product {
Method::IncProd
} else {
Method::IncPvalue
}
}
};
let gene = "gene".to_string();
let x = "log_fold_change".to_string();
let y = "pvalue".to_string();
let z = match config.aggregation() {
GeneAggregation::AlpaRRA { alpha: _, npermutations: _, adjust_alpha: _, fdr: _ } => {
"fdr".to_string()
},
GeneAggregation::Inc { token: _, fdr: _, group_size: _, use_product } => {
if *use_product {
"phenotype_score".to_string()
} else {
"pvalue".to_string()
}
}
};
let (threshold, threshold_low, threshold_high) = match config.aggregation() {
GeneAggregation::AlpaRRA { alpha: _, npermutations: _, adjust_alpha: _, fdr } => {
(Some(*fdr), None, None)
},
GeneAggregation::Inc { token: _, fdr: _, group_size: _, use_product: _ } => {
(None, results.threshold_low(), results.threshold_high())
}
};
let ntc_token = if let Some(_) = threshold_low {
Some("pseudogene".to_string())
} else {
None
};
Self {
method,
gene,
x,
y,
z,
threshold,
threshold_low,
threshold_high,
ntc_token,
}
}
pub fn write(&self, prefix: &str) -> Result<()> {
let mut writer = File::create(format!("{prefix}.screenviz.yaml")).map(BufWriter::new)?;
match self.method {
Method::AlphaRRA => {
writeln!(writer, "method: {}", self.method)?;
writeln!(writer, "gene: {}", self.gene)?;
writeln!(writer, "x: {}", self.x)?;
writeln!(writer, "y: {}", self.y)?;
writeln!(writer, "z: {}", self.z)?;
writeln!(writer, "threshold: {}", self.threshold.unwrap())?;
},
_ => {
writeln!(writer, "method: {}", self.method)?;
writeln!(writer, "gene: {}", self.gene)?;
writeln!(writer, "x: {}", self.x)?;
writeln!(writer, "y: {}", self.y)?;
writeln!(writer, "z: {}", self.z)?;
writeln!(writer, "threshold_low: {}", self.threshold_low.unwrap())?;
writeln!(writer, "threshold_high: {}", self.threshold_high.unwrap())?;
writeln!(writer, "ntc_token: {}", self.ntc_token.as_ref().unwrap())?;
},
}
Ok(())
}
}
#[cfg(test)]
mod testing {
use adjustp::Procedure;
use ndarray::Array1;
use crate::{norm::Normalization, model::ModelChoice};
use super::*;
fn build_config_rra<'a>() -> Configuration<'a> {
let normalization = Normalization::MedianRatio;
let aggregation = GeneAggregation::AlpaRRA {
alpha: 0.05,
npermutations: 1000,
adjust_alpha: true,
fdr: 0.05,
};
let correction = Procedure::BenjaminiHochberg;
let model_choice = ModelChoice::Wols;
let seed = 0;
let prefix = "results";
Configuration::new(
normalization,
aggregation,
correction,
model_choice,
seed,
prefix,
)
}
fn build_config_prod<'a>() -> Configuration<'a> {
let normalization = Normalization::MedianRatio;
let aggregation = GeneAggregation::Inc {
token: "non-targeting",
fdr: 0.05,
group_size: 5,
use_product: true,
};
let correction = Procedure::BenjaminiHochberg;
let model_choice = ModelChoice::Wols;
let seed = 0;
let prefix = "results";
Configuration::new(
normalization,
aggregation,
correction,
model_choice,
seed,
prefix,
)
}
fn build_config_pvalue<'a>() -> Configuration<'a> {
let normalization = Normalization::MedianRatio;
let aggregation = GeneAggregation::Inc {
token: "non-targeting",
fdr: 0.05,
group_size: 5,
use_product: false,
};
let correction = Procedure::BenjaminiHochberg;
let model_choice = ModelChoice::Wols;
let seed = 0;
let prefix = "results";
Configuration::new(
normalization,
aggregation,
correction,
model_choice,
seed,
prefix,
)
}
fn build_results_rra() -> AggregationResult {
let genes = vec!["gene1".to_string(), "gene2".to_string()];
let gene_fc = Array1::from(vec![1.0, 2.0]);
let pvalues_low = Array1::from(vec![0.1, 0.2]);
let pvalues_high = Array1::from(vec![0.3, 0.4]);
let fdr_low = Array1::from(vec![0.5, 0.6]);
let fdr_high = Array1::from(vec![0.7, 0.2]);
let aggregation_score_low = Array1::from(vec![0.5, 0.6]);
let aggregation_score_high = Array1::from(vec![0.7, 0.8]);
let threshold_low = None;
let threshold_high = None;
AggregationResult::new(
genes,
gene_fc,
pvalues_low,
pvalues_high,
fdr_low,
fdr_high,
aggregation_score_low,
aggregation_score_high,
threshold_low,
threshold_high,
)
}
fn build_results_incprod() -> AggregationResult {
let genes = vec!["gene1".to_string(), "gene2".to_string()];
let gene_fc = Array1::from(vec![1.0, 2.0]);
let pvalues_low = Array1::from(vec![0.1, 0.2]);
let pvalues_high = Array1::from(vec![0.3, 0.4]);
let fdr_low = Array1::from(vec![0.5, 0.6]);
let fdr_high = Array1::from(vec![0.7, 0.2]);
let aggregation_score_low = Array1::from(vec![0.5, 0.6]);
let aggregation_score_high = Array1::from(vec![0.7, 0.8]);
let threshold_low = Some(-0.5);
let threshold_high = Some(0.5);
AggregationResult::new(
genes,
gene_fc,
pvalues_low,
pvalues_high,
fdr_low,
fdr_high,
aggregation_score_low,
aggregation_score_high,
threshold_low,
threshold_high,
)
}
fn build_results_incpvalue() -> AggregationResult {
let genes = vec!["gene1".to_string(), "gene2".to_string()];
let gene_fc = Array1::from(vec![1.0, 2.0]);
let pvalues_low = Array1::from(vec![0.1, 0.2]);
let pvalues_high = Array1::from(vec![0.3, 0.4]);
let fdr_low = Array1::from(vec![0.5, 0.6]);
let fdr_high = Array1::from(vec![0.7, 0.2]);
let aggregation_score_low = Array1::from(vec![0.5, 0.6]);
let aggregation_score_high = Array1::from(vec![0.7, 0.8]);
let threshold_low = Some(0.005);
let threshold_high = Some(0.004);
AggregationResult::new(
genes,
gene_fc,
pvalues_low,
pvalues_high,
fdr_low,
fdr_high,
aggregation_score_low,
aggregation_score_high,
threshold_low,
threshold_high,
)
}
#[test]
fn test_screenviz_incprod() {
let config = build_config_prod();
let results = build_results_incprod();
let screenviz = Screenviz::new(&results, &config);
assert_eq!(screenviz.method, Method::IncProd);
assert_eq!(screenviz.gene, "gene");
assert_eq!(screenviz.x, "log_fold_change");
assert_eq!(screenviz.y, "pvalue");
assert_eq!(screenviz.z, "phenotype_score");
assert_eq!(screenviz.threshold, None);
assert_eq!(screenviz.threshold_low, Some(-0.5));
assert_eq!(screenviz.threshold_high, Some(0.5));
assert_eq!(screenviz.ntc_token, Some("pseudogene".to_string()));
}
#[test]
fn test_screenviz_incpvalue() {
let config = build_config_pvalue();
let results = build_results_incpvalue();
let screenviz = Screenviz::new(&results, &config);
assert_eq!(screenviz.method, Method::IncPvalue);
assert_eq!(screenviz.gene, "gene");
assert_eq!(screenviz.x, "log_fold_change");
assert_eq!(screenviz.y, "pvalue");
assert_eq!(screenviz.z, "pvalue");
assert_eq!(screenviz.threshold, None);
assert_eq!(screenviz.threshold_low, Some(0.005));
assert_eq!(screenviz.threshold_high, Some(0.004));
assert_eq!(screenviz.ntc_token, Some("pseudogene".to_string()));
}
#[test]
fn test_screenviz_rra() {
let config = build_config_rra();
let results = build_results_rra();
let screenviz = Screenviz::new(&results, &config);
assert_eq!(screenviz.method, Method::AlphaRRA);
assert_eq!(screenviz.gene, "gene");
assert_eq!(screenviz.x, "log_fold_change");
assert_eq!(screenviz.y, "pvalue");
assert_eq!(screenviz.z, "fdr");
assert_eq!(screenviz.threshold, Some(0.05));
assert_eq!(screenviz.threshold_low, None);
assert_eq!(screenviz.threshold_high, None);
assert_eq!(screenviz.ntc_token, None);
}
}