use crate::prelude::*;
pub const MIN_SHARED_SAMPLES: usize = 5;
pub const MIN_ANOVA_FEATURES: usize = 5;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Averaging {
#[default]
Median,
Mean,
}
pub fn parse_averaging(s: &str) -> Option<Averaging> {
match s.to_lowercase().as_str() {
"median" => Some(Averaging::Median),
"mean" => Some(Averaging::Mean),
_ => None,
}
}
#[derive(Clone, Debug)]
pub struct PmdParams {
pub k: usize,
pub n_permutations: usize,
pub extra_sparse: bool,
pub abn_c: usize,
pub p_anova: f64,
pub centre: bool,
pub cap: f64,
pub spatial: bool,
pub n_genes: usize,
pub min_ci: f64,
pub averaging: Averaging,
pub mcp_assignment_p: f64,
pub seed: u64,
}
impl PmdParams {
#[allow(clippy::too_many_arguments)]
pub fn new(
k: usize,
n_permutations: usize,
extra_sparse: bool,
abn_c: usize,
p_anova: f64,
centre: bool,
cap: f64,
spatial: bool,
n_genes: usize,
min_ci: f64,
averaging: Averaging,
mcp_assignment_p: f64,
seed: u64,
) -> Self {
Self {
k,
n_permutations,
extra_sparse,
abn_c,
p_anova,
centre,
cap,
spatial,
n_genes,
min_ci,
averaging,
mcp_assignment_p,
seed,
}
}
}
impl Default for PmdParams {
fn default() -> Self {
Self {
k: 2,
n_permutations: 100,
extra_sparse: false,
abn_c: 15,
p_anova: 0.05,
centre: true,
cap: 0.01,
spatial: false,
n_genes: 200,
min_ci: 0.05,
averaging: Averaging::Median,
mcp_assignment_p: 0.1,
seed: 1234,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct HlmParams {
pub min_cells_per_sample: usize,
pub use_tme_qc: bool,
pub use_cell_quality: bool,
pub satterthwaite: bool,
}
impl HlmParams {
pub fn new(
min_cells_per_sample: usize,
use_tme_qc: bool,
use_cell_quality: bool,
satterthwaite: bool,
) -> Self {
Self {
min_cells_per_sample,
use_tme_qc,
use_cell_quality,
satterthwaite,
}
}
}
impl Default for HlmParams {
fn default() -> Self {
Self {
min_cells_per_sample: 2,
use_tme_qc: true,
use_cell_quality: true,
satterthwaite: true,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct RefineParams {
pub support_p: f64,
pub min_support_fraction: f64,
pub min_stratum: usize,
pub early_stop_cor: f64,
pub permissive_p: f64,
pub strict_p: f64,
}
impl RefineParams {
pub fn new(
support_p: f64,
min_support_fraction: f64,
min_stratum: usize,
early_stop_cor: f64,
permissive_p: f64,
strict_p: f64,
) -> Self {
Self {
support_p,
min_support_fraction,
min_stratum,
early_stop_cor,
permissive_p,
strict_p,
}
}
}
impl Default for RefineParams {
fn default() -> Self {
Self {
support_p: 0.1,
min_support_fraction: 1.0 / 3.0,
min_stratum: 5,
early_stop_cor: 0.95,
permissive_p: 1e-3,
strict_p: 0.05,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DialogueParams {
pub pmd: PmdParams,
pub hlm: HlmParams,
pub refine: RefineParams,
}
impl DialogueParams {
pub fn new(pmd: PmdParams, hlm: HlmParams, refine: RefineParams) -> Self {
Self { pmd, hlm, refine }
}
pub fn validate(&self, n_cell_types: usize) -> Result<(), ScErrors> {
if n_cell_types < 2 {
return Err(ScErrors::DialogueTooFewCellTypes { n_cell_types });
}
if self.pmd.k == 0 {
return Err(CoreErrors::InvalidArgument(
"DIALOGUE needs at least one programme.".to_string(),
)
.into());
}
if self.pmd.n_permutations < 2 {
return Err(CoreErrors::InvalidArgument(format!(
"the empirical p-value needs at least two runs; got {}.",
self.pmd.n_permutations
))
.into());
}
if !(0.0..0.5).contains(&self.pmd.cap) {
return Err(CoreErrors::InvalidArgument(format!(
"the winsorising fraction must lie in [0, 0.5); got {}.",
self.pmd.cap
))
.into());
}
Ok(())
}
}