use csv::ReaderBuilder;
use dataset_core::{Dataset, DatasetError, acquire_dataset, download_to};
use ndarray::{Array1, Array2};
use serde::Deserialize;
use std::fs::File;
const WINE_RECOGNITION_DATA_URL: &str =
"https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data";
const WINE_RECOGNITION_FILENAME: &str = "wine_recognition.csv";
const WINE_RECOGNITION_SHA256: &str =
"6be6b1203f3d51df0b553a70e57b8a723cd405683958204f96d23d7cd6aea659";
const WINE_RECOGNITION_DATASET_NAME: &str = "wine_recognition";
const N_FEATURES: usize = 13;
type WineRecognitionData = (Array2<f64>, Array1<&'static str>);
#[derive(Deserialize)]
struct WineRecognitionRecord {
class: String,
alcohol: f64,
malic_acid: f64,
ash: f64,
alcalinity_of_ash: f64,
magnesium: f64,
total_phenols: f64,
flavanoids: f64,
nonflavanoid_phenols: f64,
proanthocyanins: f64,
color_intensity: f64,
hue: f64,
od280_od315_of_diluted_wines: f64,
proline: f64,
}
#[derive(Debug)]
pub struct WineRecognition {
dataset: Dataset<WineRecognitionData, DatasetError>,
}
impl WineRecognition {
pub fn new(storage_dir: &str) -> Self {
WineRecognition {
dataset: Dataset::new(storage_dir, Self::load_data),
}
}
fn load_data(dir: &str) -> Result<WineRecognitionData, DatasetError> {
let file_path = acquire_dataset(
dir,
WINE_RECOGNITION_FILENAME,
WINE_RECOGNITION_DATASET_NAME,
Some(WINE_RECOGNITION_SHA256),
|temp_path| {
download_to(
WINE_RECOGNITION_DATA_URL,
temp_path,
Some(WINE_RECOGNITION_FILENAME),
)?;
Ok(temp_path.join(WINE_RECOGNITION_FILENAME))
},
)?;
let file = File::open(&file_path)?;
let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(file);
let mut features = Vec::new();
let mut labels = Vec::new();
for (idx, result) in rdr.deserialize::<WineRecognitionRecord>().enumerate() {
let WineRecognitionRecord {
class,
alcohol,
malic_acid,
ash,
alcalinity_of_ash,
magnesium,
total_phenols,
flavanoids,
nonflavanoid_phenols,
proanthocyanins,
color_intensity,
hue,
od280_od315_of_diluted_wines,
proline,
} = result
.map_err(|e| DatasetError::csv_read_error(WINE_RECOGNITION_DATASET_NAME, e))?;
let line_num = idx + 1;
features.extend_from_slice(&[
alcohol,
malic_acid,
ash,
alcalinity_of_ash,
magnesium,
total_phenols,
flavanoids,
nonflavanoid_phenols,
proanthocyanins,
color_intensity,
hue,
od280_od315_of_diluted_wines,
proline,
]);
labels.push(match class.as_str() {
"1" => "class_1",
"2" => "class_2",
"3" => "class_3",
other => {
return Err(DatasetError::invalid_value(
WINE_RECOGNITION_DATASET_NAME,
"class",
other,
line_num,
));
}
});
}
let n_samples = labels.len();
if n_samples == 0 {
return Err(DatasetError::empty_dataset(WINE_RECOGNITION_DATASET_NAME));
}
let features_array =
Array2::from_shape_vec((n_samples, N_FEATURES), features).map_err(|e| {
DatasetError::array_shape_error(WINE_RECOGNITION_DATASET_NAME, "features", e)
})?;
let labels_array = Array1::from_vec(labels);
Ok((features_array, labels_array))
}
pub fn features(&self) -> Result<&Array2<f64>, DatasetError> {
Ok(&self.dataset.load()?.0)
}
pub fn labels(&self) -> Result<&Array1<&'static str>, DatasetError> {
Ok(&self.dataset.load()?.1)
}
pub fn data(&self) -> Result<&WineRecognitionData, DatasetError> {
self.dataset.load()
}
pub fn get_data(&self) -> Option<&WineRecognitionData> {
self.dataset.get()
}
pub fn get_data_mut(&mut self) -> Option<&mut WineRecognitionData> {
self.dataset.get_mut()
}
pub fn into_data(self) -> Result<WineRecognitionData, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.into_inner()
.expect("data is present after a successful load"))
}
pub fn take_data(&mut self) -> Result<WineRecognitionData, DatasetError> {
self.dataset.load()?;
Ok(self
.dataset
.take()
.expect("data is present after a successful load"))
}
}