extern crate itertools;
use itertools::Itertools;
use std::collections::HashMap;
use std::iter::repeat;
use std::num::ParseFloatError;
use std::str::FromStr;
static DATA: &'static str = include_str!("iris.data");
#[derive(Clone, Debug)]
struct Iris {
name: String,
data: [f32; 4],
}
#[derive(Clone, Debug)]
enum ParseError {
Numeric(ParseFloatError),
Other(&'static str),
}
impl From<ParseFloatError> for ParseError {
fn from(err: ParseFloatError) -> Self {
ParseError::Numeric(err)
}
}
impl FromStr for Iris {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iris = Iris { name: "".into(), data: [0.; 4] };
let mut parts = s.split(",").map(str::trim);
for (index, part) in parts.by_ref().take(4).enumerate() {
iris.data[index] = try!(part.parse::<f32>());
}
if let Some(name) = parts.next() {
iris.name = name.into();
} else {
return Err(ParseError::Other("Missing name"))
}
Ok(iris)
}
}
fn main() {
let irises = DATA.lines()
.map(str::parse)
.fold_results(Vec::new(), |mut v, iris: Iris| {
v.push(iris);
v
});
let mut irises = match irises {
Err(e) => {
println!("Error parsing: {:?}", e);
std::process::exit(1);
}
Ok(data) => data,
};
irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
let mut plot_symbols = "+ox".chars().cycle();
let mut symbolmap = HashMap::new();
for (species, species_group) in &irises.iter().group_by_lazy(|iris| &iris.name) {
symbolmap.entry(species).or_insert_with(|| {
plot_symbols.next().unwrap()
});
println!("{} (symbol={})", species, symbolmap[species]);
for iris in species_group {
println!("{}",
iris.data.iter()
.format(", ", |elt, f| f(&format_args!("{:>3.1}", elt))));
}
}
let n = 30; let mut plot = vec![' '; n * n];
for (a, b) in (0..4).combinations() {
println!("Column {} vs {}:", a, b);
plot.iter_mut().set_from(repeat(' '));
let min_max = |data: &[Iris], col| {
data.iter()
.map(|iris| iris.data[col])
.map(|x| (x, x))
.fold1(|(min, max), (elt, _)|
(f32::min(min, elt), f32::max(max, elt))
).expect("Can't find min/max of empty iterator")
};
let (min_x, max_x) = min_max(&irises, a);
let (min_y, max_y) = min_max(&irises, b);
let round_to_grid = |x, min, max| ((x - min) / (max - min) * ((n - 1) as f32)) as usize;
let flip = |ix| n - 1 - ix;
for iris in &irises {
let ix = round_to_grid(iris.data[a], min_x, max_x);
let iy = flip(round_to_grid(iris.data[b], min_y, max_y));
plot[n * iy + ix] = symbolmap[&iris.name];
}
for line in plot.chunks(n) {
println!("{}", line.iter().join(" "))
}
}
}