feat: support negative count filters as group size offsets
Updates CLI parsing to accept negative integers for count filters, interpreting them as offsets from the group size (e.g., `-1` means all but one). A resolution closure enforces a floor of 1 to prevent unconstrained filtering on small groups. Additionally, refines evolutionary distance documentation to condition comparisons on local homology, replacing union-based Jaccard with a self-contained `SnpTally`. This unified approach streamlines SNP and shared count computation, incorporates paralogy and heterozygosity handling, and enables direct derivation of corrected distance matrices without external dependencies.
This commit is contained in:
@@ -151,12 +151,14 @@ pub struct FilterArgs {
|
||||
pub outgroup: Vec<String>,
|
||||
|
||||
/// Minimum number of ingroup genomes containing the k-mer
|
||||
#[arg(long)]
|
||||
pub min_count: Option<usize>,
|
||||
/// (negative: offset from group size, e.g. -1 = all but one)
|
||||
#[arg(long, allow_hyphen_values = true)]
|
||||
pub min_count: Option<isize>,
|
||||
|
||||
/// Maximum number of ingroup genomes containing the k-mer
|
||||
#[arg(long)]
|
||||
pub max_count: Option<usize>,
|
||||
/// (negative: offset from group size, e.g. -1 = all but one)
|
||||
#[arg(long, allow_hyphen_values = true)]
|
||||
pub max_count: Option<isize>,
|
||||
|
||||
/// Minimum fraction of ingroup genomes containing the k-mer [0.0–1.0]
|
||||
/// (default 1.0 when --ingroup is set, 0.0 otherwise)
|
||||
@@ -168,13 +170,15 @@ pub struct FilterArgs {
|
||||
pub max_frac: Option<f64>,
|
||||
|
||||
/// Minimum number of outgroup genomes containing the k-mer
|
||||
#[arg(long)]
|
||||
pub min_outgroup_count: Option<usize>,
|
||||
/// (negative: offset from outgroup size, e.g. -1 = all but one)
|
||||
#[arg(long, allow_hyphen_values = true)]
|
||||
pub min_outgroup_count: Option<isize>,
|
||||
|
||||
/// Maximum number of outgroup genomes containing the k-mer
|
||||
/// (default 0 when --outgroup is set, no constraint otherwise)
|
||||
#[arg(long)]
|
||||
pub max_outgroup_count: Option<usize>,
|
||||
/// (default 0 when --outgroup is set, no constraint otherwise;
|
||||
/// negative: offset from outgroup size, e.g. -1 = all but one)
|
||||
#[arg(long, allow_hyphen_values = true)]
|
||||
pub max_outgroup_count: Option<isize>,
|
||||
|
||||
/// Minimum fraction of outgroup genomes containing the k-mer [0.0–1.0]
|
||||
#[arg(long)]
|
||||
@@ -239,12 +243,12 @@ pub fn matching_genome_indices(pred_str: &str, genomes: &[GenomeInfo]) -> Result
|
||||
|
||||
pub struct GroupFilterParams {
|
||||
pub threshold: u32,
|
||||
pub min_count: Option<usize>,
|
||||
pub max_count: Option<usize>,
|
||||
pub min_count: Option<isize>,
|
||||
pub max_count: Option<isize>,
|
||||
pub min_frac: Option<f64>,
|
||||
pub max_frac: Option<f64>,
|
||||
pub min_outgroup_count: Option<usize>,
|
||||
pub max_outgroup_count: Option<usize>,
|
||||
pub min_outgroup_count: Option<isize>,
|
||||
pub max_outgroup_count: Option<isize>,
|
||||
pub min_outgroup_frac: Option<f64>,
|
||||
pub max_outgroup_frac: Option<f64>,
|
||||
}
|
||||
@@ -279,12 +283,20 @@ pub fn build_group_filter(
|
||||
let default_min_frac = if !ingroup_preds.is_empty() && !ingroup_quorum_explicit { 1.0 } else { 0.0 };
|
||||
let default_max_outgroup_count = if !outgroup_preds.is_empty() && !outgroup_quorum_explicit { 0 } else { out_size };
|
||||
|
||||
let min_count = p.min_count.unwrap_or(0);
|
||||
let max_count = p.max_count.unwrap_or(in_size);
|
||||
// Resolve a signed count: negative means an offset from the group size
|
||||
// (e.g. -1 = all but one), floored at 1 so the negative form always keeps
|
||||
// constraining the group — even a singleton group, where n-1 would be 0
|
||||
// and would otherwise drop the constraint entirely.
|
||||
let resolve = |v: isize, size: usize| -> usize {
|
||||
if v < 0 { (size as isize + v).max(1) as usize } else { v as usize }
|
||||
};
|
||||
|
||||
let min_count = p.min_count.map(|v| resolve(v, in_size)).unwrap_or(0);
|
||||
let max_count = p.max_count.map(|v| resolve(v, in_size)).unwrap_or(in_size);
|
||||
let min_frac = p.min_frac.unwrap_or(default_min_frac);
|
||||
let max_frac = p.max_frac.unwrap_or(1.0);
|
||||
let min_outgroup_count = p.min_outgroup_count.unwrap_or(0);
|
||||
let max_outgroup_count = p.max_outgroup_count.unwrap_or(default_max_outgroup_count);
|
||||
let min_outgroup_count = p.min_outgroup_count.map(|v| resolve(v, out_size)).unwrap_or(0);
|
||||
let max_outgroup_count = p.max_outgroup_count.map(|v| resolve(v, out_size)).unwrap_or(default_max_outgroup_count);
|
||||
let min_outgroup_frac = p.min_outgroup_frac.unwrap_or(0.0);
|
||||
let max_outgroup_frac = p.max_outgroup_frac.unwrap_or(1.0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user