use edge_rs::core::dgelist::DgeList;
use edge_rs::core::expression::ave_log_cpm;
use edge_rs::core::filtering::filter_by_expr;
use edge_rs::core::normalisation::{NormMethod, calc_norm_factors};
use edge_rs::dispersion::estimate::estimate_disp;
use edge_rs::glm::ql_fit::{QlFitParams, glm_ql_fit};
use edge_rs::glm::test::{GlmTestInput, Tested, glm_ql_ftest};
use edge_rs::numeric::stats::p_adjust_bh;
use edge_rs::prelude::Recycled;
use crate::prelude::*;
const AVE_LOG_CPM_PRIOR: f64 = 2.0;
#[derive(Clone, Copy, Debug)]
pub struct EdgeRQlParams {
pub norm_method: NormMethod,
pub filter: bool,
pub min_mean: f64,
pub robust: bool,
pub legacy: bool,
}
impl Default for EdgeRQlParams {
fn default() -> Self {
Self {
norm_method: NormMethod::Tmm,
filter: true,
min_mean: 0.0,
robust: false,
legacy: false,
}
}
}
#[derive(Clone, Debug)]
pub struct EdgeRDgeRes {
pub genes_to_keep: Vec<bool>,
pub log_fc: Vec<f64>,
pub log_cpm: Vec<f64>,
pub f_stat: Vec<f64>,
pub p_val: Vec<f64>,
pub fdr: Vec<f64>,
}
pub fn run_edger_ql(
counts: &[f64],
n_genes: usize,
n_samples: usize,
design: &[f64],
n_coef: usize,
tested: &Tested,
params: &EdgeRQlParams,
) -> Result<EdgeRDgeRes, BixverseErrors> {
let dge = DgeList::new(counts.to_vec(), n_genes, n_samples, None)?;
let mut keep = if params.filter {
filter_by_expr(
&dge.counts,
n_genes,
n_samples,
None,
None,
Some((design, n_coef)),
None,
)?
} else {
vec![true; n_genes]
};
if params.min_mean > 0.0 {
for (gene, flag) in keep.iter_mut().enumerate() {
let mean = counts[gene * n_samples..(gene + 1) * n_samples]
.iter()
.sum::<f64>()
/ n_samples as f64;
*flag &= mean >= params.min_mean;
}
}
if !keep.iter().any(|k| *k) {
return Err(edge_rs::errors::EdgeErrors::NoGenesAfterFiltering { n_genes }.into());
}
let mut dge = dge.subset_genes(&keep)?;
let n_kept = dge.n_genes;
let lib_size = dge.lib_size.clone();
dge.norm_factors = calc_norm_factors(
&dge.counts,
n_kept,
n_samples,
Some(&lib_size),
params.norm_method,
None,
None,
)?;
let offset = dge.offset()?;
let abundance = ave_log_cpm(
&dge.counts,
n_kept,
n_samples,
None,
Some(&offset),
AVE_LOG_CPM_PRIOR,
None,
)?;
let dispersion = if params.legacy {
let disp = estimate_disp(
&dge.counts,
n_kept,
n_samples,
design,
n_coef,
&offset,
None,
Some(&abundance),
None,
)?;
let per_gene = disp.trended.unwrap_or_else(|| vec![disp.common; n_kept]);
Some(Recycled::by_gene(per_gene))
} else {
None
};
let fit = glm_ql_fit(
&dge.counts,
n_kept,
n_samples,
design,
n_coef,
dispersion.as_ref(),
&offset,
None,
&abundance,
Some(QlFitParams {
robust: params.robust,
legacy: params.legacy,
..Default::default()
}),
)?;
let base = fit.as_glm_fit();
let ql = fit.ql_summary();
let input = GlmTestInput {
counts: &dge.counts,
n_genes: n_kept,
n_samples,
design,
n_coef,
dispersion: &fit.dispersion,
offset: &offset,
weights: None,
log_cpm: Some(&abundance),
};
let test = glm_ql_ftest(&input, &base, &ql, tested, true)?;
let fdr = p_adjust_bh(&test.p_value);
Ok(EdgeRDgeRes {
genes_to_keep: keep,
log_fc: test.log_fc,
log_cpm: test.log_cpm.unwrap_or(abundance),
f_stat: test.statistic,
p_val: test.p_value,
fdr,
})
}