1pub mod ard;
44pub mod bayesian_ridge;
45pub mod elastic_net;
46pub mod elastic_net_cv;
47pub mod glm;
48pub mod huber_regressor;
49pub mod isotonic;
50pub mod lars;
51pub mod lasso;
52pub mod lasso_cv;
53pub mod lda;
54mod linalg;
55pub mod linear_regression;
56pub mod linear_svc;
57pub mod linear_svr;
58pub mod logistic_regression;
59pub mod logistic_regression_cv;
60pub mod nu_svm;
61pub mod omp;
62pub mod one_class_svm;
63mod optim;
64pub mod qda;
65pub mod quantile_regressor;
66pub mod ransac;
67pub mod ridge;
68pub mod ridge_classifier;
69pub mod ridge_cv;
70pub mod sgd;
71pub mod svm;
72
73pub use ard::{ARDRegression, FittedARDRegression};
75pub use bayesian_ridge::{BayesianRidge, FittedBayesianRidge};
76pub use elastic_net::{ElasticNet, FittedElasticNet};
77pub use elastic_net_cv::{ElasticNetCV, FittedElasticNetCV};
78pub use glm::{
79 FittedGLMRegressor, GLMFamily, GLMRegressor, GammaRegressor, PoissonRegressor,
80 TweedieRegressor,
81};
82pub use huber_regressor::{FittedHuberRegressor, HuberRegressor};
83pub use isotonic::{FittedIsotonicRegression, IsotonicRegression};
84pub use lars::{FittedLars, FittedLassoLars, Lars, LassoLars};
85pub use lasso::{FittedLasso, Lasso};
86pub use lasso_cv::{FittedLassoCV, LassoCV};
87pub use lda::{FittedLDA, LDA};
88pub use linear_regression::{FittedLinearRegression, LinearRegression};
89pub use linear_svc::{FittedLinearSVC, LinearSVC, LinearSVCLoss};
90pub use linear_svr::{FittedLinearSVR, LinearSVR, LinearSVRLoss};
91pub use logistic_regression::{FittedLogisticRegression, LogisticRegression};
92pub use logistic_regression_cv::{FittedLogisticRegressionCV, LogisticRegressionCV};
93pub use nu_svm::{FittedNuSVC, FittedNuSVR, NuSVC, NuSVR};
94pub use omp::{FittedOMP, OrthogonalMatchingPursuit};
95pub use one_class_svm::{FittedOneClassSVM, OneClassSVM};
96pub use qda::{FittedQDA, QDA};
97pub use quantile_regressor::{FittedQuantileRegressor, QuantileRegressor};
98pub use ransac::{FittedRANSACRegressor, RANSACRegressor};
99pub use ridge::{FittedRidge, Ridge};
100pub use ridge_classifier::{FittedRidgeClassifier, RidgeClassifier};
101pub use ridge_cv::{FittedRidgeCV, RidgeCV};
102pub use sgd::{FittedSGDClassifier, FittedSGDRegressor, SGDClassifier, SGDRegressor};
103pub use svm::{
104 FittedSVC, FittedSVR, Kernel, LinearKernel, PolynomialKernel, RbfKernel, SVC, SVR,
105 SigmoidKernel,
106};
107
108use ferrolearn_core::error::FerroError;
109use ferrolearn_core::traits::Predict;
110use ndarray::{Array1, Array2};
111use num_traits::Float;
112
113pub trait ClassifierScore<F: Float> {
120 fn score(&self, x: &Array2<F>, y: &Array1<usize>) -> Result<F, FerroError>;
127}
128
129impl<T, F> ClassifierScore<F> for T
130where
131 T: Predict<Array2<F>, Output = Array1<usize>, Error = FerroError>,
132 F: Float,
133{
134 fn score(&self, x: &Array2<F>, y: &Array1<usize>) -> Result<F, FerroError> {
135 if x.nrows() != y.len() {
136 return Err(FerroError::ShapeMismatch {
137 expected: vec![x.nrows()],
138 actual: vec![y.len()],
139 context: "y length must match number of samples in X".into(),
140 });
141 }
142 let preds = self.predict(x)?;
143 Ok(mean_accuracy(&preds, y))
144 }
145}
146
147pub trait RegressorScore<F: Float> {
153 fn score(&self, x: &Array2<F>, y: &Array1<F>) -> Result<F, FerroError>;
160}
161
162impl<T, F> RegressorScore<F> for T
163where
164 T: Predict<Array2<F>, Output = Array1<F>, Error = FerroError>,
165 F: Float,
166{
167 fn score(&self, x: &Array2<F>, y: &Array1<F>) -> Result<F, FerroError> {
168 if x.nrows() != y.len() {
169 return Err(FerroError::ShapeMismatch {
170 expected: vec![x.nrows()],
171 actual: vec![y.len()],
172 context: "y length must match number of samples in X".into(),
173 });
174 }
175 let preds = self.predict(x)?;
176 Ok(r2_score(&preds, y))
177 }
178}
179
180pub(crate) fn mean_accuracy<F: Float>(predictions: &Array1<usize>, targets: &Array1<usize>) -> F {
185 let n = targets.len();
186 if n == 0 {
187 return F::zero();
188 }
189 let correct = predictions
190 .iter()
191 .zip(targets.iter())
192 .filter(|(p, t)| p == t)
193 .count();
194 F::from(correct).unwrap() / F::from(n).unwrap()
195}
196
197pub(crate) fn r2_score<F: Float>(y_pred: &Array1<F>, y_true: &Array1<F>) -> F {
202 let n = y_true.len();
203 if n == 0 {
204 return F::zero();
205 }
206 let mean = y_true.iter().copied().fold(F::zero(), |a, b| a + b) / F::from(n).unwrap();
207 let mut ss_res = F::zero();
208 let mut ss_tot = F::zero();
209 for i in 0..n {
210 let r = y_true[i] - y_pred[i];
211 let t = y_true[i] - mean;
212 ss_res = ss_res + r * r;
213 ss_tot = ss_tot + t * t;
214 }
215 if ss_tot == F::zero() {
216 if ss_res == F::zero() {
217 F::one()
218 } else {
219 F::neg_infinity()
220 }
221 } else {
222 F::one() - ss_res / ss_tot
223 }
224}
225
226pub(crate) fn log_proba<F: Float>(proba: &Array2<F>) -> Array2<F> {
230 let eps = F::from(1e-300).unwrap();
231 proba.mapv(|p| if p > eps { p.ln() } else { eps.ln() })
232}