modewise_dataset/
events_tsv.rs1use std::path::Path;
5
6use modewise_epochs::{Event, Events};
7
8pub fn read(path: &Path, label_column: &str) -> Result<Events, String> {
9 let text = std::fs::read_to_string(path)
10 .map_err(|e| format!("events file {}: {e}", path.display()))?;
11 let mut lines = text.lines().filter(|l| !l.trim().is_empty());
12 let header: Vec<&str> = lines
13 .next()
14 .ok_or_else(|| format!("{} is empty", path.display()))?
15 .split('\t')
16 .map(str::trim)
17 .collect();
18 let column = |name: &str| {
19 header.iter().position(|h| *h == name).ok_or_else(|| {
20 format!(
21 "{} has no {name:?} column (columns: {})",
22 path.display(),
23 header.join(", ")
24 )
25 })
26 };
27 let (onset, duration, label) = (column("onset")?, column("duration")?, column(label_column)?);
28
29 let mut events = Vec::new();
30 for (row, line) in lines.enumerate() {
31 let cells: Vec<&str> = line.split('\t').map(str::trim).collect();
32 let cell = |i: usize, name: &str| {
33 cells
34 .get(i)
35 .copied()
36 .ok_or_else(|| format!("{} row {}: missing {name}", path.display(), row + 1))
37 };
38 let number = |i: usize, name: &str| -> Result<f64, String> {
39 let value = cell(i, name)?;
40 if value == "n/a" {
41 return Err(format!(
42 "{} row {}: {name} is n/a; such rows cannot be epoched",
43 path.display(),
44 row + 1
45 ));
46 }
47 value.parse().map_err(|_| {
48 format!(
49 "{} row {}: {name} {value:?} is not a number",
50 path.display(),
51 row + 1
52 )
53 })
54 };
55 events.push(Event {
56 onset_s: number(onset, "onset")?,
57 duration_s: number(duration, "duration")?,
58 label: cell(label, label_column)?.to_string(),
59 });
60 }
61 Events::new(events).map_err(|e| format!("{}: {e}", path.display()))
62}