stats_ci/
confidence.rs

1//!
2//! Implements the [`Confidence`] enum, which represents a confidence level and direction.
3//!
4
5///
6/// Confidence level of a confidence interval.
7///
8/// ## Operations
9///
10/// ### Creation
11///
12/// * [`Confidence::new`] - create a new two-sided confidence interval with the given confidence level
13/// * [`Confidence::new_two_sided`] - _idem_
14/// * [`Confidence::new_upper`] - create a new one-sided upper confidence interval with the given confidence level
15/// * [`Confidence::new_lower`] - create a new one-sided lower confidence interval with the given confidence level
16///
17/// ### Accessors
18///
19/// * [`Confidence::level`] - return the confidence level of the interval as a number in the range (0, 1)
20/// * [`Confidence::percent`] - return the confidence level of the interval as a percentage
21/// * [`Confidence::kind`] - return the kind of the confidence interval as a string (in English)
22///
23/// ### Characteristics
24///
25/// * [`Confidence::is_two_sided`] - test if the confidence interval is two-sided
26/// * [`Confidence::is_one_sided`] - test if the confidence interval is one-sided
27/// * [`Confidence::is_upper`] - test if the confidence interval is upper (one-sided)
28/// * [`Confidence::is_lower`] - test if the confidence interval is lower (one-sided)
29///
30/// ### Conversions
31///
32/// * [`Confidence::flipped`] - return the confidence interval with the same confidence level but flipped (e.g., upper to lower)
33///
34/// ### Comparison
35///
36/// [`Confidence`] implements [`PartialOrd`] where some confidence `a` is less than some confidence `b`
37/// if they are of the same kind and the confidence level of `a` is less than the confidence level of `b`.
38///
39/// # Examples
40///
41/// ## Creation
42///
43/// To create a two-sided confidence interval with 95% confidence:
44/// ```
45/// # use stats_ci::Confidence;
46/// #
47/// let confidence = Confidence::new_two_sided(0.95);
48/// // alternatively:
49/// let confidence = Confidence::new(0.95);
50/// ```
51///
52/// To create an upper one-sided confidence interval with 90% confidence:
53/// ```
54/// # use stats_ci::Confidence;
55/// #
56/// let confidence = Confidence::new_upper(0.9);
57/// ```
58///
59/// To create a lower one-sided confidence interval with 99% confidence:
60/// ```
61/// # use stats_ci::Confidence;
62/// #
63/// let confidence = Confidence::new_lower(0.99);
64/// ```
65///
66/// ## Accessors
67///
68/// The confidence object provides several accessors:
69/// ```
70/// # use stats_ci::Confidence;
71/// #
72/// let confidence = Confidence::new(0.95);
73/// assert_eq!(confidence.level(), 0.95);
74/// assert_eq!(confidence.percent(), 95.);
75/// assert_eq!(confidence.kind(), "two-sided");
76/// assert!(confidence.is_two_sided());
77/// assert!(!confidence.is_one_sided());
78/// assert!(!confidence.is_upper());
79/// assert!(!confidence.is_lower());
80/// ```
81///
82/// ## Conversions
83///
84/// ```
85/// # use stats_ci::Confidence;
86/// #
87/// let confidence = Confidence::new_upper(0.95);
88/// assert_eq!(confidence.flipped(), Confidence::new_lower(0.95));
89/// ```
90///
91/// ## Comparison
92///
93/// ```
94/// # use stats_ci::Confidence;
95/// #
96/// let confidence = Confidence::new(0.95);
97/// assert!(confidence > Confidence::new(0.9));
98/// assert!(confidence < Confidence::new(0.99));
99/// ```
100///
101#[derive(Debug, Clone, Copy, PartialEq)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103pub enum Confidence {
104    /// Confidence for a two-sided interval.
105    TwoSided(f64),
106
107    /// Confidence for an upper one-sided interval.
108    UpperOneSided(f64),
109
110    /// Confidence for a lower one-sided interval.
111    LowerOneSided(f64),
112}
113
114impl Confidence {
115    ///
116    /// Create a new two-sided confidence interval with the given confidence level.
117    /// This is the same as [`Confidence::new_two_sided`].
118    ///
119    /// # Arguments
120    ///
121    /// * `confidence` - the confidence level, e.g. 0.95 for 95% confidence
122    ///
123    /// # Panics
124    ///
125    /// * if `confidence` is not in the range (0, 1)
126    ///
127    pub fn new(confidence: f64) -> Self {
128        Self::new_two_sided(confidence)
129    }
130
131    ///
132    /// Create a new two-sided confidence interval with the given confidence level.
133    ///
134    /// # Arguments
135    ///
136    /// * `confidence` - the confidence level, e.g. 0.95 for 95% confidence
137    ///
138    /// # Panics
139    ///
140    /// * if `confidence` is not in the range (0, 1)
141    ///
142    pub fn new_two_sided(confidence: f64) -> Self {
143        if confidence > 0. && confidence < 1. {
144            Confidence::TwoSided(confidence)
145        } else {
146            panic!("Confidence level must be in the range (0, 1).")
147        }
148    }
149
150    ///
151    /// Create a new one-sided upper confidence interval with the given confidence level.
152    ///
153    /// # Arguments
154    ///
155    /// * `confidence` - the confidence level, e.g. 0.95 for 95% confidence
156    ///
157    /// # Panics
158    ///
159    /// * if `confidence` is not in the range (0, 1)
160    ///
161    pub fn new_upper(confidence: f64) -> Self {
162        if confidence > 0. && confidence < 1. {
163            Confidence::UpperOneSided(confidence)
164        } else {
165            panic!("Confidence level must be in the range (0, 1).")
166        }
167    }
168
169    ///
170    /// Create a new one-sided lower confidence interval with the given confidence level.
171    ///
172    /// # Arguments
173    ///
174    /// * `confidence` - the confidence level, e.g. 0.95 for 95% confidence
175    ///
176    /// # Panics
177    ///
178    /// * if `confidence` is not in the range (0, 1)
179    ///
180    pub fn new_lower(confidence: f64) -> Self {
181        if confidence > 0. && confidence < 1. {
182            Confidence::LowerOneSided(confidence)
183        } else {
184            panic!("Confidence level must be in the range (0, 1).")
185        }
186    }
187
188    ///
189    /// Return the confidence level of the interval as a number in the range (0, 1).
190    ///
191    pub fn level(&self) -> f64 {
192        match self {
193            Confidence::TwoSided(confidence)
194            | Confidence::UpperOneSided(confidence)
195            | Confidence::LowerOneSided(confidence) => *confidence,
196        }
197    }
198
199    ///
200    /// Return the confidence level of the interval as a percentage.
201    ///
202    pub fn percent(&self) -> f64 {
203        self.level() * 100.
204    }
205
206    ///
207    /// Return the kind of the confidence interval as a string (in English).
208    ///
209    pub fn kind(&self) -> &'static str {
210        match self {
211            Confidence::TwoSided(_) => "two-sided",
212            Confidence::UpperOneSided(_) => "upper one-sided",
213            Confidence::LowerOneSided(_) => "lower one-sided",
214        }
215    }
216
217    ///
218    /// Test if the confidence interval is two-sided.
219    ///
220    pub fn is_two_sided(&self) -> bool {
221        matches!(self, Confidence::TwoSided(_))
222    }
223
224    ///
225    /// Test if the confidence interval is one-sided.
226    ///
227    pub fn is_one_sided(&self) -> bool {
228        !self.is_two_sided()
229    }
230
231    ///
232    /// Test if the confidence interval is upper (one-sided).
233    ///
234    pub fn is_upper(&self) -> bool {
235        matches!(self, Confidence::UpperOneSided(_))
236    }
237
238    ///
239    /// Test if the confidence interval is lower (one-sided).
240    ///
241    pub fn is_lower(&self) -> bool {
242        matches!(self, Confidence::LowerOneSided(_))
243    }
244
245    ///
246    /// Return the confidence interval with the same confidence level but flipped.
247    /// For a two-sided interval, this is the same interval.
248    /// For a one-sided interval, this is the interval with the opposite direction.
249    /// For example, a lower one-sided interval with confidence 0.95 flipped is an upper one-sided interval with confidence 0.95.
250    ///
251    pub fn flipped(&self) -> Self {
252        match self {
253            Confidence::TwoSided(_) => *self,
254            Confidence::UpperOneSided(confidence) => Confidence::LowerOneSided(*confidence),
255            Confidence::LowerOneSided(confidence) => Confidence::UpperOneSided(*confidence),
256        }
257    }
258
259    ///
260    /// Return the quantile of the confidence interval.
261    ///
262    /// For a two-sided interval, this is (1-\alpha/2) where \alpha is 1-confidence.
263    /// For a one-sided interval, this is the confidence level.
264    ///
265    /// # Example
266    ///
267    /// `quantile()` returns 0.975 for two-sided 95% confidence.
268    ///
269    pub(crate) fn quantile(&self) -> f64 {
270        match self {
271            Confidence::TwoSided(confidence) => 1. - (1. - confidence) / 2.,
272            Confidence::UpperOneSided(confidence) | Confidence::LowerOneSided(confidence) => {
273                *confidence
274            }
275        }
276    }
277}
278
279impl Default for Confidence {
280    ///
281    /// Create a new two-sided confidence interval with the default confidence level of 95%.
282    ///
283    fn default() -> Self {
284        Confidence::new_two_sided(0.95)
285    }
286}
287
288impl PartialOrd for Confidence {
289    // NB: the partial ordering obtained from derivation rule is unsound, so we need to
290    // implement it manually.
291    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
292        match (self, other) {
293            (Confidence::TwoSided(x), Confidence::TwoSided(y))
294            | (Confidence::UpperOneSided(x), Confidence::UpperOneSided(y))
295            | (Confidence::LowerOneSided(x), Confidence::LowerOneSided(y)) => x.partial_cmp(y),
296            _ => None,
297        }
298    }
299}
300
301use crate::error::CIError;
302impl TryFrom<f64> for Confidence {
303    type Error = CIError;
304
305    fn try_from(confidence: f64) -> Result<Self, Self::Error> {
306        if confidence > 0. && confidence < 1. {
307            Ok(Confidence::new_two_sided(confidence))
308        } else {
309            Err(CIError::InvalidConfidenceLevel(confidence))
310        }
311    }
312}
313
314impl TryFrom<f32> for Confidence {
315    type Error = CIError;
316
317    fn try_from(confidence: f32) -> Result<Self, Self::Error> {
318        Confidence::try_from(confidence as f64)
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn test_ordering() {
328        let two_sided = Confidence::new_two_sided(0.95);
329        let upper = Confidence::new_upper(0.95);
330        let lower = Confidence::new_lower(0.95);
331        assert!(!(two_sided > upper));
332        assert!(!(two_sided < upper));
333        assert!(!(two_sided > lower));
334        assert!(!(two_sided < lower));
335        assert!(!(lower > upper));
336        assert!(!(lower < upper));
337
338        assert!(two_sided < Confidence::new_two_sided(0.99));
339        assert!(two_sided > Confidence::new_two_sided(0.9));
340
341        assert!(upper < Confidence::new_upper(0.99));
342        assert!(upper > Confidence::new_upper(0.9));
343
344        assert!(lower < Confidence::new_lower(0.99));
345        assert!(lower > Confidence::new_lower(0.9));
346
347        assert_eq!(two_sided, Confidence::new_two_sided(0.95));
348        assert_eq!(upper, Confidence::new_upper(0.95));
349        assert_eq!(lower, Confidence::new_lower(0.95));
350    }
351
352    #[test]
353    fn test_quantile() {
354        let two_sided = Confidence::new_two_sided(0.95);
355        let upper = Confidence::new_upper(0.95);
356        let lower = Confidence::new_lower(0.95);
357        assert_eq!(two_sided.quantile(), 0.975);
358        assert_eq!(upper.quantile(), 0.95);
359        assert_eq!(lower.quantile(), 0.95);
360    }
361
362    #[test]
363    fn test_send() {
364        fn assert_send<T: Send>() {}
365        assert_send::<Confidence>();
366    }
367
368    #[test]
369    fn test_sync() {
370        fn assert_sync<T: Sync>() {}
371        assert_sync::<Confidence>();
372    }
373
374    #[test]
375    #[should_panic]
376    fn test_invalid_two_sided_confidence_level_zero() {
377        Confidence::new_two_sided(0.);
378    }
379
380    #[test]
381    #[should_panic]
382    fn test_invalid_two_sided_confidence_level_one() {
383        Confidence::new_two_sided(1.);
384    }
385
386    #[test]
387    #[should_panic]
388    fn test_invalid_upper_confidence_level_zero() {
389        Confidence::new_upper(0.);
390    }
391
392    #[test]
393    #[should_panic]
394    fn test_invalid_upper_confidence_level_one() {
395        Confidence::new_upper(1.);
396    }
397
398    #[test]
399    #[should_panic]
400    fn test_invalid_lower_confidence_level_zero() {
401        Confidence::new_lower(0.);
402    }
403
404    #[test]
405    #[should_panic]
406    fn test_invalid_lower_confidence_level_one() {
407        Confidence::new_lower(1.);
408    }
409}