1pub mod bin;
2pub mod bin2d;
3pub mod bindot;
4pub mod binhex;
5pub mod boxplot;
6pub mod calendar;
7#[cfg(feature = "ggpubr")]
8pub mod compare_means;
9pub mod contour;
10pub mod contour_filled;
11#[cfg(feature = "ggpubr")]
12pub mod cor;
13pub mod count;
14pub mod density;
15pub mod density2d;
16pub mod dist;
17pub mod ecdf;
18pub mod ellipse;
19pub mod function;
20pub mod identity;
21pub mod loess;
22pub mod marching_squares;
23pub mod qq;
24#[cfg(feature = "regression")]
25pub mod quantile;
26pub mod smooth;
27pub mod sum;
28pub mod summary;
29pub mod summary2d;
30pub mod summary_bin;
31pub mod ydensity;
32
33use crate::aes::{Aes, Aesthetic};
34use crate::data::DataFrame;
35use crate::scale::ScaleSet;
36
37pub trait Stat: Send + Sync {
39 fn compute_group(&self, data: &DataFrame, scales: &ScaleSet) -> DataFrame;
41
42 fn required_aes(&self) -> Vec<Aesthetic>;
44
45 fn default_aes(&self) -> Aes {
47 Aes::default()
48 }
49
50 fn panelwise(&self) -> bool {
55 false
56 }
57
58 fn name(&self) -> &str;
60}
61
62pub(crate) fn bw_nrd0(values: &[f64]) -> f64 {
66 let n = values.len();
67 if n < 2 {
68 return 1.0;
69 }
70 let nf = n as f64;
71 let mean = values.iter().sum::<f64>() / nf;
72 let sd = (values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (nf - 1.0)).sqrt();
73 let mut sorted = values.to_vec();
74 sorted.sort_by(|a, b| a.total_cmp(b));
75 let q = |p: f64| {
76 let h = (n - 1) as f64 * p;
77 let lo = h.floor() as usize;
78 let hi = (lo + 1).min(n - 1);
79 sorted[lo] + (h - lo as f64) * (sorted[hi] - sorted[lo])
80 };
81 let iqr = q(0.75) - q(0.25);
82 let mut lo = sd.min(iqr / 1.34);
83 for fallback in [sd, values[0].abs(), 1.0] {
84 if lo > 0.0 && lo.is_finite() {
85 break;
86 }
87 lo = fallback;
88 }
89 0.9 * lo * nf.powf(-0.2)
90}