1 ///
2 /// This example parses, sorts and groups the iris dataset
3 /// and does some simple manipulations.
4 ///
5 /// Iterators and itertools functionality are used throughout.
6 ///
7 ///
8 
9 extern crate itertools;
10 
11 use itertools::Itertools;
12 use std::collections::HashMap;
13 use std::iter::repeat;
14 use std::num::ParseFloatError;
15 use std::str::FromStr;
16 
17 static DATA: &'static str = include_str!("iris.data");
18 
19 #[derive(Clone, Debug)]
20 struct Iris {
21     name: String,
22     data: [f32; 4],
23 }
24 
25 #[derive(Clone, Debug)]
26 enum ParseError {
27     Numeric(ParseFloatError),
28     Other(&'static str),
29 }
30 
31 impl From<ParseFloatError> for ParseError {
from(err: ParseFloatError) -> Self32     fn from(err: ParseFloatError) -> Self {
33         ParseError::Numeric(err)
34     }
35 }
36 
37 /// Parse an Iris from a comma-separated line
38 impl FromStr for Iris {
39     type Err = ParseError;
40 
from_str(s: &str) -> Result<Self, Self::Err>41     fn from_str(s: &str) -> Result<Self, Self::Err> {
42         let mut iris = Iris { name: "".into(), data: [0.; 4] };
43         let mut parts = s.split(",").map(str::trim);
44 
45         // using Iterator::by_ref()
46         for (index, part) in parts.by_ref().take(4).enumerate() {
47             iris.data[index] = try!(part.parse::<f32>());
48         }
49         if let Some(name) = parts.next() {
50             iris.name = name.into();
51         } else {
52             return Err(ParseError::Other("Missing name"))
53         }
54         Ok(iris)
55     }
56 }
57 
main()58 fn main() {
59     // using Itertools::fold_results to create the result of parsing
60     let irises = DATA.lines()
61                      .map(str::parse)
62                      .fold_results(Vec::new(), |mut v, iris: Iris| {
63                          v.push(iris);
64                          v
65                      });
66     let mut irises = match irises {
67         Err(e) => {
68             println!("Error parsing: {:?}", e);
69             std::process::exit(1);
70         }
71         Ok(data) => data,
72     };
73 
74     // Sort them and group them
75     irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
76 
77     // using Iterator::cycle()
78     let mut plot_symbols = "+ox".chars().cycle();
79     let mut symbolmap = HashMap::new();
80 
81     // using Itertools::group_by
82     for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
83         // assign a plot symbol
84         symbolmap.entry(species).or_insert_with(|| {
85             plot_symbols.next().unwrap()
86         });
87         println!("{} (symbol={})", species, symbolmap[species]);
88 
89         for iris in species_group {
90             // using Itertools::format for lazy formatting
91             println!("{:>3.1}", iris.data.iter().format(", "));
92         }
93 
94     }
95 
96     // Look at all combinations of the four columns
97     //
98     // See https://en.wikipedia.org/wiki/Iris_flower_data_set
99     //
100     let n = 30; // plot size
101     let mut plot = vec![' '; n * n];
102 
103     // using Itertools::tuple_combinations
104     for (a, b) in (0..4).tuple_combinations() {
105         println!("Column {} vs {}:", a, b);
106 
107         // Clear plot
108         //
109         // using std::iter::repeat;
110         // using Itertools::set_from
111         plot.iter_mut().set_from(repeat(' '));
112 
113         // using Itertools::minmax
114         let min_max = |data: &[Iris], col| {
115             data.iter()
116                 .map(|iris| iris.data[col])
117                 .minmax()
118                 .into_option()
119                 .expect("Can't find min/max of empty iterator")
120         };
121         let (min_x, max_x) = min_max(&irises, a);
122         let (min_y, max_y) = min_max(&irises, b);
123 
124         // Plot the data points
125         let round_to_grid = |x, min, max| ((x - min) / (max - min) * ((n - 1) as f32)) as usize;
126         let flip = |ix| n - 1 - ix; // reverse axis direction
127 
128         for iris in &irises {
129             let ix = round_to_grid(iris.data[a], min_x, max_x);
130             let iy = flip(round_to_grid(iris.data[b], min_y, max_y));
131             plot[n * iy + ix] = symbolmap[&iris.name];
132         }
133 
134         // render plot
135         //
136         // using Itertools::join
137         for line in plot.chunks(n) {
138             println!("{}", line.iter().join(" "))
139         }
140     }
141 }
142