adjustp/
benjamini_yekutieli.rs1use crate::utils::{rank_rev, reindex, sort_vector_rev};
2use num_traits::{Float, FromPrimitive};
3use std::iter::Sum;
4
5pub struct BenjaminiYekutieli<T: Float + FromPrimitive + Sum> {
6 num_elements: usize,
7 current_max: T,
8 cumulative: T,
9}
10
11impl<T: Float + FromPrimitive + Sum> Default for BenjaminiYekutieli<T> {
12 fn default() -> Self {
13 Self::new(0)
14 }
15}
16
17impl<T: Float + FromPrimitive + Sum> BenjaminiYekutieli<T> {
18 #[must_use]
24 pub fn new(num_elements: usize) -> Self {
25 let cumulative = Self::calculate_cumulative(num_elements);
26 Self {
27 num_elements,
28 current_max: T::one(),
29 cumulative,
30 }
31 }
32
33 fn calculate_cumulative(num_elements: usize) -> T {
35 (1..=num_elements)
36 .map(|x| T::from_usize(x).unwrap().recip())
37 .sum()
38 }
39
40 pub fn adjust(&mut self, pvalue: T, rank: usize) -> T {
50 let n = T::from_usize(self.num_elements).unwrap();
51 let r = T::from_usize(rank).unwrap();
52 let qvalue = (pvalue * self.cumulative * (n / r))
53 .min(self.current_max)
54 .min(T::one());
55 self.current_max = qvalue;
56 qvalue
57 }
58
59 #[must_use]
69 pub fn adjust_slice(slice: &[T]) -> Vec<T> {
70 if slice.is_empty() {
71 return Vec::new();
72 }
73
74 let mut method = Self::new(slice.len());
75 let original_index = rank_rev(slice);
76 let max = original_index.len();
77
78 let sorted_qvalues = sort_vector_rev(slice)
79 .iter()
80 .enumerate()
81 .map(|(idx, &p)| method.adjust(p, max - idx))
82 .collect::<Vec<T>>();
83
84 reindex(&sorted_qvalues, &original_index)
85 }
86}
87
88#[cfg(test)]
89mod testing {
90 use super::BenjaminiYekutieli;
91
92 #[test]
93 fn example() {
94 let pvalues = vec![0.1, 0.2, 0.3, 0.4, 0.1];
95 let adj_pvalues = BenjaminiYekutieli::adjust_slice(&pvalues);
96 assert_eq!(
97 adj_pvalues,
98 vec![
99 0.5708333333333333,
100 0.7611111111111112,
101 0.8562500,
102 0.9133333333333333,
103 0.5708333333333333
104 ]
105 );
106 }
107
108 #[test]
109 fn example_null() {
110 let pvalues: Vec<f64> = vec![];
111 let adj_pvalues = BenjaminiYekutieli::adjust_slice(&pvalues);
112 assert_eq!(adj_pvalues, vec![]);
113 }
114
115 #[test]
116 fn example_adjust() {
117 let mut b = BenjaminiYekutieli::new(100);
118 assert_eq!(b.adjust(0.001, 1), 0.5187377517639621);
119 assert_eq!(b.adjust(0.001, 2), 0.25936887588198104);
120 assert_eq!(b.adjust(0.001, 3), 0.1729125839213207);
121 }
122}