From 0ca1b0374536f78c7e9650d7b7b7c38d28ab9ab5 Mon Sep 17 00:00:00 2001 From: Ben Sully Date: Thu, 12 Dec 2024 10:24:51 +0000 Subject: [PATCH] perf: precalculate offset and scale factor for min-max scale transformer (#196) --- crates/augurs-forecaster/src/transforms.rs | 64 +++++++++++----------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/crates/augurs-forecaster/src/transforms.rs b/crates/augurs-forecaster/src/transforms.rs index 01252f6..7268a9b 100644 --- a/crates/augurs-forecaster/src/transforms.rs +++ b/crates/augurs-forecaster/src/transforms.rs @@ -172,7 +172,7 @@ impl Transform { { match self { Self::LinearInterpolator => Box::new(input.interpolate(LinearInterpolator::default())), - Self::MinMaxScaler(params) => Box::new(input.min_max_scale(params.clone())), + Self::MinMaxScaler(params) => Box::new(input.min_max_scale(params)), Self::Logit => Box::new(input.logit()), Self::Log => Box::new(input.log()), Self::BoxCox { lambda } => Box::new(input.box_cox(*lambda)), @@ -201,7 +201,7 @@ impl Transform { { match self { Self::LinearInterpolator => Box::new(input), - Self::MinMaxScaler(params) => Box::new(input.inverse_min_max_scale(params.clone())), + Self::MinMaxScaler(params) => Box::new(input.inverse_min_max_scale(params)), Self::Logit => Box::new(input.logistic()), Self::Log => Box::new(input.exp()), Self::BoxCox { lambda } => Box::new(input.inverse_box_cox(*lambda)), @@ -228,7 +228,7 @@ impl Transform { // Actual implementations of the transforms. // These may be moved to a separate module or crate in the future. -/// A transformer that scales each item to a certain range. +/// Parameters for the min-max scaler. /// /// The target range is [0, 1] by default. Use [`MinMaxScaleParams::with_scaled_range`] /// to set a custom range. @@ -276,7 +276,8 @@ impl MinMaxScaleParams { #[derive(Debug, Clone)] struct MinMaxScale { inner: T, - params: MinMaxScaleParams, + scale_factor: f64, + offset: f64, } impl Iterator for MinMaxScale @@ -286,29 +287,27 @@ where type Item = f64; fn next(&mut self) -> Option { let Self { - params: - MinMaxScaleParams { - data_min, - data_max, - scaled_min, - scaled_max, - }, + scale_factor, + offset, + inner, .. } = self; - self.inner.next().map(|x| { - *scaled_min + ((x - *data_min) * (*scaled_max - *scaled_min)) / (*data_max - *data_min) - }) + inner.next().map(|x| *offset + (x * *scale_factor)) } } trait MinMaxScaleExt: Iterator { - fn min_max_scale(self, params: MinMaxScaleParams) -> MinMaxScale + fn min_max_scale(self, params: &MinMaxScaleParams) -> MinMaxScale where Self: Sized, { + let scale_factor = + (params.scaled_max - params.scaled_min) / (params.data_max - params.data_min); + let offset = params.scaled_min - (params.data_min * scale_factor); MinMaxScale { inner: self, - params, + scale_factor, + offset, } } } @@ -317,7 +316,8 @@ impl MinMaxScaleExt for T where T: Iterator {} struct InverseMinMaxScale { inner: T, - params: MinMaxScaleParams, + scale_factor: f64, + offset: f64, } impl Iterator for InverseMinMaxScale @@ -327,29 +327,27 @@ where type Item = f64; fn next(&mut self) -> Option { let Self { - params: - MinMaxScaleParams { - data_min, - data_max, - scaled_min, - scaled_max, - }, + inner, + scale_factor, + offset, .. } = self; - self.inner.next().map(|x| { - *data_min + ((x - *scaled_min) * (*data_max - *data_min)) / (*scaled_max - *scaled_min) - }) + inner.next().map(|x| *offset + (x * *scale_factor)) } } trait InverseMinMaxScaleExt: Iterator { - fn inverse_min_max_scale(self, params: MinMaxScaleParams) -> InverseMinMaxScale + fn inverse_min_max_scale(self, params: &MinMaxScaleParams) -> InverseMinMaxScale where Self: Sized, { + let scale_factor = + (params.data_max - params.data_min) / (params.scaled_max - params.scaled_min); + let offset = params.data_min - (params.scaled_min * scale_factor); InverseMinMaxScale { inner: self, - params, + scale_factor, + offset, } } } @@ -751,7 +749,7 @@ mod test { let expected = vec![0.0, 0.5, 1.0]; let actual: Vec<_> = data .into_iter() - .min_max_scale(MinMaxScaleParams::new(min, max)) + .min_max_scale(&MinMaxScaleParams::new(min, max)) .collect(); assert_all_close(&expected, &actual); } @@ -764,7 +762,7 @@ mod test { let expected = vec![0.0, 5.0, 10.0]; let actual: Vec<_> = data .into_iter() - .min_max_scale(MinMaxScaleParams::new(min, max).with_scaled_range(0.0, 10.0)) + .min_max_scale(&MinMaxScaleParams::new(min, max).with_scaled_range(0.0, 10.0)) .collect(); assert_all_close(&expected, &actual); } @@ -777,7 +775,7 @@ mod test { let expected = vec![1.0, 2.0, 3.0]; let actual: Vec<_> = data .into_iter() - .inverse_min_max_scale(MinMaxScaleParams::new(min, max)) + .inverse_min_max_scale(&MinMaxScaleParams::new(min, max)) .collect(); assert_all_close(&expected, &actual); } @@ -790,7 +788,7 @@ mod test { let expected = vec![1.0, 2.0, 3.0]; let actual: Vec<_> = data .into_iter() - .inverse_min_max_scale(MinMaxScaleParams::new(min, max).with_scaled_range(0.0, 10.0)) + .inverse_min_max_scale(&MinMaxScaleParams::new(min, max).with_scaled_range(0.0, 10.0)) .collect(); assert_all_close(&expected, &actual); }