lfq_group_audit/
lfq_group_audit.rs1use koth_ms::{
4 alignment::{align_runs, RunInput},
5 config::AlignConfig,
6 input::{discover_runs, read_features},
7 lfq::consensus::build_consensus_candidates,
8};
9use std::{io::Write, path::PathBuf};
10
11fn main() -> anyhow::Result<()> {
12 env_logger::init();
13 let args: Vec<_> = std::env::args().skip(1).map(PathBuf::from).collect();
14 anyhow::ensure!(args.len() == 3, "usage: lfq_group_audit BATCH CONFIG OUT");
15 let cfg = AlignConfig::from_toml(&args[1])?;
16 let paths = discover_runs(&args[0])?;
17 anyhow::ensure!(!paths.is_empty(), "no feature runs found");
18 let runs: Vec<_> = paths
19 .iter()
20 .map(|r| {
21 Ok(RunInput {
22 name: r.name.clone(),
23 features: read_features(&r.features_path)?,
24 hills: Vec::new(),
25 scan_times: Vec::new(),
26 rt_bounds: None,
27 })
28 })
29 .collect::<anyhow::Result<_>>()?;
30 let alignment = align_runs(&runs, &cfg.alignment);
31 let groups = build_consensus_candidates(&runs, &alignment, &cfg.lfq);
32 std::fs::create_dir_all(&args[2])?;
33 let mut out = std::io::BufWriter::new(std::fs::File::create(args[2].join("candidates.tsv"))?);
34 writeln!(out, "candidate_id\tmassCalib\tmz\tcharge\trtApex\tim\tcombined_score\tn_contributing_runs\tgroup_score\tgroup_qvalue\tweak_members\tretained")?;
35 for (i, g) in groups.iter().enumerate() {
36 let weak = g
37 .members
38 .iter()
39 .filter(|&&(r, f)| runs[r].features[f as usize].combined_score < 0.5)
40 .count();
41 writeln!(
42 out,
43 "{i}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{weak}\t{}",
44 g.neutral_mass,
45 g.ref_mz,
46 g.charge,
47 g.ref_rt,
48 g.ref_im,
49 g.seed_combined_score,
50 g.n_contributing_runs,
51 g.group_score,
52 g.group_qvalue,
53 g.n_contributing_runs >= 2 && g.group_qvalue <= cfg.lfq.consensus.max_group_qvalue
54 )?;
55 }
56 out.flush()?;
57 Ok(())
58}