use crate::error::Error;
#[cfg(feature = "machine_learning")]
use crate::machine_learning::{
DBSCAN, DecisionTree, IsolationForest, KMeans, KNN, KernelPCA, LDA, LinearRegression,
LinearSVC, LogisticRegression, MeanShift, PCA, SVC, TSNE,
};
#[cfg(feature = "utils")]
use crate::utils::{MaxAbsScaler, MinMaxScaler, Normalizer, RobustScaler, StandardScaler};
#[cfg(feature = "machine_learning")]
use ndarray::{Array1, Ix1};
use ndarray::{Array2, ArrayBase, Data, Ix2};
#[cfg(feature = "machine_learning")]
use std::hash::Hash;
pub trait Fit<D> {
fn fit(&mut self, data: D) -> Result<&mut Self, Error>;
}
pub trait Predict<X> {
type Output;
fn predict(&self, input: X) -> Result<Self::Output, Error>;
}
pub trait Transform<X> {
type Output;
fn transform(&self, input: X) -> Result<Self::Output, Error>;
}
pub trait FitTransform<D> {
type Output;
fn fit_transform(&mut self, data: D) -> Result<Self::Output, Error>;
}
#[cfg(feature = "machine_learning")]
mod machine_learning_impls {
use super::*;
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for LinearRegression
where
S1: Data<Elem = f64>,
S2: Data<Elem = f64>,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for LogisticRegression
where
S1: Data<Elem = f64>,
S2: Data<Elem = f64>,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for DecisionTree
where
S1: Data<Elem = f64> + Send + Sync,
S2: Data<Elem = f64> + Send + Sync,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for LinearSVC
where
S1: Data<Elem = f64> + Send + Sync,
S2: Data<Elem = f64> + Send + Sync,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for SVC
where
S1: Data<Elem = f64> + Send + Sync,
S2: Data<Elem = f64> + Send + Sync,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, T, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for KNN<T>
where
T: Clone + Hash + Eq,
S1: Data<Elem = f64>,
S2: Data<Elem = T>,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S1, S2> Fit<(&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>)> for LDA
where
S1: Data<Elem = f64>,
S2: Data<Elem = i32>,
{
fn fit(
&mut self,
data: (&'a ArrayBase<S1, Ix2>, &'a ArrayBase<S2, Ix1>),
) -> Result<&mut Self, Error> {
let (x, y) = data;
self.fit(x, y)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for KMeans
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for DBSCAN
where
S: Data<Elem = f64> + Send + Sync,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for MeanShift
where
S: Data<Elem = f64> + Send + Sync,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for IsolationForest
where
S: Data<Elem = f64> + Send + Sync,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for PCA
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for KernelPCA
where
S: Data<Elem = f64> + Send + Sync,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for LinearRegression
where
S: Data<Elem = f64>,
{
type Output = Array1<f64>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for LogisticRegression
where
S: Data<Elem = f64>,
{
type Output = Array1<f64>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for DecisionTree
where
S: Data<Elem = f64>,
{
type Output = Array1<f64>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for LinearSVC
where
S: Data<Elem = f64>,
{
type Output = Array1<f64>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for SVC
where
S: Data<Elem = f64> + Send + Sync,
{
type Output = Array1<f64>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for KMeans
where
S: Data<Elem = f64>,
{
type Output = Array1<isize>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for MeanShift
where
S: Data<Elem = f64> + Sync,
{
type Output = Array1<isize>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for IsolationForest
where
S: Data<Elem = f64>,
{
type Output = Array1<i32>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, T, S> Predict<&'a ArrayBase<S, Ix2>> for KNN<T>
where
T: Clone + Hash + Eq,
S: Data<Elem = f64>,
{
type Output = Array1<T>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for LDA
where
S: Data<Elem = f64>,
{
type Output = Array1<i32>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Predict<&'a ArrayBase<S, Ix2>> for DBSCAN
where
S: Data<Elem = f64> + Send + Sync,
{
type Output = Array1<isize>;
fn predict(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.predict(input)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for PCA
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for KernelPCA
where
S: Data<Elem = f64> + Send + Sync,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for PCA
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for KernelPCA
where
S: Data<Elem = f64> + Send + Sync,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for TSNE
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
let this: &TSNE = self;
this.fit_transform(data)
}
}
}
#[cfg(feature = "utils")]
mod utils_impls {
use super::*;
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for StandardScaler
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for StandardScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for StandardScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for MinMaxScaler
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for MinMaxScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for MinMaxScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for MaxAbsScaler
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for MaxAbsScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for MaxAbsScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for RobustScaler
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for RobustScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for RobustScaler
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
impl<'a, S> Fit<&'a ArrayBase<S, Ix2>> for Normalizer
where
S: Data<Elem = f64>,
{
fn fit(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<&mut Self, Error> {
self.fit(data)
}
}
impl<'a, S> Transform<&'a ArrayBase<S, Ix2>> for Normalizer
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn transform(&self, input: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.transform(input)
}
}
impl<'a, S> FitTransform<&'a ArrayBase<S, Ix2>> for Normalizer
where
S: Data<Elem = f64>,
{
type Output = Array2<f64>;
fn fit_transform(&mut self, data: &'a ArrayBase<S, Ix2>) -> Result<Self::Output, Error> {
self.fit_transform(data)
}
}
}
#[cfg(all(test, feature = "machine_learning"))]
mod tests {
use super::*;
use crate::machine_learning::linear_model::LeastSquaresSolver;
use ndarray::{Array1, Array2};
#[test]
fn fit_and_predict_through_traits() {
let x = Array2::from_shape_vec((3, 1), vec![1.0, 2.0, 3.0]).unwrap();
let y = Array1::from_vec(vec![2.0, 4.0, 6.0]);
let mut model = LinearRegression::new(true)
.with_solver(LeastSquaresSolver::GradientDescent {
learning_rate: 0.05,
max_iter: 5000,
tol: 1e-9,
})
.unwrap();
Fit::fit(&mut model, (&x, &y)).unwrap();
let preds = Predict::predict(&model, &x).unwrap();
assert_eq!(preds.len(), 3);
for (p, t) in preds.iter().zip(y.iter()) {
assert!((p - t).abs() < 0.5);
}
}
#[test]
fn transformers_through_traits() {
use crate::machine_learning::{KernelPCA, KernelType, PCA, TSNE, TSNEMethod};
let x = ndarray::array![[0.0, 1.0], [1.0, 0.0], [2.0, 2.0]];
let mut pca = PCA::new(2).unwrap();
Fit::fit(&mut pca, &x).unwrap();
let projected = Transform::transform(&pca, &x).unwrap();
assert_eq!(projected.ncols(), 2);
let mut pca2 = PCA::new(2).unwrap();
assert_eq!(
FitTransform::fit_transform(&mut pca2, &x).unwrap().ncols(),
2
);
let mut kpca = KernelPCA::new(KernelType::Linear, 2).unwrap();
assert_eq!(
FitTransform::fit_transform(&mut kpca, &x).unwrap().ncols(),
2
);
let mut tsne = TSNE::new(2, 2.0, 200.0, 50)
.unwrap()
.with_random_state(42)
.with_method(TSNEMethod::Exact)
.unwrap();
let embedding = FitTransform::fit_transform(&mut tsne, &x).unwrap();
assert_eq!(embedding.ncols(), 2);
}
}
#[cfg(all(test, feature = "utils"))]
mod utils_tests {
use super::*;
use crate::utils::StandardScaler;
#[test]
fn scaler_through_traits() {
let x_train = ndarray::array![[1.0, 100.0], [2.0, 150.0], [3.0, 200.0]];
let x_test = ndarray::array![[4.0, 250.0]];
let mut scaler = StandardScaler::new();
Fit::fit(&mut scaler, &x_train).unwrap();
let scaled = Transform::transform(&scaler, &x_test).unwrap();
assert_eq!(scaled.dim(), (1, 2));
assert!((scaled[[0, 0]] - 2.449_489_742_783_178).abs() < 1e-12);
let mut scaler2 = StandardScaler::new();
assert_eq!(
FitTransform::fit_transform(&mut scaler2, &x_train)
.unwrap()
.dim(),
(3, 2)
);
}
}