1
/*!
2

3
A module for Histograms
4

5
# Examples
6

7
```
8
# use plotlib::repr::Histogram;
9
// Create some dummy data
10
let data = vec![0.3, 0.5, 6.4, 5.3, 3.6, 3.6, 3.5, 7.5, 4.0];
11

12
// and create a histogram out of it
13
let h = Histogram::from_slice(&data, plotlib::repr::HistogramBins::Count(30));
14
```
15

16
TODO:
17

18
- frequency or density option
19
    - Variable bins implies frequency
20
    - What should be the default?
21
*/
22

23
use std;
24

25
use svg;
26

27
use crate::axis;
28
use crate::repr::ContinuousRepresentation;
29
use crate::style::BoxStyle;
30
use crate::svg_render;
31
use crate::text_render;
32
use crate::utils::PairWise;
33

34
#[derive(Debug)]
35
enum HistogramType {
36
    Count,
37
    Density,
38
}
39

40
#[derive(Debug)]
41
pub enum HistogramBins {
42
    Count(usize),
43
    Bounds(Vec<f64>),
44
}
45

46
/**
47
A one-dimensional histogram with equal binning.
48
*/
49
#[derive(Debug)]
50
pub struct Histogram {
51
    pub bin_bounds: Vec<f64>,    // will have N_bins + 1 entries
52
    pub bin_counts: Vec<f64>,    // will have N_bins entries
53
    pub bin_densities: Vec<f64>, // will have N_bins entries
54
    style: BoxStyle,
55
    h_type: HistogramType,
56
}
57

58
impl Histogram {
59 1
    pub fn from_slice(v: &[f64], bins: HistogramBins) -> Histogram {
60 1
        let mut max = v.iter().fold(-1. / 0., |a, &b| f64::max(a, b));
61 1
        let mut min = v.iter().fold(1. / 0., |a, &b| f64::min(a, b));
62

63 1
        if (min - max).abs() < std::f64::EPSILON {
64 1
            min -= 0.5;
65 1
            max += 0.5;
66
        }
67

68 1
        let (num_bins, bounds) = match bins {
69 1
            HistogramBins::Count(num_bins) => {
70 1
                let range = max - min;
71 1
                let mut bounds: Vec<f64> = (0..num_bins)
72 1
                    .map(|n| (n as f64 / num_bins as f64) * range + min)
73
                    .collect();
74 1
                bounds.push(max);
75 1
                (num_bins, bounds)
76
            }
77 1
            HistogramBins::Bounds(bounds) => (bounds.len(), bounds),
78
        };
79

80 1
        let mut bins = vec![0; num_bins];
81

82 1
        let bin_width = (max - min) / num_bins as f64; // width of bin in real units
83

84 1
        for &val in v.iter() {
85 1
            let bin = bounds
86
                .pairwise()
87
                .enumerate()
88 1
                .skip_while(|&(_, (&l, &u))| !(val >= l && val <= u))
89 1
                .map(|(i, (_, _))| i)
90
                .next()
91
                .unwrap();
92 1
            bins[bin] += 1;
93
        }
94 1
        let density_per_bin = bins.iter().map(|&x| f64::from(x) / bin_width).collect();
95

96
        Histogram {
97
            bin_bounds: bounds,
98 1
            bin_counts: bins.iter().map(|&x| f64::from(x)).collect(),
99
            bin_densities: density_per_bin,
100 1
            style: BoxStyle::new(),
101
            h_type: HistogramType::Count,
102
        }
103
    }
104

105 0
    pub fn num_bins(&self) -> usize {
106 0
        self.bin_counts.len()
107
    }
108

109 0
    fn x_range(&self) -> (f64, f64) {
110
        (
111 0
            *self.bin_bounds.first().unwrap(),
112 0
            *self.bin_bounds.last().unwrap(),
113
        )
114
    }
115

116 0
    fn y_range(&self) -> (f64, f64) {
117 0
        let max = self
118
            .get_values()
119
            .iter()
120 0
            .fold(-1. / 0., |a, &b| f64::max(a, b));
121 0
        (0., max)
122
    }
123

124 0
    pub fn style(mut self, style: &BoxStyle) -> Self {
125 0
        self.style.overlay(style);
126 0
        self
127
    }
128

129
    /**
130
    Set the histogram to display as normalised densities
131
    */
132 0
    pub fn density(mut self) -> Self {
133 0
        self.h_type = HistogramType::Density;
134 0
        self
135
    }
136

137 0
    pub fn get_style(&self) -> &BoxStyle {
138 0
        &self.style
139
    }
140

141 1
    pub fn get_values(&self) -> &[f64] {
142 1
        match self.h_type {
143 1
            HistogramType::Count => &self.bin_counts,
144 0
            HistogramType::Density => &self.bin_densities,
145
        }
146
    }
147
}
148

149
impl ContinuousRepresentation for Histogram {
150 0
    fn range(&self, dim: u32) -> (f64, f64) {
151 0
        match dim {
152 0
            0 => self.x_range(),
153 0
            1 => self.y_range(),
154 0
            _ => panic!("Axis out of range"),
155
        }
156
    }
157

158 0
    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 0
        svg_render::draw_face_bars(self, x_axis, y_axis, face_width, face_height, &self.style)
166
    }
167 0
    fn legend_svg(&self) -> Option<svg::node::element::Group> {
168
        // TODO implement
169 0
        None
170
    }
171

172 0
    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 0
        text_render::render_face_bars(self, x_axis, y_axis, face_width, face_height)
180
    }
181
}
182

183
#[cfg(test)]
184
mod tests {
185
    use super::*;
186

187
    #[test]
188 1
    fn test_histogram_from_slice() {
189 1
        assert_eq!(
190 1
            Histogram::from_slice(&[], HistogramBins::Count(3)).get_values(),
191
            [0., 0., 0.]
192
        );
193 1
        assert_eq!(
194 1
            Histogram::from_slice(&[0.], HistogramBins::Count(3)).get_values(),
195
            [0., 1., 0.]
196
        );
197 1
        assert_eq!(
198 1
            Histogram::from_slice(&[0., 3.], HistogramBins::Count(3)).get_values(),
199
            [1., 0., 1.]
200
        );
201 1
        assert_eq!(
202 1
            Histogram::from_slice(&[0., 1., 2., 3.], HistogramBins::Count(3)).get_values(),
203
            [2., 1., 1.]
204
        );
205
    }
206

207
    #[test]
208 1
    fn test_histogram_define_bin_bounds() {
209 1
        assert_eq!(
210 1
            Histogram::from_slice(&[0., 1.], HistogramBins::Count(3)).bin_bounds,
211
            [0., 1. / 3., 2. / 3., 1.]
212
        );
213 1
        assert_eq!(
214 1
            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
}

Read our documentation on viewing source code .

Loading