|
10 | 10 | use num_traits::Float;
|
11 | 11 | use num_traits::One;
|
12 | 12 | use num_traits::{FromPrimitive, Zero};
|
13 |
| -use std::ops::{Add, Div, Mul}; |
| 13 | +use std::ops::{Add, Div, Mul, Sub}; |
14 | 14 |
|
15 | 15 | use crate::imp_prelude::*;
|
16 | 16 | use crate::numeric_util;
|
| 17 | +use crate::Slice; |
17 | 18 |
|
18 | 19 | /// # Numerical Methods for Arrays
|
19 | 20 | impl<A, S, D> ArrayBase<S, D>
|
@@ -437,4 +438,59 @@ where
|
437 | 438 | {
|
438 | 439 | self.var_axis(axis, ddof).mapv_into(|x| x.sqrt())
|
439 | 440 | }
|
| 441 | + |
| 442 | + /// Calculates the (forward) finite differences of order `n`, along the `axis`. |
| 443 | + /// For the 1D-case, `n==1`, this means: `diff[i] == arr[i+1] - arr[i]` |
| 444 | + /// |
| 445 | + /// For `n>=2`, the process is iterated: |
| 446 | + /// ``` |
| 447 | + /// use ndarray::{array, Axis}; |
| 448 | + /// let arr = array![1.0, 2.0, 5.0]; |
| 449 | + /// assert_eq!(arr.diff(2, Axis(0)), arr.diff(1, Axis(0)).diff(1, Axis(0))) |
| 450 | + /// ``` |
| 451 | + /// **Panics** if `axis` is out of bounds |
| 452 | + /// |
| 453 | + /// **Panics** if `n` is too big / the array is to short: |
| 454 | + /// ```should_panic |
| 455 | + /// use ndarray::{array, Axis}; |
| 456 | + /// array![1.0, 2.0, 3.0].diff(10, Axis(0)); |
| 457 | + /// ``` |
| 458 | + pub fn diff(&self, n: usize, axis: Axis) -> Array<A, D> |
| 459 | + where A: Sub<A, Output = A> + Zero + Clone |
| 460 | + { |
| 461 | + if n == 0 { |
| 462 | + return self.to_owned(); |
| 463 | + } |
| 464 | + assert!(axis.0 < self.ndim(), "The array has only ndim {}, but `axis` {:?} is given.", self.ndim(), axis); |
| 465 | + assert!( |
| 466 | + n < self.shape()[axis.0], |
| 467 | + "The array must have length at least `n+1`=={} in the direction of `axis`. It has length {}", |
| 468 | + n + 1, |
| 469 | + self.shape()[axis.0] |
| 470 | + ); |
| 471 | + |
| 472 | + let mut inp = self.to_owned(); |
| 473 | + let mut out = Array::zeros({ |
| 474 | + let mut inp_dim = self.raw_dim(); |
| 475 | + // inp_dim[axis.0] >= 1 as per the 2nd assertion. |
| 476 | + inp_dim[axis.0] -= 1; |
| 477 | + inp_dim |
| 478 | + }); |
| 479 | + for _ in 0..n { |
| 480 | + let head = inp.slice_axis(axis, Slice::from(..-1)); |
| 481 | + let tail = inp.slice_axis(axis, Slice::from(1..)); |
| 482 | + |
| 483 | + azip!((o in &mut out, h in head, t in tail) *o = t.clone() - h.clone()); |
| 484 | + |
| 485 | + // feed the output as the input to the next iteration |
| 486 | + std::mem::swap(&mut inp, &mut out); |
| 487 | + |
| 488 | + // adjust the new output array width along `axis`. |
| 489 | + // Current situation: width of `inp`: k, `out`: k+1 |
| 490 | + // needed width: `inp`: k, `out`: k-1 |
| 491 | + // slice is possible, since k >= 1. |
| 492 | + out.slice_axis_inplace(axis, Slice::from(..-2)); |
| 493 | + } |
| 494 | + inp |
| 495 | + } |
440 | 496 | }
|
0 commit comments