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