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}