]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 { | |
32 | 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 | ||
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 | ||
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 | } |