Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ let bad_and_silly_prediction = fit.y(150.0); // This is outside the range of the
// Violating that is called extrapolation, which we generally want to avoid
// This will return an error!

let bad_prediction_probably = fit.as_polynomial().y(150.0); // This is outside the range of the data, but you asked for it specifically
let bad_prediction_probably = fit.as_polynomial().y(150.0); // This is outside the range of the data, but you asked for it specifically
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readme is a generated file (cargo rdme), please don't directly alter it - also this instance is already gone

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this with cargo rdme because the PR build failed originally without it.

// Unlike a CurveFit, a Polynomial is just a mathematical function - no seatbelts
// This will return a value, but it is probably nonsense

Expand Down
39 changes: 32 additions & 7 deletions src/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
//! - Z-Score Normalization: [`NormalizationTransform::ZScore`]
//! - Normalizes the dataset to zero mean and unit variance.
//! - [`ApplyNormalization::apply_z_score_normalization`] allows you to apply it to the Y channel of an (X, Y) dataset
use std::borrow::BorrowMut;

use crate::value::Value;

mod noise;
Expand All @@ -87,24 +89,35 @@ pub trait Transform<T: Value> {
}
}

/// Allow passing an `&impl Transform` to anything that wants `impl Transform`.
///
/// This way if you don't need to re-use a transform, you can pass it directly,
/// but you can still pass large ones by reference where that's helpful.
impl<T: Value, R: ?Sized + Transform<T>> Transform<T> for &R {
fn apply<'a>(&self, data: impl Iterator<Item = &'a mut T>) {
<R as Transform<T>>::apply(*self, data);
}
}

/// Trait for transforming data.
pub trait Transformable<T: Value> {
/// Transforms the data in place.
fn transform<R: Transform<T>>(&mut self, transform: &R);
fn transform<R: Transform<T>>(&mut self, transform: R);

/// Returns a transformed copy of the data.
#[must_use]
fn transformed<R: Transform<T>>(&self, transform: &R) -> Self
fn transformed<R: Transform<T>>(&self, transform: R) -> Self::Owned
where
Self: Sized + Clone,
Self: ToOwned,
Self::Owned: BorrowMut<Self>,
{
let mut new_data = self.clone();
new_data.transform(transform);
let mut new_data = self.to_owned();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

annot: note specifically here that it's still making a copy of it https://doc.rust-lang.org/std/borrow/trait.ToOwned.html

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok fair point - I concede that the ToOwned version is more general.

However I still want to keep the &R here
implementing Transform for &Transform makes it compatible but imho encourages stateful or single use transforms, which I don't love

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me the thing that enforces that is that https://docs.rs/polyfit/latest/polyfit/transforms/trait.Transform.html#tymethod.apply takes &self, which I do not propose to change. You'd still have to use Cell or similar to make a stateful transform as a result.

I just got sad having to do blah.transform(&NormalizationTransform::MeanSubtraction); when the & didn't really seem to help anything at all, vs being able to just blah.transform(NormalizationTransform::MeanSubtraction);.

new_data.borrow_mut().transform(transform);
new_data
}
}
impl<T: Value> Transformable<T> for Vec<(T, T)> {
fn transform<R: Transform<T>>(&mut self, transform: &R) {
impl<T: Value> Transformable<T> for [(T, T)] {
fn transform<R: Transform<T>>(&mut self, transform: R) {
transform.apply(self.iter_mut().map(|(_, y)| y));
}
}
Expand Down Expand Up @@ -228,3 +241,15 @@ impl SeedSource {
Some(out)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_transformed_slice_gives_vec() {
let points = [(-0.5, -4.0), (1.0, 3.0), (5.0, 4.0)];
let new_points: Vec<_> = points[1..].transformed(ScaleTransform::Quadratic(0.5));
assert_eq!(new_points, [(1.0, 4.5), (5.0, 8.0)]);
}
}
Loading