use crate::DOWNLOAD_RETRIES;
use crate::table::{Column, ColumnData, Table};
use crate::traits::impl_ml_dataset;
use csv::ReaderBuilder;
use dataset_core::{Dataset, DatasetError, acquire_dataset, download_to_with_retries};
use ndarray::Array1;
use serde::Deserialize;
use std::fs::File;
const BOSTON_HOUSING_DATA_URL: &str =
"https://github.com/selva86/datasets/raw/master/BostonHousing.csv";
const BOSTON_HOUSING_FILENAME: &str = "BostonHousing.csv";
const BOSTON_HOUSING_SHA256: &str =
"ab16ba38fbbbbcc69fe930aab1293104f1442c8279c130d9eba03dd864bef675";
const BOSTON_HOUSING_DATASET_NAME: &str = "boston_housing";
const N_SAMPLES: usize = 506;
#[derive(Deserialize)]
struct BostonHousingRecord {
crim: f64,
zn: f64,
indus: f64,
chas: f64,
nox: f64,
rm: f64,
age: f64,
dis: f64,
rad: f64,
tax: f64,
ptratio: f64,
b: f64,
lstat: f64,
medv: f64,
}
#[derive(Debug)]
pub struct BostonHousing {
dataset: Dataset<Table, DatasetError>,
}
impl BostonHousing {
pub const FEATURE_NAMES: [&'static str; 13] = [
"CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B",
"LSTAT",
];
pub const TARGET: &'static str = "MEDV";
pub fn new(storage_dir: &str) -> Self {
BostonHousing {
dataset: Dataset::new(storage_dir, Self::load_data),
}
}
fn load_data(dir: &str) -> Result<Table, DatasetError> {
let file_path = acquire_dataset(
dir,
BOSTON_HOUSING_FILENAME,
BOSTON_HOUSING_DATASET_NAME,
Some(BOSTON_HOUSING_SHA256),
|temp_path| {
download_to_with_retries(
BOSTON_HOUSING_DATA_URL,
temp_path,
None,
DOWNLOAD_RETRIES,
)?;
Ok(temp_path.join(BOSTON_HOUSING_FILENAME))
},
)?;
let file = File::open(&file_path)?;
let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(file);
let mut crim = Vec::with_capacity(N_SAMPLES);
let mut zn = Vec::with_capacity(N_SAMPLES);
let mut indus = Vec::with_capacity(N_SAMPLES);
let mut chas = Vec::with_capacity(N_SAMPLES);
let mut nox = Vec::with_capacity(N_SAMPLES);
let mut rm = Vec::with_capacity(N_SAMPLES);
let mut age = Vec::with_capacity(N_SAMPLES);
let mut dis = Vec::with_capacity(N_SAMPLES);
let mut rad = Vec::with_capacity(N_SAMPLES);
let mut tax = Vec::with_capacity(N_SAMPLES);
let mut ptratio = Vec::with_capacity(N_SAMPLES);
let mut b = Vec::with_capacity(N_SAMPLES);
let mut lstat = Vec::with_capacity(N_SAMPLES);
let mut medv = Vec::with_capacity(N_SAMPLES);
for result in rdr.deserialize::<BostonHousingRecord>().skip(1) {
let record =
result.map_err(|e| DatasetError::csv_read_error(BOSTON_HOUSING_DATASET_NAME, e))?;
crim.push(record.crim);
zn.push(record.zn);
indus.push(record.indus);
chas.push(record.chas);
nox.push(record.nox);
rm.push(record.rm);
age.push(record.age);
dis.push(record.dis);
rad.push(record.rad);
tax.push(record.tax);
ptratio.push(record.ptratio);
b.push(record.b);
lstat.push(record.lstat);
medv.push(record.medv);
}
Table::new(
BOSTON_HOUSING_DATASET_NAME,
vec![
Column::new(
Self::FEATURE_NAMES[0],
ColumnData::Numeric(Array1::from_vec(crim)),
),
Column::new(
Self::FEATURE_NAMES[1],
ColumnData::Numeric(Array1::from_vec(zn)),
),
Column::new(
Self::FEATURE_NAMES[2],
ColumnData::Numeric(Array1::from_vec(indus)),
),
Column::new(
Self::FEATURE_NAMES[3],
ColumnData::Numeric(Array1::from_vec(chas)),
),
Column::new(
Self::FEATURE_NAMES[4],
ColumnData::Numeric(Array1::from_vec(nox)),
),
Column::new(
Self::FEATURE_NAMES[5],
ColumnData::Numeric(Array1::from_vec(rm)),
),
Column::new(
Self::FEATURE_NAMES[6],
ColumnData::Numeric(Array1::from_vec(age)),
),
Column::new(
Self::FEATURE_NAMES[7],
ColumnData::Numeric(Array1::from_vec(dis)),
),
Column::new(
Self::FEATURE_NAMES[8],
ColumnData::Numeric(Array1::from_vec(rad)),
),
Column::new(
Self::FEATURE_NAMES[9],
ColumnData::Numeric(Array1::from_vec(tax)),
),
Column::new(
Self::FEATURE_NAMES[10],
ColumnData::Numeric(Array1::from_vec(ptratio)),
),
Column::new(
Self::FEATURE_NAMES[11],
ColumnData::Numeric(Array1::from_vec(b)),
),
Column::new(
Self::FEATURE_NAMES[12],
ColumnData::Numeric(Array1::from_vec(lstat)),
),
Column::new(Self::TARGET, ColumnData::Numeric(Array1::from_vec(medv))),
],
)
}
pub fn data(&self) -> Result<&Table, DatasetError> {
self.dataset.load()
}
pub fn get_data(&self) -> Option<&Table> {
self.dataset.get()
}
pub fn get_data_mut(&mut self) -> Option<&mut Table> {
self.dataset.get_mut()
}
pub fn into_data(self) -> Result<Table, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.into_inner()
.expect("data is present after a successful load"))
}
pub fn take_data(&mut self) -> Result<Table, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.take()
.expect("data is present after a successful load"))
}
}
impl_ml_dataset!(BostonHousing, "boston_housing");