TD7KX2PIGP2TCZ2Q7XXNBBDJIQ2KXEIFWLBMWXTJE3OEXVV4L7MAC YMV7RPQ5TFBETNHRMS26MGHJXEAYRMIF4F7Z6ITGRCG65TOGSNDAC P6XNTWNXKWQ7APCAHAP635VIWCOX5NH5P4DS3C4APOE6OKJGEJOQC QYLGEDIVYSUHAYU7ZLUZDA6OULFDDZYTQN264V3473MEXLFZ4U3AC IYW574EKVRH2QJ7GFNX4FMCNI7EMLNYYIC6NGHVIJVDEWSDL42GQC XIWTRGR6SRVSX3TA6YZVMZIETMZNLJBQSJ3BAL3O6ZXURJPTHQZAC XCCNAGMZBU6N3YNH3ILES46HV6HBSSCSO52CK5ZQNYIYNSDCHXIAC /// A duration between two `MonthlyDates`. The value can be positive or negative.#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub struct Duration(isize);impl Duration {fn months(n: isize) -> Self {Duration(n)}fn quarters(n: isize) -> Self {Duration(3 * n)}fn year(n: isize) -> Self {Duration(12 * n)}/// Return the duration between two dates.fn between(min: MonthlyDate, max: MonthlyDate) -> Self {Duration(max.as_isize() - min.as_isize())}fn is_not_positive(&self) -> bool {self.0 <= 0}}
/// Return true if the durations between Points are all equal.pub fn is_regular(&self, duration: &MonthlyDate) -> bool {self.0.as_slice().windows(2).all(|datapoint_pair| {datapoint_pair[1].date() - datapoint_pair[0].date() == *duration})}
// /// Return true if the durations between Points are all equal.// pub fn is_regular(&self, duration: &MonthlyDate) -> bool {// if self.len < 2 {// return false// } else {// self.0.as_slice().windows(2).all(|datapoint_pair| {// datapoint_pair[1].date() - datapoint_pair[0].date() == *duration// })// }// }
Ok((self.0)[1].date() - (self.0)[0].date())
let first_date = self.0[0].date();let second_date = self.0[1].date();let duration = Duration::between(second_date, first_date);if duration.is_not_positive() {return Err(expected_positive_duration(&format!("{}-{}-01", first_date.year(), first_date.month()),&format!("{}-{}-01", second_date.year(), second_date.month()),))};Ok(duration)
/// A `RegularTimeSeries` has an additional requirements over `TimeSeries` in/// the time interval between successive `DatePoints` has the same `Duration`./// This also ensures ordering.
/// A `RegularTimeSeries` has an additional requirements over `TimeSeries` in the time interval/// between successive `DatePoints` has the same `Duration`. This also ensures ordering. A/// `RegularTimeSeries` is guaranteed to have two or more data points.
/// Return the datapoint for the given date or error if that date is not in `Self`.pub fn datepoint_from_date(&self, date: MonthlyDate) -> Result<DatePoint::<N>, Error> {if date < self.first_date() || date > self.last_date(){return Err(date_not_in_timeseries(&format!("{}-{:02}-01",date.year(),date.month())))};let months_delta = date.as_isize() - self.first_date().as_isize();if months_delta % self.duration.0 != 0 {return Err(date_not_in_timeseries(&format!("{}-{:02}-01",date.year(),date.month())))};let index = date.as_isize() - self.first_date().as_isize() / self.duration.0;
/// Transform a `RegularTimeSeries` into quarterly data.pub fn to_quarterly(&self, n: usize) -> RegularTimeSeries<1> {let x = self.ts.0.iter().map(|dp| dp.date().as_isize() as f64).collect::<Vec<f64>>();let y = self.ts.0.iter().map(|dp| dp.value(n) as f64).collect::<Vec<f64>>();
let spline = CubicSpline::from_nodes(x, y);let (add_year, month) = match self.first_date().month() {1 => (0, 1),2 | 3 | 4 => (0, 4),5 | 6 | 7 => (0, 7),8 | 9 | 10 => (0, 10),11 | 12 => (1, 1),_ => panic!(),};let mut date = MonthlyDate::ym(self.first_date().year() + add_year, month);let mut v = Vec::new();while date <= self.last_date() {let dp = DatePoint::<1>::new(date, [spline.eval(date.as_isize() as f64) as f32]);v.push(dp);date = MonthlyDate(date.0 + 3);};TimeSeries::new(v).try_into().unwrap()}/// Transform a `RegularTimeSeries` into year-on-year percentage change over the previous year.pub fn to_year_on_year(&self, n: usize) -> Result<RegularTimeSeries<1>, Error> {let mut v = Vec::new();let mut date = self.first_date();while let Ok(dp2) = self.datepoint_from_date(date + Duration::year(1)) {let dp1 = self.datepoint_from_date(date).unwrap();let yoy = (dp2.value(0) - dp1.value(n)) * 100.0 / dp1.value(n);let dp = DatePoint::<1>::new(date + Duration::year(1), [yoy]);v.push(dp);date = date + self.duration;}TimeSeries::new(v).try_into()}
pub fn new(start_date: Option<MonthlyDate>, end_date: Option<MonthlyDate>) -> Self {DateRange { start_date, end_date }
/// Return a new `DateRange`.pub fn new(start_date: &Option<MonthlyDate>, end_date: &Option<MonthlyDate>) -> Self {DateRange {start_date: start_date.clone(),end_date: end_date.clone()}
match ts.is_regular(&duration) {true => {Ok(RegularTimeSeries::<N> {duration: duration.clone(),ts: ts,})},false => Err(expected_regular_time_series()),}
Ok(RegularTimeSeries::<N> { duration, ts })