use ndarray::{Array1, Array2};
use crate::error::Result;
use crate::float::Float;
pub trait Fit<F: Float> {
type Fitted;
fn fit(&self, x: &Array2<F>, y: &Array1<F>) -> Result<Self::Fitted>;
}
pub trait FitUnsupervised<F: Float> {
type Fitted;
fn fit(&self, x: &Array2<F>) -> Result<Self::Fitted>;
}
pub trait Predict<F: Float> {
fn predict(&self, x: &Array2<F>) -> Result<Array1<F>>;
}
pub trait Transform<F: Float> {
fn transform(&self, x: &Array2<F>) -> Result<Array2<F>>;
}
pub trait InverseTransform<F: Float> {
fn inverse_transform(&self, x: &Array2<F>) -> Result<Array2<F>>;
}
pub trait PredictProba<F: Float> {
fn predict_proba(&self, x: &Array2<F>) -> Result<Array2<F>>;
}
pub trait FitWeighted<F: Float> {
type Fitted;
fn fit_weighted(
&self,
x: &Array2<F>,
y: &Array1<F>,
sample_weight: Option<&Array1<F>>,
) -> Result<Self::Fitted>;
}
pub trait PartialFit<F: Float> {
type Fitted;
fn partial_fit(
&self,
state: Option<Self::Fitted>,
x: &Array2<F>,
y: &Array1<F>,
classes: Option<&[F]>,
) -> Result<Self::Fitted>;
}
pub trait FitUnsupervisedWeighted<F: Float> {
type Fitted;
fn fit_unsupervised_weighted(
&self,
x: &Array2<F>,
sample_weight: Option<&Array1<F>>,
) -> Result<Self::Fitted>;
}
pub trait PredictLogProba<F: Float>: PredictProba<F> {
fn predict_log_proba(&self, x: &Array2<F>) -> Result<Array2<F>> {
let p = self.predict_proba(x)?;
let eps = F::from_f64(1e-300).unwrap();
Ok(p.mapv(|v| if v < eps { eps.ln() } else { v.ln() }))
}
}
pub trait DecisionFunction<F: Float> {
fn decision_function(&self, x: &Array2<F>) -> Result<Array2<F>>;
}
pub trait RegressorScore<F: Float>: Predict<F> {
fn score(&self, x: &Array2<F>, y: &Array1<F>) -> Result<F> {
let pred = self.predict(x)?;
let n = y.len();
if n != pred.len() {
return Err(crate::error::RustMlError::ShapeMismatch(format!(
"y len {} != pred len {}",
n,
pred.len()
)));
}
let y_mean = y.iter().fold(F::zero(), |acc, &v| acc + v) / F::from_usize(n).unwrap();
let mut rss = F::zero();
let mut tss = F::zero();
for (a, b) in y.iter().zip(pred.iter()) {
let r = *a - *b;
rss = rss + r * r;
let t = *a - y_mean;
tss = tss + t * t;
}
let tss_safe = if tss == F::zero() {
F::from_f64(1e-12).unwrap()
} else {
tss
};
Ok(F::one() - rss / tss_safe)
}
}
pub trait ClassifierScore<F: Float>: Predict<F> {
fn score(&self, x: &Array2<F>, y: &Array1<F>) -> Result<F> {
let pred = self.predict(x)?;
let n = y.len();
if n != pred.len() {
return Err(crate::error::RustMlError::ShapeMismatch(format!(
"y len {} != pred len {}",
n,
pred.len()
)));
}
let eps = F::from_f64(1e-9).unwrap();
let correct = y
.iter()
.zip(pred.iter())
.filter(|(a, b)| (**a - **b).abs() < eps)
.count();
Ok(F::from_usize(correct).unwrap() / F::from_usize(n).unwrap())
}
}