1use crate::aes::Aesthetic;
2use crate::data::{DataFrame, Value};
3use crate::scale::ScaleSet;
4
5use super::Stat;
6
7pub struct StatEcdf;
10
11impl Default for StatEcdf {
12 fn default() -> Self {
13 StatEcdf
14 }
15}
16
17impl Stat for StatEcdf {
18 fn compute_group(&self, data: &DataFrame, _scales: &ScaleSet) -> DataFrame {
19 let x_col = match data.column("x") {
20 Some(c) => c,
21 None => return DataFrame::new(),
22 };
23
24 let mut values: Vec<f64> = x_col.iter().filter_map(|v| v.as_f64()).collect();
25 if values.is_empty() {
26 return DataFrame::new();
27 }
28
29 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
30 let n = values.len() as f64;
31
32 let mut x_vals = Vec::with_capacity(values.len());
33 let mut y_vals = Vec::with_capacity(values.len());
34
35 for (i, &x) in values.iter().enumerate() {
36 x_vals.push(Value::Float(x));
37 y_vals.push(Value::Float((i + 1) as f64 / n));
38 }
39
40 let mut result = DataFrame::new();
41 result.add_column("x".to_string(), x_vals);
42 result.add_column("y".to_string(), y_vals);
43
44 let nrows = values.len();
46 for col_name in &["color", "fill", "group"] {
47 if let Some(col) = data.column(col_name) {
48 if let Some(first) = col.first() {
49 result.add_column(col_name.to_string(), vec![first.clone(); nrows]);
50 }
51 }
52 }
53
54 result
55 }
56
57 fn required_aes(&self) -> Vec<Aesthetic> {
58 vec![Aesthetic::X]
59 }
60
61 fn name(&self) -> &str {
62 "ecdf"
63 }
64}