tsxlib 0.1.3

timeseries library
Documentation

#![allow(unused_macros)]
macro_rules! gen_timings { 
    ($test_case:expr, $closure:expr, $niter:expr) => {
        {
            let niter = $niter;
            let mut _durvec = Vec::with_capacity(niter);
            for _ in 0..niter{
                let before = std::time::Instant::now();
                $closure();
                let timing = before.elapsed();
                _durvec.push(timing)
            }
            let _total = _durvec.iter().fold(std::time::Duration::from_nanos(0), |x,y| x + *y );
            let _avg = std::time::Duration::from_secs_f64( _total.as_secs_f64()/(niter as f64));
            let _min = _durvec.iter().min().unwrap();
            let _max = _durvec.iter().max().unwrap();
            eprintln!("{}: total: {:.2?}, avg: {:.2?}, min: {:.2?}, max: {:.2?}", $test_case,_total,_avg,_min,_max);
        }
    }
}


#[cfg(feature = "parq")]
fn runnable_with_parq_enabled(){

    use serde::Serialize;
    use chrono::{NaiveDateTime,Duration};
    use parquet::record::RowAccessor;
    use tsxlib::timeseries::{TimeSeries};
    use tsxlib::data_elements::TimeSeriesDataPoint;
    use tsxlib::timeseries_iterators::{FromUncheckedIterator};
    use tsxlib::timeutils;


    fn datapoint_gen_func(row: &parquet::record::Row) -> TimeSeriesDataPoint<NaiveDateTime,f64> {
        let value = row.get_double(1).unwrap();
        let istamp = row.get_timestamp_millis(0).unwrap() as i64;
        let ndt = timeutils::naive_datetime_from_millis(istamp);
        TimeSeriesDataPoint::new(ndt,value)
    };

    
    gen_timings!("Read Parquet Test", Box::new(|| {tsxlib::io::parquet::read_from_file::<NaiveDateTime, f64>("../../../testdata/rand_data.parquet",datapoint_gen_func).unwrap();}),10);

    let ts: TimeSeries<NaiveDateTime,f64>  = tsxlib::io::parquet::read_from_file::<NaiveDateTime, f64>("../../../testdata/rand_data.parquet",datapoint_gen_func).unwrap();

    
    let tsrres: TimeSeries<NaiveDateTime,f64> = ts.into_iter().map(|x| TimeSeriesDataPoint::new(x.timestamp,x.value * 2.0)).collect_from_unchecked_iter();

    gen_timings!("Map float via value iterator", Box::new(|| {ts.into_iter().map(|x| TimeSeriesDataPoint::new(x.timestamp,x.value * 2.0)).collect_from_unchecked_iter();}),100);
    gen_timings!("Map float via ref iterator", Box::new(|| {ts.iter().map(|x| TimeSeriesDataPoint::new(x.timestamp, x.value * 2.0)).collect_from_unchecked_iter();}),100);
    gen_timings!("Map float via native method", Box::new(|| {ts.map(|x| x * 2.0);}),100);

    gen_timings!("Lag by 1", Box::new(|| {ts.shift(-1).collect_from_unchecked_iter();}),100);

    gen_timings!("Cross Apply (inner join)", Box::new(|| {tsrres.cross_apply_inner(&ts,|x,y| x + y);}),100);
    let lagged: TimeSeries<NaiveDateTime,f64> =  ts.shift(-1).collect_from_unchecked_iter();

    gen_timings!("Cross Apply (inner join) Different Lengths", Box::new(|| {tsrres.cross_apply_inner(&lagged,|x,y| x + y);}),100);

    gen_timings!("Cross Apply (left join) Different Lengths", Box::new(|| {
        tsrres.cross_apply_left(&lagged,|x,y| match y.is_some(){
            true => y.unwrap() + x,
            false => *x
        });
    }),100);

    gen_timings!("Bar Data round up", Box::new(|| {ts.resample_and_agg(Duration::minutes(15), |dt,dur| timeutils::round_up_to_nearest_duration(dt, dur), |x| *x.last().unwrap().value);}),100);
    gen_timings!("Bar Data round down", Box::new(|| {ts.resample_and_agg(Duration::minutes(15), |dt,dur| timeutils::round_down_to_nearest_duration(dt, dur), |x| *x.last().unwrap().value);}),100);
    
    #[derive(Clone,Copy,Serialize,Default)]
    struct SimpleStruct{
        pub timestamp: i64,
        pub floatvalueother: f64,
        pub floatvalue: f64
    };
    #[derive(Clone,Copy,Serialize,Default)]
    struct OtherStruct{
        pub timestamp: i64,
        pub ratio: f64
    };

    let newbase: TimeSeries<NaiveDateTime,SimpleStruct>  = ts.map_with_date(|dt,x| SimpleStruct{timestamp:dt.timestamp_millis(),floatvalue:x*2.0,floatvalueother:*x} );
    fn complicated(x: &SimpleStruct) -> OtherStruct{
        if x.timestamp & 1 == 0 {
            OtherStruct {timestamp:x.timestamp,ratio:x.floatvalue}
        }
        else{
            OtherStruct {timestamp:x.timestamp,ratio:x.floatvalue/x.floatvalueother}
        }

    }

    gen_timings!("Map struct via iterator", Box::new(|| {newbase.iter().map(|x| TimeSeriesDataPoint::new(x.timestamp,complicated(&x.value))).collect_from_unchecked_iter();}),100);
    gen_timings!("Map struct via native method", Box::new(|| {newbase.map(complicated);}),100);

}

#[cfg(not(feature = "parq"))]
fn no_parq(){ 
    println!("you need to build with --features \"parq\" to enable this")
}

fn main() {
    #[cfg(feature = "parq")]
    runnable_with_parq_enabled();
    #[cfg(not(feature = "parq"))]
    no_parq();
}