1use crate::aes::Aesthetic;
2use crate::data::DataFrame;
3use crate::scale::ScaleSet;
4
5use super::Stat;
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub struct StatUnique;
11
12impl Stat for StatUnique {
13 fn compute_group(&self, data: &DataFrame, _scales: &ScaleSet) -> DataFrame {
14 let names = data.column_names();
15 let cols: Vec<_> = names.iter().map(|n| data.column(n).unwrap()).collect();
16 let mut seen = std::collections::HashSet::new();
17 let keep: Vec<usize> = (0..data.nrows())
18 .filter(|&i| {
19 let key: Vec<String> = cols.iter().map(|c| c[i].to_group_key()).collect();
20 seen.insert(key)
21 })
22 .collect();
23 let mut out = DataFrame::new();
24 for (name, col) in names.into_iter().zip(cols) {
25 out.add_column(
26 name.to_string(),
27 keep.iter().map(|&i| col[i].clone()).collect(),
28 );
29 }
30 out
31 }
32
33 fn required_aes(&self) -> Vec<Aesthetic> {
34 Vec::new()
35 }
36
37 fn name(&self) -> &str {
38 "unique"
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::data::Value;
46
47 #[test]
48 fn drops_exact_duplicates_only() {
49 let mut df = DataFrame::new();
50 df.add_column("x".into(), [1.0, 1.0, 1.0, 2.0].map(Value::Float).to_vec());
51 df.add_column("y".into(), [5.0, 5.0, 6.0, 5.0].map(Value::Float).to_vec());
52 let out = StatUnique.compute_group(&df, &ScaleSet::new());
53 assert_eq!(out.nrows(), 3);
54 }
55}