Push zunrplorkwkt #70
Generated
+1
@@ -1667,6 +1667,7 @@ dependencies = [
|
|||||||
"csv",
|
"csv",
|
||||||
"obifastwrite",
|
"obifastwrite",
|
||||||
"obikalgorithm",
|
"obikalgorithm",
|
||||||
|
"obikdump",
|
||||||
"obikfilter",
|
"obikfilter",
|
||||||
"obikindex",
|
"obikindex",
|
||||||
"obikindexer",
|
"obikindexer",
|
||||||
|
|||||||
@@ -6,3 +6,5 @@
|
|||||||
//! reverse), same pattern as `obikindexer`/`obikquery`.
|
//! reverse), same pattern as `obikindexer`/`obikquery`.
|
||||||
|
|
||||||
mod dump;
|
mod dump;
|
||||||
|
|
||||||
|
pub use dump::IndexDump;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ obikalgorithm = { path = "../obikalgorithm" }
|
|||||||
obikmerge = { path = "../obikmerge" }
|
obikmerge = { path = "../obikmerge" }
|
||||||
obikfilter = { path = "../obikfilter" }
|
obikfilter = { path = "../obikfilter" }
|
||||||
obikselect = { path = "../obikselect" }
|
obikselect = { path = "../obikselect" }
|
||||||
|
obikdump = { path = "../obikdump" }
|
||||||
obifastwrite = { path = "../obifastwrite" }
|
obifastwrite = { path = "../obifastwrite" }
|
||||||
obiskbuilder = { path = "../obiskbuilder" }
|
obiskbuilder = { path = "../obiskbuilder" }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use std::io::{self, BufWriter};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
use obikdump::IndexDump;
|
||||||
|
use obikfilter::KmerFilter;
|
||||||
|
use obikindex::KmerIndex;
|
||||||
|
use obisys::progress_bar;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use super::predicate::GroupFilterArgs;
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct DumpArgs {
|
||||||
|
/// Index directory to dump
|
||||||
|
pub index: PathBuf,
|
||||||
|
|
||||||
|
/// Output presence/absence (0/1) even if the index stores counts
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub force_presence: bool,
|
||||||
|
|
||||||
|
/// Prepend partition and layer columns to each row
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
|
/// Only output the first N kmers
|
||||||
|
#[arg(long)]
|
||||||
|
pub head: Option<usize>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub group_filter: GroupFilterArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(args: DumpArgs) {
|
||||||
|
let idx = KmerIndex::open(&args.index).unwrap_or_else(|e| {
|
||||||
|
eprintln!("error opening index: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let n_genomes = idx.meta().genomes().unwrap_or_else(|e| {
|
||||||
|
eprintln!("error reading index metadata: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}).len();
|
||||||
|
info!(
|
||||||
|
"dumping {} partition(s), {} genome(s)",
|
||||||
|
idx.n_partitions(),
|
||||||
|
n_genomes
|
||||||
|
);
|
||||||
|
|
||||||
|
let filters: Vec<Box<dyn KmerFilter>> = vec![Box::new(args.group_filter.build_filter(&idx.meta()))];
|
||||||
|
let pb = progress_bar("dump", idx.n_partitions() as u64, "partitions");
|
||||||
|
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let mut out = BufWriter::new(stdout.lock());
|
||||||
|
|
||||||
|
idx.dump(&mut out, args.force_presence, args.debug, args.head, &filters, || pb.inc(1))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
eprintln!("dump error: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
pb.finish_and_clear();
|
||||||
|
}
|
||||||
@@ -2,14 +2,13 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use obikalgorithm::Algorithm;
|
use obikalgorithm::Algorithm;
|
||||||
use obikfilter::{
|
use obikfilter::{Filter, KmerFilter, MaxTotalCount, MinComplexity, MinTotalCount};
|
||||||
Filter, GenomeSelector, GroupFilterParams, KmerFilter, MaxTotalCount, MinComplexity,
|
|
||||||
MinTotalCount,
|
|
||||||
};
|
|
||||||
use obikindex::KmerIndex;
|
use obikindex::KmerIndex;
|
||||||
use obisys::{Progress, Reporter, Stage, progress_bar};
|
use obisys::{Progress, Reporter, Stage, progress_bar};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
use super::predicate::GroupFilterArgs;
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
pub struct FilterArgs {
|
pub struct FilterArgs {
|
||||||
/// Source index directory
|
/// Source index directory
|
||||||
@@ -19,55 +18,8 @@ pub struct FilterArgs {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub output: PathBuf,
|
pub output: PathBuf,
|
||||||
|
|
||||||
/// Ingroup predicate (repeatable; AND). Forms: `key=v1|v2`, `key!=v`, `key~path`, `key!~path`, `*`/`all`
|
#[command(flatten)]
|
||||||
#[arg(long, value_name = "PRED")]
|
pub group_filter: GroupFilterArgs,
|
||||||
pub ingroup: Vec<String>,
|
|
||||||
|
|
||||||
/// Outgroup predicate (repeatable; OR). Forms: `key=v1|v2`, `key!=v`, `key~path`, `key!~path`, `*`/`all`
|
|
||||||
#[arg(long, value_name = "PRED")]
|
|
||||||
pub outgroup: Vec<String>,
|
|
||||||
|
|
||||||
/// Minimum number of ingroup genomes containing the k-mer
|
|
||||||
/// (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
|
|
||||||
/// (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)
|
|
||||||
#[arg(long)]
|
|
||||||
pub min_frac: Option<f64>,
|
|
||||||
|
|
||||||
/// Maximum fraction of ingroup genomes containing the k-mer [0.0-1.0]
|
|
||||||
#[arg(long)]
|
|
||||||
pub max_frac: Option<f64>,
|
|
||||||
|
|
||||||
/// Minimum number of outgroup genomes containing the k-mer
|
|
||||||
/// (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;
|
|
||||||
/// 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)]
|
|
||||||
pub min_outgroup_frac: Option<f64>,
|
|
||||||
|
|
||||||
/// Maximum fraction of outgroup genomes containing the k-mer [0.0-1.0]
|
|
||||||
#[arg(long)]
|
|
||||||
pub max_outgroup_frac: Option<f64>,
|
|
||||||
|
|
||||||
/// Per-genome count threshold to consider a genome as "containing" the k-mer (default 0)
|
|
||||||
#[arg(long, default_value = "0")]
|
|
||||||
pub presence_threshold: u32,
|
|
||||||
|
|
||||||
/// Minimum total count across all genomes (count index only)
|
/// Minimum total count across all genomes (count index only)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
@@ -104,31 +56,8 @@ pub fn run(args: FilterArgs) {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
let selector = GenomeSelector::parse(&args.ingroup, &args.outgroup).unwrap_or_else(|e| {
|
let mut filters: Vec<Box<dyn KmerFilter>> =
|
||||||
eprintln!("error in --ingroup/--outgroup: {e}");
|
vec![Box::new(args.group_filter.build_filter(&src.meta()))];
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
let group_filter = selector
|
|
||||||
.build_group_filter(
|
|
||||||
&src.meta(),
|
|
||||||
GroupFilterParams {
|
|
||||||
threshold: args.presence_threshold,
|
|
||||||
min_count: args.min_count,
|
|
||||||
max_count: args.max_count,
|
|
||||||
min_frac: args.min_frac,
|
|
||||||
max_frac: args.max_frac,
|
|
||||||
min_outgroup_count: args.min_outgroup_count,
|
|
||||||
max_outgroup_count: args.max_outgroup_count,
|
|
||||||
min_outgroup_frac: args.min_outgroup_frac,
|
|
||||||
max_outgroup_frac: args.max_outgroup_frac,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("error in filter parameters: {e}");
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut filters: Vec<Box<dyn KmerFilter>> = vec![Box::new(group_filter)];
|
|
||||||
if let Some(v) = args.min_total_count {
|
if let Some(v) = args.min_total_count {
|
||||||
filters.push(Box::new(MinTotalCount { total: v }));
|
filters.push(Box::new(MinTotalCount { total: v }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
pub mod annotate;
|
pub mod annotate;
|
||||||
|
pub mod dump;
|
||||||
pub mod estimate;
|
pub mod estimate;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
pub mod merge;
|
pub mod merge;
|
||||||
|
mod predicate;
|
||||||
pub mod select;
|
pub mod select;
|
||||||
pub mod superkmer;
|
pub mod superkmer;
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
use clap::Args;
|
||||||
|
use obikfilter::{GenomeSelector, GroupFilterParams, GroupQuorumFilter};
|
||||||
|
use obikindex::IndexMeta;
|
||||||
|
|
||||||
|
/// Ingroup/outgroup metadata-predicate quorum filtering — embeddable in any
|
||||||
|
/// command via `#[command(flatten)]` (`filter`, `dump`).
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct GroupFilterArgs {
|
||||||
|
/// Ingroup predicate (repeatable; AND). Forms: `key=v1|v2`, `key!=v`, `key~path`, `key!~path`, `*`/`all`
|
||||||
|
#[arg(long, value_name = "PRED")]
|
||||||
|
pub ingroup: Vec<String>,
|
||||||
|
|
||||||
|
/// Outgroup predicate (repeatable; OR). Forms: `key=v1|v2`, `key!=v`, `key~path`, `key!~path`, `*`/`all`
|
||||||
|
#[arg(long, value_name = "PRED")]
|
||||||
|
pub outgroup: Vec<String>,
|
||||||
|
|
||||||
|
/// Minimum number of ingroup genomes containing the k-mer
|
||||||
|
/// (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
|
||||||
|
/// (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)
|
||||||
|
#[arg(long)]
|
||||||
|
pub min_frac: Option<f64>,
|
||||||
|
|
||||||
|
/// Maximum fraction of ingroup genomes containing the k-mer [0.0-1.0]
|
||||||
|
#[arg(long)]
|
||||||
|
pub max_frac: Option<f64>,
|
||||||
|
|
||||||
|
/// Minimum number of outgroup genomes containing the k-mer
|
||||||
|
/// (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;
|
||||||
|
/// 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)]
|
||||||
|
pub min_outgroup_frac: Option<f64>,
|
||||||
|
|
||||||
|
/// Maximum fraction of outgroup genomes containing the k-mer [0.0-1.0]
|
||||||
|
#[arg(long)]
|
||||||
|
pub max_outgroup_frac: Option<f64>,
|
||||||
|
|
||||||
|
/// Per-genome count threshold to consider a genome as "containing" the k-mer (default 0)
|
||||||
|
#[arg(long, default_value = "0")]
|
||||||
|
pub presence_threshold: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GroupFilterArgs {
|
||||||
|
/// Parse `--ingroup`/`--outgroup` and build the quorum filter. Exits on error.
|
||||||
|
pub fn build_filter(&self, meta: &IndexMeta) -> GroupQuorumFilter {
|
||||||
|
let selector = GenomeSelector::parse(&self.ingroup, &self.outgroup).unwrap_or_else(|e| {
|
||||||
|
eprintln!("error in --ingroup/--outgroup: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
selector
|
||||||
|
.build_group_filter(
|
||||||
|
meta,
|
||||||
|
GroupFilterParams {
|
||||||
|
threshold: self.presence_threshold,
|
||||||
|
min_count: self.min_count,
|
||||||
|
max_count: self.max_count,
|
||||||
|
min_frac: self.min_frac,
|
||||||
|
max_frac: self.max_frac,
|
||||||
|
min_outgroup_count: self.min_outgroup_count,
|
||||||
|
max_outgroup_count: self.max_outgroup_count,
|
||||||
|
min_outgroup_frac: self.min_outgroup_frac,
|
||||||
|
max_outgroup_frac: self.max_outgroup_frac,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
eprintln!("error in filter parameters: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ enum Commands {
|
|||||||
Filter(cmd::filter::FilterArgs),
|
Filter(cmd::filter::FilterArgs),
|
||||||
/// Project/aggregate genome columns into a new index
|
/// Project/aggregate genome columns into a new index
|
||||||
Select(cmd::select::SelectArgs),
|
Select(cmd::select::SelectArgs),
|
||||||
|
/// Dump an index's kmers as a CSV table
|
||||||
|
Dump(cmd::dump::DumpArgs),
|
||||||
/// Estimate approximate-evidence false-positive rates for given parameters
|
/// Estimate approximate-evidence false-positive rates for given parameters
|
||||||
Estimate(cmd::estimate::EstimateArgs),
|
Estimate(cmd::estimate::EstimateArgs),
|
||||||
/// Read/write genome metadata (CSV) on an already-built index
|
/// Read/write genome metadata (CSV) on an already-built index
|
||||||
@@ -44,6 +46,7 @@ fn main() {
|
|||||||
Commands::Merge(args) => cmd::merge::run(args),
|
Commands::Merge(args) => cmd::merge::run(args),
|
||||||
Commands::Filter(args) => cmd::filter::run(args),
|
Commands::Filter(args) => cmd::filter::run(args),
|
||||||
Commands::Select(args) => cmd::select::run(args),
|
Commands::Select(args) => cmd::select::run(args),
|
||||||
|
Commands::Dump(args) => cmd::dump::run(args),
|
||||||
Commands::Estimate(args) => cmd::estimate::run(args),
|
Commands::Estimate(args) => cmd::estimate::run(args),
|
||||||
Commands::Annotate(args) => cmd::annotate::run(args),
|
Commands::Annotate(args) => cmd::annotate::run(args),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user