burn_dataset/dataset/
in_memory.rs1use std::{
2 fs::File,
3 io::{BufRead, BufReader},
4 path::Path,
5};
6
7use serde::de::DeserializeOwned;
8
9use crate::{Dataset, DatasetError};
10
11pub struct InMemDataset<I> {
13 items: Vec<I>,
14}
15
16impl<I> InMemDataset<I> {
17 pub fn new(items: Vec<I>) -> Self {
19 InMemDataset { items }
20 }
21}
22
23impl<I> Dataset<I> for InMemDataset<I>
24where
25 I: Clone + Send + Sync,
26{
27 fn get(&self, index: usize) -> Result<I, DatasetError> {
28 match self.items.get(index) {
29 Some(item) => Ok(item.clone()),
30 None => panic!(
31 "Index out of bounds for InMemDataset: {index} >= {}",
32 self.items.len()
33 ),
34 }
35 }
36 fn len(&self) -> usize {
37 self.items.len()
38 }
39}
40
41impl<I> InMemDataset<I>
42where
43 I: Clone + DeserializeOwned,
44{
45 pub fn from_dataset<E>(dataset: &impl Dataset<I, E>) -> Self
47 where
48 E: std::error::Error + Send + Sync + 'static,
49 {
50 let items: Vec<I> = dataset.iter().map(Result::unwrap).collect();
51 Self::new(items)
52 }
53
54 pub fn from_json_rows<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
58 let file = File::open(path)?;
59 let reader = BufReader::new(file);
60 let mut items = Vec::new();
61
62 for line in reader.lines() {
63 let item = serde_json::from_str(line.unwrap().as_str()).unwrap();
64 items.push(item);
65 }
66
67 let dataset = Self::new(items);
68
69 Ok(dataset)
70 }
71
72 pub fn from_csv<P: AsRef<Path>>(
82 path: P,
83 builder: &csv::ReaderBuilder,
84 ) -> Result<Self, std::io::Error> {
85 let mut rdr = builder.from_path(path)?;
86
87 let mut items = Vec::new();
88
89 for result in rdr.deserialize() {
90 let item: I = result?;
91 items.push(item);
92 }
93
94 let dataset = Self::new(items);
95
96 Ok(dataset)
97 }
98}
99
100#[cfg(test)]
101mod tests {
102
103 use super::*;
104 #[cfg(feature = "sqlite")]
105 use crate::SqliteDataset;
106 use crate::test_data;
107
108 #[cfg(feature = "sqlite")]
109 use rstest::{fixture, rstest};
110 use serde::{Deserialize, Serialize};
111
112 #[cfg(feature = "sqlite")]
113 const DB_FILE: &str = "tests/data/sqlite-dataset.db";
114 const JSON_FILE: &str = "tests/data/dataset.json";
115 const CSV_FILE: &str = "tests/data/dataset.csv";
116 const CSV_FMT_FILE: &str = "tests/data/dataset-fmt.csv";
117
118 #[cfg(feature = "sqlite")]
119 type SqlDs = SqliteDataset<Sample>;
120
121 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
122 pub struct Sample {
123 column_str: String,
124 column_bytes: Vec<u8>,
125 column_int: i64,
126 column_bool: bool,
127 column_float: f64,
128 }
129
130 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131 pub struct SampleCsv {
132 column_str: String,
133 column_int: i64,
134 column_bool: bool,
135 column_float: f64,
136 }
137
138 #[cfg(feature = "sqlite")]
139 #[fixture]
140 fn train_dataset() -> SqlDs {
141 SqliteDataset::from_db_file(DB_FILE, "train").unwrap()
142 }
143
144 #[cfg(feature = "sqlite")]
145 #[rstest]
146 pub fn from_dataset(train_dataset: SqlDs) {
147 let dataset = InMemDataset::from_dataset(&train_dataset);
148
149 let record_index: usize = 0;
150
151 assert_eq!(dataset.get(record_index).unwrap().column_str, "HI1");
152 }
153
154 #[cfg(feature = "sqlite")]
155 #[rstest]
156 #[should_panic(expected = "Index out of bounds")]
157 pub fn from_dataset_out_of_bounds(train_dataset: SqlDs) {
158 let non_existing_record_index: usize = 10;
159 train_dataset.get(non_existing_record_index).unwrap();
160 }
161
162 #[test]
163 pub fn from_json_rows() {
164 let dataset = InMemDataset::<Sample>::from_json_rows(JSON_FILE).unwrap();
165
166 let record_index: usize = 1;
167
168 assert_eq!(dataset.get(record_index).unwrap().column_str, "HI2");
169 assert!(!dataset.get(record_index).unwrap().column_bool);
170 }
171
172 #[test]
173 #[should_panic(expected = "Index out of bounds")]
174 pub fn from_json_rows_out_of_bounds() {
175 let dataset = InMemDataset::<Sample>::from_json_rows(JSON_FILE).unwrap();
176 dataset.get(10).unwrap();
177 }
178
179 #[test]
180 pub fn from_csv_rows() {
181 let rdr = csv::ReaderBuilder::new();
182 let dataset = InMemDataset::<SampleCsv>::from_csv(CSV_FILE, &rdr).unwrap();
183
184 let record_index: usize = 1;
185
186 assert_eq!(dataset.get(record_index).unwrap().column_str, "HI2");
187 assert_eq!(dataset.get(record_index).unwrap().column_int, 1);
188 assert!(!dataset.get(record_index).unwrap().column_bool);
189 assert_eq!(dataset.get(record_index).unwrap().column_float, 1.0);
190 }
191
192 #[test]
193 pub fn from_csv_rows_fmt() {
194 let mut rdr = csv::ReaderBuilder::new();
195 let rdr = rdr.delimiter(b' ').has_headers(false);
196 let dataset = InMemDataset::<SampleCsv>::from_csv(CSV_FMT_FILE, rdr).unwrap();
197
198 let record_index: usize = 1;
199
200 assert_eq!(dataset.get(record_index).unwrap().column_str, "HI2");
201 assert_eq!(dataset.get(record_index).unwrap().column_int, 1);
202 assert!(!dataset.get(record_index).unwrap().column_bool);
203 assert_eq!(dataset.get(record_index).unwrap().column_float, 1.0);
204 }
205
206 #[test]
207 pub fn given_in_memory_dataset_when_iterate_should_iterate_though_all_items() {
208 let items_original = test_data::string_items();
209 let dataset = InMemDataset::new(items_original.clone());
210
211 let items: Vec<String> = dataset.iter().map(Result::unwrap).collect();
212
213 assert_eq!(items_original, items);
214 }
215}