#[cfg(all(feature = "mesalock_sgx", not(target_env = "sgx")))]
use std::prelude::v1::*;
use crate::decision_tree::{Data, DataVec, ValueType, VALUE_TYPE_UNKNOWN};
use crate::errors::Result;
cfg_if! {
if #[cfg(all(feature = "mesalock_sgx", not(target_env = "sgx")))] {
use std::collections::HashMap;
use std::error::Error;
use std::untrusted::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
} else {
use std::collections::HashMap;
#[cfg(not(feature = "mesalock_sgx"))]
use std::fs::File;
#[cfg(feature = "mesalock_sgx")]
use std::untrusted::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
}
}
use regex::Regex;
use serde_derive::{Deserialize, Serialize};
#[derive(Copy, Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum FileFormat {
CSV,
TXT,
}
#[derive(Copy, Debug, Clone, Serialize, Deserialize)]
pub struct InputFormat {
pub ftype: FileFormat,
pub header: bool,
pub label_idx: usize,
pub enable_unknown_value: bool,
pub delimeter: char,
pub feature_size: usize,
}
impl InputFormat {
pub fn csv_format() -> InputFormat {
InputFormat {
ftype: FileFormat::CSV,
header: false,
label_idx: 0,
enable_unknown_value: false,
delimeter: ',',
feature_size: 0,
}
}
pub fn txt_format() -> InputFormat {
InputFormat {
ftype: FileFormat::TXT,
header: false,
label_idx: 0,
enable_unknown_value: false,
delimeter: '\t',
feature_size: 0,
}
}
pub fn to_string(&self) -> String {
let mut s = String::from("");
s.push_str(&format!(
"File type: {}\n",
match self.ftype {
FileFormat::CSV => "CSV",
FileFormat::TXT => "TXT",
}
));
match self.ftype {
FileFormat::CSV => {
s.push_str(&format!("Has header: {}\n", self.header));
s.push_str(&format!("Label index: {}\n", self.label_idx));
}
FileFormat::TXT => {
s.push_str(&format!("Feature size: {}\n", self.feature_size));
}
}
s.push_str(&format!("Delemeter: [{}]", self.delimeter));
s
}
pub fn set_feature_size(&mut self, size: usize) {
self.feature_size = size;
}
pub fn set_label_index(&mut self, idx: usize) {
self.label_idx = idx;
}
pub fn set_delimeter(&mut self, delim: char) {
self.delimeter = delim;
}
}
fn count(mut hash_map: HashMap<char, u32>, word: char) -> HashMap<char, u32> {
{
let c = hash_map.entry(word).or_insert(0);
*c += 1;
}
hash_map
}
pub fn infer(file_name: &str) -> InputFormat {
let file = File::open(file_name).unwrap();
let mut reader = BufReader::new(file);
let mut first_line = String::new();
reader.read_line(&mut first_line).unwrap();
let mut input_format = if first_line.contains(':') {
InputFormat::txt_format()
} else {
InputFormat::csv_format()
};
let reg = match input_format.ftype {
FileFormat::CSV => Regex::new(r"[+-]?\d+(,\d+)*(.\d+(e\d+)?)?").unwrap(),
FileFormat::TXT => Regex::new(r"\d+:[+-]?\d+(,\d+)*(.\d+(e\d+)?)?").unwrap(),
};
let mut second_line = String::new();
reader
.read_line(&mut second_line)
.expect("No second line to read");
let caps = reg.captures(&second_line).unwrap().len();
let second_line_after = reg.replace_all(&second_line, "");
let cnt = second_line_after.chars().fold(HashMap::new(), count);
let default_delim: char = match input_format.ftype {
FileFormat::CSV => ',',
FileFormat::TXT => '\t',
};
let mut flag = false;
if let Some(value) = cnt.get(&default_delim) {
if *value > ((caps as u32) - 2) {
input_format.delimeter = default_delim;
flag = true;
}
}
if !flag {
let mut max_cnt: u32 = 0;
let mut delim = '\t';
for (k, v) in &cnt {
if *v > max_cnt {
max_cnt = *v;
delim = *k;
}
}
input_format.delimeter = delim;
flag = true;
}
assert!(flag);
if let FileFormat::CSV = input_format.ftype {
let first_line_after = reg.replace_all(&first_line, "");
let letters = Regex::new(r"[a-zA-Z]").unwrap();
if let Some(letter_caps) = letters.captures(&first_line_after) {
input_format.header = letter_caps.len() > 0;
}
}
input_format
}
pub fn load_csv(file: &mut File, input_format: InputFormat) -> Result<DataVec> {
file.seek(SeekFrom::Start(0))?;
let mut dv = Vec::new();
let mut reader = BufReader::new(file);
let mut l = String::new();
if input_format.header {
reader.read_line(&mut l).unwrap_or(0);
}
let mut v: Vec<ValueType>;
for line in reader.lines() {
let content = line?;
if input_format.enable_unknown_value {
v = content
.split(input_format.delimeter)
.map(|x| x.parse::<ValueType>().unwrap_or(VALUE_TYPE_UNKNOWN))
.collect();
} else {
v = content
.split(input_format.delimeter)
.map(|x| x.parse::<ValueType>().unwrap())
.collect();
}
dv.push(Data {
label: v.swap_remove(input_format.label_idx),
feature: v,
target: 0.0,
weight: 1.0,
residual: 0.0,
initial_guess: 0.0,
})
}
Ok(dv)
}
pub fn load_txt(file: &mut File, input_format: InputFormat) -> Result<DataVec> {
file.seek(SeekFrom::Start(0))?;
let mut dv = Vec::new();
let reader = BufReader::new(file);
let mut label: ValueType = 0.0;
let mut idx: usize = 0;
let mut val: ValueType = 0.0;
for line in reader.lines() {
let mut v: Vec<ValueType> = vec![VALUE_TYPE_UNKNOWN; input_format.feature_size];
for token in line.unwrap().split(input_format.delimeter) {
let splited_token: Vec<&str> = token.split(':').collect();
if splited_token.len() == 2 {
let mut err = false;
match splited_token[0].parse::<usize>() {
Ok(kk) => {
idx = kk;
}
Err(_) => err = true,
}
match splited_token[1].parse::<ValueType>() {
Ok(vv) => {
val = vv;
}
Err(_) => err = true,
}
if idx >= input_format.feature_size {
err = true;
}
if !err {
v[idx] = val;
}
}
if splited_token.len() == 1 {
label = splited_token[0].parse::<ValueType>().unwrap();
} else {
}
}
dv.push(Data {
label,
feature: v,
target: 0.0,
weight: 1.0,
residual: 0.0,
initial_guess: 0.0,
});
}
Ok(dv)
}
pub fn load(file_name: &str, input_format: InputFormat) -> Result<DataVec> {
let mut file = File::open(file_name)?;
match input_format.ftype {
FileFormat::CSV => load_csv(&mut file, input_format),
FileFormat::TXT => load_txt(&mut file, input_format),
}
}
#[cfg(test)]
mod tests {
use crate::input::{self, FileFormat, InputFormat};
#[test]
fn doc_test_libsvm_format() {
let test_file = "xgb-data/xgb_binary_logistic/agaricus.txt.test";
let mut fmt = InputFormat::txt_format();
fmt.set_feature_size(126);
fmt.set_delimeter(' ');
let test_data = input::load(test_file, fmt);
assert!(test_data.is_ok());
}
#[test]
fn doc_test_libsvm_format_csv() {
let test_file = "xgb-data/xgb_multi_softmax/dermatology.data.test";
let mut fmt = InputFormat::csv_format();
fmt.set_feature_size(34);
let test_data = input::load(test_file, fmt);
assert!(test_data.is_ok());
}
#[test]
fn doc_test_inputformat_csv_format() {
let fmt = InputFormat::csv_format();
assert_eq!(fmt.ftype, FileFormat::CSV);
assert!(!fmt.header);
assert_eq!(fmt.label_idx, 0);
assert!(!fmt.enable_unknown_value);
assert_eq!(fmt.delimeter, ',');
assert_eq!(fmt.feature_size, 0);
}
#[test]
fn doc_test_inputformat_txt_format() {
let fmt = InputFormat::txt_format();
assert_eq!(fmt.ftype, FileFormat::TXT);
assert!(!fmt.header);
assert_eq!(fmt.label_idx, 0);
assert!(!fmt.enable_unknown_value);
assert_eq!(fmt.delimeter, '\t');
assert_eq!(fmt.feature_size, 0);
}
#[test]
fn doc_test_inputformat_to_string() {
let fmt = InputFormat::txt_format();
assert_eq!(
fmt.to_string(),
"File type: TXT\nFeature size: 0\nDelemeter: [\t]"
);
let fmt = InputFormat::csv_format();
assert_eq!(
fmt.to_string(),
"File type: CSV\nHas header: false\nLabel index: 0\nDelemeter: [,]"
);
}
#[test]
fn doc_test_inputformat_set_feature_size() {
let mut fmt = InputFormat::txt_format();
fmt.set_feature_size(10);
assert_eq!(fmt.feature_size, 10);
fmt.set_feature_size(20);
assert_eq!(fmt.feature_size, 20);
let mut fmt = InputFormat::csv_format();
fmt.set_feature_size(10);
assert_eq!(fmt.feature_size, 10);
fmt.set_feature_size(20);
assert_eq!(fmt.feature_size, 20);
}
#[test]
fn doc_test_inputformat_set_label_index() {
let mut fmt = InputFormat::txt_format();
fmt.set_label_index(10);
assert_eq!(fmt.label_idx, 10);
fmt.set_label_index(20);
assert_eq!(fmt.label_idx, 20);
let mut fmt = InputFormat::csv_format();
fmt.set_label_index(10);
assert_eq!(fmt.label_idx, 10);
fmt.set_label_index(20);
assert_eq!(fmt.label_idx, 20);
}
#[test]
fn doc_test_inputformat_set_delimeter() {
let mut fmt = InputFormat::txt_format();
fmt.set_delimeter('\n');
assert_eq!(fmt.delimeter, '\n');
fmt.set_delimeter(':');
assert_eq!(fmt.delimeter, ':');
let mut fmt = InputFormat::csv_format();
fmt.set_delimeter('\n');
assert_eq!(fmt.delimeter, '\n');
fmt.set_delimeter(':');
assert_eq!(fmt.delimeter, ':');
}
#[test]
fn doc_test_test_infer() {
use crate::input::infer;
let train_file = "dataset/iris/train.txt";
let fmt = infer(train_file);
assert_eq!(
fmt.to_string(),
"File type: CSV\nHas header: false\nLabel index: 0\nDelemeter: [,]"
);
}
#[test]
fn doc_test_load_csv() {
use std::fs::File;
let train_file = "dataset/iris/train.txt";
let mut file = File::open(train_file).unwrap();
let mut fmt = InputFormat::csv_format();
fmt.set_label_index(4);
let train_dv = input::load_csv(&mut file, fmt);
assert!(train_dv.is_ok());
}
#[test]
fn doc_test_load_txt() {
use std::fs::File;
let test_file = "xgb-data/xgb_binary_logistic/agaricus.txt.test";
let mut file = File::open(test_file).unwrap();
let mut fmt = InputFormat::csv_format();
fmt.set_feature_size(126);
fmt.set_delimeter(' ');
let test_dv = input::load_txt(&mut file, fmt);
assert!(test_dv.is_ok());
}
#[test]
fn doc_test_load_1() {
let test_file = "xgb-data/xgb_binary_logistic/agaricus.txt.test";
let mut fmt = InputFormat::txt_format();
fmt.set_feature_size(126);
fmt.set_delimeter(' ');
let test_data = input::load(test_file, fmt);
assert!(test_data.is_ok());
}
#[test]
fn doc_test_load_2() {
let test_file = "xgb-data/xgb_multi_softmax/dermatology.data.test";
let mut fmt = InputFormat::csv_format();
fmt.set_feature_size(34);
let test_data = input::load(test_file, fmt);
assert!(test_data.is_ok());
}
}