1
|
|
/*!
|
2
|
|
|
3
|
|
Box plot
|
4
|
|
|
5
|
|
# Examples
|
6
|
|
|
7
|
|
```
|
8
|
|
# use plotlib::repr::BoxPlot;
|
9
|
|
# use plotlib::view::CategoricalView;
|
10
|
|
let b1 = BoxPlot::from_slice(&[0., 2., 3., 4.]);
|
11
|
|
let b2 = BoxPlot::from_vec(vec![0., 2., 3., 4.]);
|
12
|
|
let v = CategoricalView::new().add(b1);
|
13
|
|
```
|
14
|
|
*/
|
15
|
|
|
16
|
|
use std::f64;
|
17
|
|
|
18
|
|
use svg;
|
19
|
|
|
20
|
|
use crate::axis;
|
21
|
|
use crate::repr::CategoricalRepresentation;
|
22
|
|
use crate::style::BoxStyle;
|
23
|
|
use crate::svg_render;
|
24
|
|
use crate::utils;
|
25
|
|
|
26
|
|
enum BoxData<'a> {
|
27
|
|
Owned(Vec<f64>),
|
28
|
|
Ref(&'a [f64]),
|
29
|
|
}
|
30
|
|
|
31
|
|
pub struct BoxPlot<'a> {
|
32
|
|
data: BoxData<'a>,
|
33
|
|
label: String,
|
34
|
|
style: BoxStyle,
|
35
|
|
}
|
36
|
|
|
37
|
|
impl<'a> BoxPlot<'a> {
|
38
|
0
|
pub fn from_slice(v: &'a [f64]) -> Self {
|
39
|
|
BoxPlot {
|
40
|
0
|
data: BoxData::Ref(v),
|
41
|
0
|
style: BoxStyle::new(),
|
42
|
0
|
label: String::new(),
|
43
|
|
}
|
44
|
|
}
|
45
|
|
|
46
|
0
|
pub fn from_vec(v: Vec<f64>) -> Self {
|
47
|
|
BoxPlot {
|
48
|
0
|
data: BoxData::Owned(v),
|
49
|
0
|
style: BoxStyle::new(),
|
50
|
0
|
label: String::new(),
|
51
|
|
}
|
52
|
|
}
|
53
|
|
|
54
|
0
|
pub fn style(mut self, style: &BoxStyle) -> Self {
|
55
|
0
|
self.style.overlay(style);
|
56
|
0
|
self
|
57
|
|
}
|
58
|
|
|
59
|
0
|
pub fn get_style(&self) -> &BoxStyle {
|
60
|
0
|
&self.style
|
61
|
|
}
|
62
|
|
|
63
|
|
pub fn label<T>(mut self, label: T) -> Self
|
64
|
|
where
|
65
|
|
T: Into<String>,
|
66
|
|
{
|
67
|
0
|
self.label = label.into();
|
68
|
0
|
self
|
69
|
|
}
|
70
|
|
|
71
|
0
|
pub fn get_label(&self) -> &String {
|
72
|
0
|
&self.label
|
73
|
|
}
|
74
|
|
|
75
|
0
|
fn get_data(&'a self) -> &'a [f64] {
|
76
|
0
|
match self.data {
|
77
|
0
|
BoxData::Owned(ref v) => v,
|
78
|
0
|
BoxData::Ref(v) => v,
|
79
|
|
}
|
80
|
|
}
|
81
|
|
|
82
|
0
|
fn range(&self) -> (f64, f64) {
|
83
|
0
|
match self.data {
|
84
|
0
|
BoxData::Owned(ref v) => utils::range(v),
|
85
|
0
|
BoxData::Ref(v) => utils::range(v),
|
86
|
|
}
|
87
|
|
}
|
88
|
|
}
|
89
|
|
|
90
|
|
impl<'a> CategoricalRepresentation for BoxPlot<'a> {
|
91
|
|
/// The maximum range. Used for auto-scaling axis
|
92
|
0
|
fn range(&self) -> (f64, f64) {
|
93
|
0
|
self.range()
|
94
|
|
}
|
95
|
|
|
96
|
|
/// The ticks that this representation covers. Used to collect all ticks for display
|
97
|
0
|
fn ticks(&self) -> Vec<String> {
|
98
|
0
|
vec![self.label.clone()]
|
99
|
|
}
|
100
|
|
|
101
|
0
|
fn to_svg(
|
102
|
|
&self,
|
103
|
|
x_axis: &axis::CategoricalAxis,
|
104
|
|
y_axis: &axis::ContinuousAxis,
|
105
|
|
face_width: f64,
|
106
|
|
face_height: f64,
|
107
|
|
) -> svg::node::element::Group {
|
108
|
|
svg_render::draw_face_boxplot(
|
109
|
0
|
self.get_data(),
|
110
|
0
|
&self.label,
|
111
|
0
|
x_axis,
|
112
|
0
|
y_axis,
|
113
|
0
|
face_width,
|
114
|
0
|
face_height,
|
115
|
0
|
&self.style,
|
116
|
|
)
|
117
|
|
}
|
118
|
|
|
119
|
0
|
fn to_text(
|
120
|
|
&self,
|
121
|
|
_x_axis: &axis::CategoricalAxis,
|
122
|
|
_y_axis: &axis::ContinuousAxis,
|
123
|
|
_face_width: u32,
|
124
|
|
_face_height: u32,
|
125
|
|
) -> String {
|
126
|
0
|
"".into()
|
127
|
|
}
|
128
|
|
}
|