use csv::ReaderBuilder;
use dataset_core::{Dataset, DatasetError, acquire_dataset, download_to};
use ndarray::{Array1, Array2};
use serde::Deserialize;
use std::fs::File;
type TitanicData = (Array2<String>, Array2<f64>, Array1<f64>);
const TITANIC_DATA_URL: &str =
"https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv";
const TITANIC_FILENAME: &str = "titanic.csv";
const TITANIC_SHA256: &str = "4a437fde05fe5264e1701a7387ac6fb75393772ba38bb2c9c566405af5af4bd7";
const TITANIC_DATASET_NAME: &str = "titanic";
#[derive(Deserialize)]
struct TitanicRecord {
passenger_id: Option<f64>,
survived: Option<f64>,
pclass: Option<f64>,
name: String,
sex: String,
age: Option<f64>,
sib_sp: Option<f64>,
parch: Option<f64>,
ticket: String,
fare: Option<f64>,
cabin: String,
embarked: String,
}
#[derive(Debug)]
pub struct Titanic {
dataset: Dataset<TitanicData, DatasetError>,
}
impl Titanic {
pub fn new(storage_dir: &str) -> Self {
Titanic {
dataset: Dataset::new(storage_dir, Self::load_data),
}
}
fn load_data(dir: &str) -> Result<TitanicData, DatasetError> {
let file_path = acquire_dataset(
dir,
TITANIC_FILENAME,
TITANIC_DATASET_NAME,
Some(TITANIC_SHA256),
|temp_path| {
download_to(TITANIC_DATA_URL, temp_path, None)?;
Ok(temp_path.join(TITANIC_FILENAME))
},
)?;
let file = File::open(&file_path)?;
let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(file);
let mut string_features = Vec::new();
let mut numeric_features = Vec::new();
let mut labels = Vec::new();
for result in rdr.deserialize::<TitanicRecord>().skip(1) {
let TitanicRecord {
passenger_id,
survived,
pclass,
name,
sex,
age,
sib_sp,
parch,
ticket,
fare,
cabin,
embarked,
} = result.map_err(|e| DatasetError::csv_read_error(TITANIC_DATASET_NAME, e))?;
labels.push(survived.unwrap_or(f64::NAN));
numeric_features.extend_from_slice(&[
passenger_id.unwrap_or(f64::NAN),
pclass.unwrap_or(f64::NAN),
age.unwrap_or(f64::NAN),
sib_sp.unwrap_or(f64::NAN),
parch.unwrap_or(f64::NAN),
fare.unwrap_or(f64::NAN),
]);
string_features.push(name);
string_features.push(sex);
string_features.push(ticket);
string_features.push(cabin);
string_features.push(embarked);
}
let n_samples = labels.len();
if n_samples == 0 {
return Err(DatasetError::empty_dataset(TITANIC_DATASET_NAME));
}
let string_array =
Array2::from_shape_vec((n_samples, 5), string_features).map_err(|e| {
DatasetError::array_shape_error(TITANIC_DATASET_NAME, "string_features", e)
})?;
let numeric_array =
Array2::from_shape_vec((n_samples, 6), numeric_features).map_err(|e| {
DatasetError::array_shape_error(TITANIC_DATASET_NAME, "numeric_features", e)
})?;
let labels_array = Array1::from_vec(labels);
Ok((string_array, numeric_array, labels_array))
}
pub fn features(&self) -> Result<(&Array2<String>, &Array2<f64>), DatasetError> {
let data = self.dataset.load()?;
Ok((&data.0, &data.1))
}
pub fn labels(&self) -> Result<&Array1<f64>, DatasetError> {
Ok(&self.dataset.load()?.2)
}
pub fn data(&self) -> Result<&TitanicData, DatasetError> {
self.dataset.load()
}
pub fn get_data(&self) -> Option<&TitanicData> {
self.dataset.get()
}
pub fn get_data_mut(&mut self) -> Option<&mut TitanicData> {
self.dataset.get_mut()
}
pub fn into_data(self) -> Result<TitanicData, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.into_inner()
.expect("data is present after a successful load"))
}
pub fn take_data(&mut self) -> Result<TitanicData, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.take()
.expect("data is present after a successful load"))
}
}