ggplot_rs/position/
fill.rs1use crate::data::{DataFrame, Value};
2
3use super::{Position, PositionParams};
4
5pub struct PositionFill;
7
8impl Position for PositionFill {
9 fn compute(&self, data: &mut DataFrame, _params: &PositionParams) {
10 let x_col = match data.column("x") {
11 Some(c) => c.to_vec(),
12 None => return,
13 };
14 let y_col = match data.column("y") {
15 Some(c) => c.to_vec(),
16 None => return,
17 };
18
19 let mut x_totals: Vec<(String, f64)> = Vec::new();
21 for (x, y) in x_col.iter().zip(y_col.iter()) {
22 let x_key = x.to_group_key();
23 let y_val = y.as_f64().unwrap_or(0.0);
24
25 if let Some(entry) = x_totals.iter_mut().find(|(k, _)| k == &x_key) {
26 entry.1 += y_val;
27 } else {
28 x_totals.push((x_key, y_val));
29 }
30 }
31
32 let mut x_cumsum: Vec<(String, f64)> = Vec::new();
34 let mut new_y = Vec::with_capacity(y_col.len());
35 let mut ymin_vals = Vec::with_capacity(y_col.len());
36
37 for (x, y) in x_col.iter().zip(y_col.iter()) {
38 let x_key = x.to_group_key();
39 let y_val = y.as_f64().unwrap_or(0.0);
40
41 let total = x_totals
42 .iter()
43 .find(|(k, _)| k == &x_key)
44 .map(|(_, v)| *v)
45 .unwrap_or(1.0);
46 let total = if total.abs() < f64::EPSILON {
47 1.0
48 } else {
49 total
50 };
51
52 let consumed = x_cumsum
54 .iter()
55 .find(|(k, _)| k == &x_key)
56 .map(|(_, v)| *v)
57 .unwrap_or(0.0);
58
59 let norm_y = y_val / total;
60 new_y.push(Value::Float(1.0 - consumed));
61 ymin_vals.push(Value::Float(1.0 - consumed - norm_y));
62
63 if let Some(entry) = x_cumsum.iter_mut().find(|(k, _)| k == &x_key) {
64 entry.1 += norm_y;
65 } else {
66 x_cumsum.push((x_key, norm_y));
67 }
68 }
69
70 if let Some(col) = data.column_mut("y") {
71 *col = new_y;
72 }
73 if !data.has_column("ymin") {
74 data.add_column("ymin".to_string(), ymin_vals);
75 } else if let Some(col) = data.column_mut("ymin") {
76 *col = ymin_vals;
77 }
78 }
79
80 fn name(&self) -> &str {
81 "fill"
82 }
83}