modewise_results/
tables.rs1use std::path::Path;
4
5use modewise_study::FeatureTable;
6
7use crate::ResultsError;
8
9pub fn write_feature_table(path: &Path, table: &FeatureTable) -> Result<(), ResultsError> {
12 let io = |e: std::io::Error| ResultsError::Io {
13 path: path.display().to_string(),
14 message: e.to_string(),
15 };
16 let metadata: Vec<&str> = table.metadata_columns().collect();
17 let mut out = String::new();
18 let header: Vec<&str> = ["recording", "subject", "condition"]
19 .into_iter()
20 .chain(metadata.iter().copied())
21 .chain(table.feature_names().iter().map(String::as_str))
22 .collect();
23 out.push_str(&header.join("\t"));
24 out.push('\n');
25 let text = |v: Option<&str>| v.unwrap_or("n/a").to_string();
26 for (row, o) in table.observations().iter().enumerate() {
27 let mut cells = vec![
28 o.recording.clone(),
29 text(o.subject.as_deref()),
30 text(o.condition.as_deref()),
31 ];
32 for column in &metadata {
33 let values = table.metadata(column).map_err(|e| ResultsError::Io {
34 path: path.display().to_string(),
35 message: e.to_string(),
36 })?;
37 cells.push(text(values[row].as_deref()));
38 }
39 cells.extend(table.values().row(row).iter().map(|v| {
40 if v.is_nan() {
41 "n/a".to_string()
42 } else {
43 v.to_string()
44 }
45 }));
46 out.push_str(&cells.join("\t"));
47 out.push('\n');
48 }
49 if let Some(parent) = path.parent() {
50 std::fs::create_dir_all(parent).map_err(io)?;
51 }
52 std::fs::write(path, out).map_err(io)
53}
54
55#[cfg(test)]
56mod tests {
57 use std::collections::BTreeMap;
58
59 use modewise_study::{Observation, Participants};
60
61 use super::*;
62
63 #[test]
64 fn writes_ids_metadata_and_features_with_missing_as_na() {
65 let rows = vec![
66 Observation {
67 recording: "subject-01".into(),
68 subject: Some("01".into()),
69 condition: Some("face".into()),
70 },
71 Observation {
72 recording: "subject-02".into(),
73 subject: None,
74 condition: None,
75 },
76 ];
77 let p = Participants::new(
78 vec!["group".into()],
79 BTreeMap::from([(
80 "01".to_string(),
81 BTreeMap::from([("group".to_string(), "Patient".to_string())]),
82 )]),
83 )
84 .unwrap();
85 let table = FeatureTable::new(
86 rows,
87 vec!["m0:a~b".into()],
88 ndarray::array![[0.25], [f64::NAN]],
89 )
90 .unwrap()
91 .with_participants(&p)
92 .unwrap();
93 let path =
94 std::env::temp_dir().join(format!("modewise-features-{}/t.tsv", std::process::id()));
95 write_feature_table(&path, &table).unwrap();
96 let text = std::fs::read_to_string(&path).unwrap();
97 assert_eq!(
98 text,
99 "recording\tsubject\tcondition\tgroup\tm0:a~b\nsubject-01\t01\tface\tPatient\t0.25\nsubject-02\tn/a\tn/a\tn/a\tn/a\n"
100 );
101 }
102}