Skip to main content

lfq_permutation_audit/support/
lfq_matrices.rs

1use koth_ms::lfq::IntensityMatrix;
2use std::path::Path;
3
4pub fn matrices(m: &IntensityMatrix, folder: &Path) -> anyhow::Result<()> {
5    let meta = [
6        "massCalib",
7        "mz",
8        "charge",
9        "rtApex",
10        "im",
11        "combined_score",
12        "seed_run",
13        "n_contributing_runs",
14    ];
15    for (kind, name) in [
16        "consensus_features.tsv",
17        "intensity_matrix.tsv",
18        "qvalue_matrix.tsv",
19        "decoy_intensity_matrix.tsv",
20    ]
21    .iter()
22    .enumerate()
23    {
24        let mut out = csv::WriterBuilder::new()
25            .delimiter(b'\t')
26            .from_path(folder.join(name))?;
27        let mut header: Vec<String> = meta.iter().map(|s| s.to_string()).collect();
28        if kind == 0 {
29            header.extend(["n_runs_detected", "group_score", "group_qvalue"].map(String::from));
30        } else {
31            header.extend(m.run_names.clone());
32        }
33        out.write_record(header)?;
34        for (i, g) in m.consensus.iter().enumerate() {
35            let mut row = vec![
36                format!("{:.6}", g.neutral_mass),
37                format!("{:.6}", g.ref_mz),
38                g.charge.to_string(),
39                format!("{:.6}", g.ref_rt),
40                if g.ref_im == 0.0 {
41                    String::new()
42                } else {
43                    format!("{:.6}", g.ref_im)
44                },
45                format!("{:.6}", g.seed_combined_score),
46                m.run_names[g.seed_run_idx].clone(),
47                g.n_contributing_runs.to_string(),
48            ];
49            if kind == 0 {
50                row.extend([
51                    (0..m.n_runs)
52                        .filter(|&r| m.intensity(i, r) > 0.0)
53                        .count()
54                        .to_string(),
55                    format!("{:.6}", g.group_score),
56                    format!("{:.6}", g.group_qvalue),
57                ]);
58            } else {
59                for r in 0..m.n_runs {
60                    let v = match kind {
61                        1 => m.intensity(i, r),
62                        2 => m.q_value(i, r),
63                        _ => m.decoy_intensity(i, r),
64                    };
65                    row.push(if kind == 2 {
66                        format!("{v:.4}")
67                    } else if v > 0.0 {
68                        format!("{v:.5e}")
69                    } else {
70                        "0".into()
71                    });
72                }
73            }
74            out.write_record(row)?;
75        }
76        out.flush()?;
77    }
78    let mut audit = csv::WriterBuilder::new()
79        .delimiter(b'\t')
80        .from_path(folder.join("ownership.tsv"))?;
81    audit.write_record([
82        "feature_idx",
83        "run_idx",
84        "is_decoy",
85        "intensity",
86        "ownership_status",
87        "owned_samples",
88        "excluded_samples",
89        "competing_feature",
90        "apex_rt",
91        "bhattacharyya",
92        "coelution",
93        "preceding_signal_fraction",
94    ])?;
95    for e in &m.entries {
96        audit.write_record([
97            e.feature_idx.to_string(),
98            e.run_idx.to_string(),
99            e.is_decoy.to_string(),
100            e.intensity.to_string(),
101            e.ownership_status.to_string(),
102            e.owned_samples.to_string(),
103            e.excluded_samples.to_string(),
104            e.competing_feature
105                .map(|i| i.to_string())
106                .unwrap_or_default(),
107            e.apex_rt.to_string(),
108            e.spectral_bhattacharyya.to_string(),
109            e.coelution.to_string(),
110            e.preceding_signal_fraction.to_string(),
111        ])?;
112    }
113    audit.flush()?;
114    Ok(())
115}