1use std;
24
25use svg;
26
27use crate::axis;
28use crate::repr::ContinuousRepresentation;
29use crate::style::BoxStyle;
30use crate::svg_render;
31use crate::text_render;
32use crate::utils::PairWise;
33
34#[derive(Debug)]
35enum HistogramType {
36 Count,
37 Density,
38}
39
40#[derive(Debug)]
41pub enum HistogramBins {
42 Count(usize),
43 Bounds(Vec<f64>),
44}
45
46#[derive(Debug)]
50pub struct Histogram {
51 pub bin_bounds: Vec<f64>, pub bin_counts: Vec<f64>, pub bin_densities: Vec<f64>, style: BoxStyle,
55 h_type: HistogramType,
56}
57
58impl Histogram {
59 pub fn from_slice(v: &[f64], bins: HistogramBins) -> Histogram {
60 let mut max = v.iter().fold(-1. / 0., |a, &b| f64::max(a, b));
61 let mut min = v.iter().fold(1. / 0., |a, &b| f64::min(a, b));
62
63 if (min - max).abs() < std::f64::EPSILON {
64 min -= 0.5;
65 max += 0.5;
66 }
67
68 let (num_bins, bounds) = match bins {
69 HistogramBins::Count(num_bins) => {
70 let range = max - min;
71 let mut bounds: Vec<f64> = (0..num_bins)
72 .map(|n| (n as f64 / num_bins as f64) * range + min)
73 .collect();
74 bounds.push(max);
75 (num_bins, bounds)
76 }
77 HistogramBins::Bounds(bounds) => (bounds.len(), bounds),
78 };
79
80 let mut bins = vec![0; num_bins];
81
82 let bin_width = (max - min) / num_bins as f64; for &val in v.iter() {
85 let bin = bounds
86 .pairwise()
87 .enumerate()
88 .skip_while(|&(_, (&l, &u))| !(val >= l && val <= u))
89 .map(|(i, (_, _))| i)
90 .next()
91 .unwrap();
92 bins[bin] += 1;
93 }
94 let density_per_bin = bins.iter().map(|&x| f64::from(x) / bin_width).collect();
95
96 Histogram {
97 bin_bounds: bounds,
98 bin_counts: bins.iter().map(|&x| f64::from(x)).collect(),
99 bin_densities: density_per_bin,
100 style: BoxStyle::new(),
101 h_type: HistogramType::Count,
102 }
103 }
104
105 pub fn num_bins(&self) -> usize {
106 self.bin_counts.len()
107 }
108
109 fn x_range(&self) -> (f64, f64) {
110 (
111 *self.bin_bounds.first().unwrap(),
112 *self.bin_bounds.last().unwrap(),
113 )
114 }
115
116 fn y_range(&self) -> (f64, f64) {
117 let max = self
118 .get_values()
119 .iter()
120 .fold(-1. / 0., |a, &b| f64::max(a, b));
121 (0., max)
122 }
123
124 pub fn style(mut self, style: &BoxStyle) -> Self {
125 self.style.overlay(style);
126 self
127 }
128
129 pub fn density(mut self) -> Self {
133 self.h_type = HistogramType::Density;
134 self
135 }
136
137 pub fn get_style(&self) -> &BoxStyle {
138 &self.style
139 }
140
141 pub fn get_values(&self) -> &[f64] {
142 match self.h_type {
143 HistogramType::Count => &self.bin_counts,
144 HistogramType::Density => &self.bin_densities,
145 }
146 }
147}
148
149impl ContinuousRepresentation for Histogram {
150 fn range(&self, dim: u32) -> (f64, f64) {
151 match dim {
152 0 => self.x_range(),
153 1 => self.y_range(),
154 _ => panic!("Axis out of range"),
155 }
156 }
157
158 fn to_svg(
159 &self,
160 x_axis: &axis::ContinuousAxis,
161 y_axis: &axis::ContinuousAxis,
162 face_width: f64,
163 face_height: f64,
164 ) -> svg::node::element::Group {
165 svg_render::draw_face_bars(self, x_axis, y_axis, face_width, face_height, &self.style)
166 }
167 fn legend_svg(&self) -> Option<svg::node::element::Group> {
168 None
170 }
171
172 fn to_text(
173 &self,
174 x_axis: &axis::ContinuousAxis,
175 y_axis: &axis::ContinuousAxis,
176 face_width: u32,
177 face_height: u32,
178 ) -> String {
179 text_render::render_face_bars(self, x_axis, y_axis, face_width, face_height)
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_histogram_from_slice() {
189 assert_eq!(
190 Histogram::from_slice(&[], HistogramBins::Count(3)).get_values(),
191 [0., 0., 0.]
192 );
193 assert_eq!(
194 Histogram::from_slice(&[0.], HistogramBins::Count(3)).get_values(),
195 [0., 1., 0.]
196 );
197 assert_eq!(
198 Histogram::from_slice(&[0., 3.], HistogramBins::Count(3)).get_values(),
199 [1., 0., 1.]
200 );
201 assert_eq!(
202 Histogram::from_slice(&[0., 1., 2., 3.], HistogramBins::Count(3)).get_values(),
203 [2., 1., 1.]
204 );
205 }
206
207 #[test]
208 fn test_histogram_define_bin_bounds() {
209 assert_eq!(
210 Histogram::from_slice(&[0., 1.], HistogramBins::Count(3)).bin_bounds,
211 [0., 1. / 3., 2. / 3., 1.]
212 );
213 assert_eq!(
214 Histogram::from_slice(&[], HistogramBins::Bounds([0., 1., 1.5, 2., 5.6].to_vec()))
215 .bin_bounds,
216 [0., 1., 1.5, 2., 5.6]
217 );
218 }
219}