ndarray_stats/maybe_nan/
mod.rs

1use ndarray::prelude::*;
2use ndarray::{s, Data, DataMut, RemoveAxis};
3use noisy_float::types::{N32, N64};
4use std::mem;
5
6/// A number type that can have not-a-number values.
7pub trait MaybeNan: Sized {
8    /// A type that is guaranteed not to be a NaN value.
9    type NotNan;
10
11    /// Returns `true` if the value is a NaN value.
12    fn is_nan(&self) -> bool;
13
14    /// Tries to convert the value to `NotNan`.
15    ///
16    /// Returns `None` if the value is a NaN value.
17    fn try_as_not_nan(&self) -> Option<&Self::NotNan>;
18
19    /// Converts the value.
20    ///
21    /// If the value is `None`, a NaN value is returned.
22    fn from_not_nan(_: Self::NotNan) -> Self;
23
24    /// Converts the value.
25    ///
26    /// If the value is `None`, a NaN value is returned.
27    fn from_not_nan_opt(_: Option<Self::NotNan>) -> Self;
28
29    /// Converts the value.
30    ///
31    /// If the value is `None`, a NaN value is returned.
32    fn from_not_nan_ref_opt(_: Option<&Self::NotNan>) -> &Self;
33
34    /// Returns a view with the NaN values removed.
35    ///
36    /// This modifies the input view by moving elements as necessary. The final
37    /// order of the elements is unspecified. However, this method is
38    /// idempotent, and given the same input data, the result is always ordered
39    /// the same way.
40    fn remove_nan_mut(_: ArrayViewMut1<'_, Self>) -> ArrayViewMut1<'_, Self::NotNan>;
41}
42
43/// Returns a view with the NaN values removed.
44///
45/// This modifies the input view by moving elements as necessary.
46fn remove_nan_mut<A: MaybeNan>(mut view: ArrayViewMut1<'_, A>) -> ArrayViewMut1<'_, A> {
47    if view.is_empty() {
48        return view.slice_move(s![..0]);
49    }
50    let mut i = 0;
51    let mut j = view.len() - 1;
52    loop {
53        // At this point, `i == 0 || !view[i-1].is_nan()`
54        // and `j == view.len() - 1 || view[j+1].is_nan()`.
55        while i <= j && !view[i].is_nan() {
56            i += 1;
57        }
58        // At this point, `view[i].is_nan() || i == j + 1`.
59        while j > i && view[j].is_nan() {
60            j -= 1;
61        }
62        // At this point, `!view[j].is_nan() || j == i`.
63        if i >= j {
64            return view.slice_move(s![..i]);
65        } else {
66            view.swap(i, j);
67            i += 1;
68            j -= 1;
69        }
70    }
71}
72
73/// Casts a view from one element type to another.
74///
75/// # Panics
76///
77/// Panics if `T` and `U` differ in size or alignment.
78///
79/// # Safety
80///
81/// The caller must ensure that qll elements in `view` are valid values for type `U`.
82unsafe fn cast_view_mut<T, U>(mut view: ArrayViewMut1<'_, T>) -> ArrayViewMut1<'_, U> {
83    assert_eq!(mem::size_of::<T>(), mem::size_of::<U>());
84    assert_eq!(mem::align_of::<T>(), mem::align_of::<U>());
85    let ptr: *mut U = view.as_mut_ptr().cast();
86    let len: usize = view.len_of(Axis(0));
87    let stride: isize = view.stride_of(Axis(0));
88    if len <= 1 {
89        // We can use a stride of `0` because the stride is irrelevant for the `len == 1` case.
90        let stride = 0;
91        ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
92    } else if stride >= 0 {
93        let stride = stride as usize;
94        ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
95    } else {
96        // At this point, stride < 0. We have to construct the view by using the inverse of the
97        // stride and then inverting the axis, since `ArrayViewMut::from_shape_ptr` requires the
98        // stride to be nonnegative.
99        let neg_stride = stride.checked_neg().unwrap() as usize;
100        // This is safe because `ndarray` guarantees that it's safe to offset the
101        // pointer anywhere in the array.
102        let neg_ptr = ptr.offset((len - 1) as isize * stride);
103        let mut v = ArrayViewMut1::from_shape_ptr([len].strides([neg_stride]), neg_ptr);
104        v.invert_axis(Axis(0));
105        v
106    }
107}
108
109macro_rules! impl_maybenan_for_fxx {
110    ($fxx:ident, $Nxx:ident) => {
111        impl MaybeNan for $fxx {
112            type NotNan = $Nxx;
113
114            fn is_nan(&self) -> bool {
115                $fxx::is_nan(*self)
116            }
117
118            fn try_as_not_nan(&self) -> Option<&$Nxx> {
119                $Nxx::try_borrowed(self)
120            }
121
122            fn from_not_nan(value: $Nxx) -> $fxx {
123                value.raw()
124            }
125
126            fn from_not_nan_opt(value: Option<$Nxx>) -> $fxx {
127                match value {
128                    None => ::std::$fxx::NAN,
129                    Some(num) => num.raw(),
130                }
131            }
132
133            fn from_not_nan_ref_opt(value: Option<&$Nxx>) -> &$fxx {
134                match value {
135                    None => &::std::$fxx::NAN,
136                    Some(num) => num.as_ref(),
137                }
138            }
139
140            fn remove_nan_mut(view: ArrayViewMut1<'_, $fxx>) -> ArrayViewMut1<'_, $Nxx> {
141                let not_nan = remove_nan_mut(view);
142                // This is safe because `remove_nan_mut` has removed the NaN values, and `$Nxx` is
143                // a thin wrapper around `$fxx`.
144                unsafe { cast_view_mut(not_nan) }
145            }
146        }
147    };
148}
149impl_maybenan_for_fxx!(f32, N32);
150impl_maybenan_for_fxx!(f64, N64);
151
152macro_rules! impl_maybenan_for_opt_never_nan {
153    ($ty:ty) => {
154        impl MaybeNan for Option<$ty> {
155            type NotNan = NotNone<$ty>;
156
157            fn is_nan(&self) -> bool {
158                self.is_none()
159            }
160
161            fn try_as_not_nan(&self) -> Option<&NotNone<$ty>> {
162                if self.is_none() {
163                    None
164                } else {
165                    // This is safe because we have checked for the `None`
166                    // case, and `NotNone<$ty>` is a thin wrapper around `Option<$ty>`.
167                    Some(unsafe { &*(self as *const Option<$ty> as *const NotNone<$ty>) })
168                }
169            }
170
171            fn from_not_nan(value: NotNone<$ty>) -> Option<$ty> {
172                value.into_inner()
173            }
174
175            fn from_not_nan_opt(value: Option<NotNone<$ty>>) -> Option<$ty> {
176                value.and_then(|v| v.into_inner())
177            }
178
179            fn from_not_nan_ref_opt(value: Option<&NotNone<$ty>>) -> &Option<$ty> {
180                match value {
181                    None => &None,
182                    // This is safe because `NotNone<$ty>` is a thin wrapper around
183                    // `Option<$ty>`.
184                    Some(num) => unsafe { &*(num as *const NotNone<$ty> as *const Option<$ty>) },
185                }
186            }
187
188            fn remove_nan_mut(view: ArrayViewMut1<'_, Self>) -> ArrayViewMut1<'_, Self::NotNan> {
189                let not_nan = remove_nan_mut(view);
190                // This is safe because `remove_nan_mut` has removed the `None`
191                // values, and `NotNone<$ty>` is a thin wrapper around `Option<$ty>`.
192                unsafe {
193                    ArrayViewMut1::from_shape_ptr(
194                        not_nan.dim(),
195                        not_nan.as_ptr() as *mut NotNone<$ty>,
196                    )
197                }
198            }
199        }
200    };
201}
202impl_maybenan_for_opt_never_nan!(u8);
203impl_maybenan_for_opt_never_nan!(u16);
204impl_maybenan_for_opt_never_nan!(u32);
205impl_maybenan_for_opt_never_nan!(u64);
206impl_maybenan_for_opt_never_nan!(u128);
207impl_maybenan_for_opt_never_nan!(i8);
208impl_maybenan_for_opt_never_nan!(i16);
209impl_maybenan_for_opt_never_nan!(i32);
210impl_maybenan_for_opt_never_nan!(i64);
211impl_maybenan_for_opt_never_nan!(i128);
212impl_maybenan_for_opt_never_nan!(N32);
213impl_maybenan_for_opt_never_nan!(N64);
214
215/// A thin wrapper around `Option` that guarantees that the value is not
216/// `None`.
217#[derive(Clone, Copy, Debug)]
218#[repr(transparent)]
219pub struct NotNone<T>(Option<T>);
220
221impl<T> NotNone<T> {
222    /// Creates a new `NotNone` containing the given value.
223    pub fn new(value: T) -> NotNone<T> {
224        NotNone(Some(value))
225    }
226
227    /// Creates a new `NotNone` containing the given value.
228    ///
229    /// Returns `None` if `value` is `None`.
230    pub fn try_new(value: Option<T>) -> Option<NotNone<T>> {
231        if value.is_some() {
232            Some(NotNone(value))
233        } else {
234            None
235        }
236    }
237
238    /// Returns the underling option.
239    pub fn into_inner(self) -> Option<T> {
240        self.0
241    }
242
243    /// Moves the value out of the inner option.
244    ///
245    /// This method is guaranteed not to panic.
246    pub fn unwrap(self) -> T {
247        match self.0 {
248            Some(inner) => inner,
249            None => unsafe { ::std::hint::unreachable_unchecked() },
250        }
251    }
252
253    /// Maps an `NotNone<T>` to `NotNone<U>` by applying a function to the
254    /// contained value.
255    pub fn map<U, F>(self, f: F) -> NotNone<U>
256    where
257        F: FnOnce(T) -> U,
258    {
259        NotNone::new(f(self.unwrap()))
260    }
261}
262
263/// Extension trait for `ArrayBase` providing NaN-related functionality.
264pub trait MaybeNanExt<A, S, D>
265where
266    A: MaybeNan,
267    S: Data<Elem = A>,
268    D: Dimension,
269{
270    /// Traverse the non-NaN array elements and apply a fold, returning the
271    /// resulting value.
272    ///
273    /// Elements are visited in arbitrary order.
274    fn fold_skipnan<'a, F, B>(&'a self, init: B, f: F) -> B
275    where
276        A: 'a,
277        F: FnMut(B, &'a A::NotNan) -> B;
278
279    /// Traverse the non-NaN elements and their indices and apply a fold,
280    /// returning the resulting value.
281    ///
282    /// Elements are visited in arbitrary order.
283    fn indexed_fold_skipnan<'a, F, B>(&'a self, init: B, f: F) -> B
284    where
285        A: 'a,
286        F: FnMut(B, (D::Pattern, &'a A::NotNan)) -> B;
287
288    /// Visit each non-NaN element in the array by calling `f` on each element.
289    ///
290    /// Elements are visited in arbitrary order.
291    fn visit_skipnan<'a, F>(&'a self, f: F)
292    where
293        A: 'a,
294        F: FnMut(&'a A::NotNan);
295
296    /// Fold non-NaN values along an axis.
297    ///
298    /// Combine the non-NaN elements of each subview with the previous using
299    /// the fold function and initial value init.
300    fn fold_axis_skipnan<B, F>(&self, axis: Axis, init: B, fold: F) -> Array<B, D::Smaller>
301    where
302        D: RemoveAxis,
303        F: FnMut(&B, &A::NotNan) -> B,
304        B: Clone;
305
306    /// Reduce the values along an axis into just one value, producing a new
307    /// array with one less dimension.
308    ///
309    /// The NaN values are removed from the 1-dimensional lanes, then they are
310    /// passed as mutable views to the reducer, allowing for side-effects.
311    ///
312    /// **Warnings**:
313    ///
314    /// * The lanes are visited in arbitrary order.
315    ///
316    /// * The order of the elements within the lanes is unspecified. However,
317    ///   if `mapping` is idempotent, this method is idempotent. Additionally,
318    ///   given the same input data, the lane is always ordered the same way.
319    ///
320    /// **Panics** if `axis` is out of bounds.
321    fn map_axis_skipnan_mut<'a, B, F>(&'a mut self, axis: Axis, mapping: F) -> Array<B, D::Smaller>
322    where
323        A: 'a,
324        S: DataMut,
325        D: RemoveAxis,
326        F: FnMut(ArrayViewMut1<'a, A::NotNan>) -> B;
327
328    private_decl! {}
329}
330
331impl<A, S, D> MaybeNanExt<A, S, D> for ArrayBase<S, D>
332where
333    A: MaybeNan,
334    S: Data<Elem = A>,
335    D: Dimension,
336{
337    fn fold_skipnan<'a, F, B>(&'a self, init: B, mut f: F) -> B
338    where
339        A: 'a,
340        F: FnMut(B, &'a A::NotNan) -> B,
341    {
342        self.fold(init, |acc, elem| {
343            if let Some(not_nan) = elem.try_as_not_nan() {
344                f(acc, not_nan)
345            } else {
346                acc
347            }
348        })
349    }
350
351    fn indexed_fold_skipnan<'a, F, B>(&'a self, init: B, mut f: F) -> B
352    where
353        A: 'a,
354        F: FnMut(B, (D::Pattern, &'a A::NotNan)) -> B,
355    {
356        self.indexed_iter().fold(init, |acc, (idx, elem)| {
357            if let Some(not_nan) = elem.try_as_not_nan() {
358                f(acc, (idx, not_nan))
359            } else {
360                acc
361            }
362        })
363    }
364
365    fn visit_skipnan<'a, F>(&'a self, mut f: F)
366    where
367        A: 'a,
368        F: FnMut(&'a A::NotNan),
369    {
370        self.for_each(|elem| {
371            if let Some(not_nan) = elem.try_as_not_nan() {
372                f(not_nan)
373            }
374        })
375    }
376
377    fn fold_axis_skipnan<B, F>(&self, axis: Axis, init: B, mut fold: F) -> Array<B, D::Smaller>
378    where
379        D: RemoveAxis,
380        F: FnMut(&B, &A::NotNan) -> B,
381        B: Clone,
382    {
383        self.fold_axis(axis, init, |acc, elem| {
384            if let Some(not_nan) = elem.try_as_not_nan() {
385                fold(acc, not_nan)
386            } else {
387                acc.clone()
388            }
389        })
390    }
391
392    fn map_axis_skipnan_mut<'a, B, F>(
393        &'a mut self,
394        axis: Axis,
395        mut mapping: F,
396    ) -> Array<B, D::Smaller>
397    where
398        A: 'a,
399        S: DataMut,
400        D: RemoveAxis,
401        F: FnMut(ArrayViewMut1<'a, A::NotNan>) -> B,
402    {
403        self.map_axis_mut(axis, |lane| mapping(A::remove_nan_mut(lane)))
404    }
405
406    private_impl! {}
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412    use quickcheck_macros::quickcheck;
413
414    #[quickcheck]
415    fn remove_nan_mut_idempotent(is_nan: Vec<bool>) -> bool {
416        let mut values: Vec<_> = is_nan
417            .into_iter()
418            .map(|is_nan| if is_nan { None } else { Some(1) })
419            .collect();
420        let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
421        let removed = remove_nan_mut(view);
422        removed == remove_nan_mut(removed.to_owned().view_mut())
423    }
424
425    #[quickcheck]
426    fn remove_nan_mut_only_nan_remaining(is_nan: Vec<bool>) -> bool {
427        let mut values: Vec<_> = is_nan
428            .into_iter()
429            .map(|is_nan| if is_nan { None } else { Some(1) })
430            .collect();
431        let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
432        remove_nan_mut(view).iter().all(|elem| !elem.is_nan())
433    }
434
435    #[quickcheck]
436    fn remove_nan_mut_keep_all_non_nan(is_nan: Vec<bool>) -> bool {
437        let non_nan_count = is_nan.iter().filter(|&&is_nan| !is_nan).count();
438        let mut values: Vec<_> = is_nan
439            .into_iter()
440            .map(|is_nan| if is_nan { None } else { Some(1) })
441            .collect();
442        let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
443        remove_nan_mut(view).len() == non_nan_count
444    }
445}
446
447mod impl_not_none;