diff --git a/CHANGELOG.md b/CHANGELOG.md index 26b52ccc..3990e24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.6.12] ### Fixed -- `xgboost/xgb_regressor.rs`: `XGRegressor::fit` no longer panics with `attempt to subtract with overflow` when `subsample` is less than 1.0 on a small dataset (#444). `floor(n_samples * subsample)` truncates to 0 rows. This occurs with 3 rows at a ratio of 0.3, or with 1 row at any ratio below 1.0. The tree fit then read `sorted_idxs.len() - 1` on an empty index set. The sample size now keeps a minimum of one row, as scikit-learn does for its own `subsample` parameter. Sample sizes of one row or more are unchanged. +- `xgboost/xgb_regressor.rs`: `XGRegressor::fit` no longer panics when `subsample` is less than 1.0 on a small dataset (#444). The sample for each tree now keeps a minimum of one row, as scikit-learn does for its own `subsample` parameter. Sample sizes of one row or more are unchanged. ## [0.6.11] ### Fixed diff --git a/src/algorithm/neighbour/cosinepair.rs b/src/algorithm/neighbour/cosinepair.rs index 8ab8cb9e..598806ce 100644 --- a/src/algorithm/neighbour/cosinepair.rs +++ b/src/algorithm/neighbour/cosinepair.rs @@ -39,6 +39,7 @@ use crate::numbers::realnum::RealNumber; /// Parameters for CosinePair construction #[derive(Debug, Clone)] +#[must_use] pub struct CosinePairParameters { /// Maximum number of neighbours returned by /// [`CosinePair::query_row_top_k`] (default: all points). The build stays @@ -395,6 +396,7 @@ impl<'a, T: RealNumber + FloatNumber + FloatCore, M: Array2> CosinePair<'a, T /// Find closest pair by scanning list of nearest neighbors. #[allow(dead_code)] + #[must_use] pub fn closest_pair(&self) -> PairwiseDistance { let mut a = self.neighbours[0]; // Start with first point let mut d = self.distances[&a].distance; @@ -416,6 +418,7 @@ impl<'a, T: RealNumber + FloatNumber + FloatCore, M: Array2> CosinePair<'a, T /// Return order dissimilarities from closest to furthest /// #[allow(dead_code)] + #[must_use] pub fn ordered_pairs(&self) -> std::vec::IntoIter<&PairwiseDistance> { // improvement: implement this to return `impl Iterator>` // need to implement trait `Iterator` for `Vec<&PairwiseDistance>` diff --git a/src/algorithm/neighbour/fastpair.rs b/src/algorithm/neighbour/fastpair.rs index b86578fe..688fd886 100644 --- a/src/algorithm/neighbour/fastpair.rs +++ b/src/algorithm/neighbour/fastpair.rs @@ -156,6 +156,7 @@ impl<'a, T: RealNumber + FloatNumber, M: Array2> FastPair<'a, T, M> { /// Find closest pair by scanning list of nearest neighbors. #[allow(dead_code)] + #[must_use] pub fn closest_pair(&self) -> PairwiseDistance { let mut a = self.neighbours[0]; // Start with first point let mut d = self.distances[&a].distance; @@ -177,6 +178,7 @@ impl<'a, T: RealNumber + FloatNumber, M: Array2> FastPair<'a, T, M> { /// Return order dissimilarities from closest to furthest /// #[allow(dead_code)] + #[must_use] pub fn ordered_pairs(&self) -> std::vec::IntoIter<&PairwiseDistance> { // improvement: implement this to return `impl Iterator>` // need to implement trait `Iterator` for `Vec<&PairwiseDistance>` diff --git a/src/cluster/agglomerative.rs b/src/cluster/agglomerative.rs index 373f6f95..b4298863 100644 --- a/src/cluster/agglomerative.rs +++ b/src/cluster/agglomerative.rs @@ -43,6 +43,7 @@ use crate::numbers::basenum::Number; /// Parameters for the Agglomerative Clustering algorithm. #[derive(Debug, Clone, Copy)] +#[must_use] pub struct AgglomerativeClusteringParameters { /// The number of clusters to find. pub n_clusters: usize, diff --git a/src/cluster/dbscan.rs b/src/cluster/dbscan.rs index 2e2aac10..40a6bfad 100644 --- a/src/cluster/dbscan.rs +++ b/src/cluster/dbscan.rs @@ -72,6 +72,7 @@ pub struct DBSCAN, Y: Array1, D: Dista #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// DBSCAN clustering algorithm parameters +#[must_use] pub struct DBSCANParameters>> { #[cfg_attr(feature = "serde", serde(default))] /// a function that defines a distance between each pair of point in training data. @@ -124,6 +125,7 @@ impl>> DBSCANParameters { /// DBSCAN grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct DBSCANSearchParameters>> { #[cfg_attr(feature = "serde", serde(default))] /// a function that defines a distance between each pair of point in training data. diff --git a/src/cluster/kmeans.rs b/src/cluster/kmeans.rs index 02fd96de..564a7e95 100644 --- a/src/cluster/kmeans.rs +++ b/src/cluster/kmeans.rs @@ -109,6 +109,7 @@ impl, Y: Array1> PartialEq for KMeans< #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// K-Means clustering algorithm parameters +#[must_use] pub struct KMeansParameters { #[cfg_attr(feature = "serde", serde(default))] /// Number of clusters. @@ -148,6 +149,7 @@ impl Default for KMeansParameters { /// KMeans grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct KMeansSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Number of clusters. diff --git a/src/dataset/boston.rs b/src/dataset/boston.rs index 498ffeba..ad285e20 100644 --- a/src/dataset/boston.rs +++ b/src/dataset/boston.rs @@ -28,6 +28,7 @@ use crate::dataset::Dataset; use crate::dataset::deserialize_data; /// Get dataset +#[must_use] pub fn load_dataset() -> Dataset { let (x, y, num_samples, num_features) = match deserialize_data(std::include_bytes!("boston.xy")) { diff --git a/src/dataset/breast_cancer.rs b/src/dataset/breast_cancer.rs index eec14b37..17a0e6f1 100644 --- a/src/dataset/breast_cancer.rs +++ b/src/dataset/breast_cancer.rs @@ -30,6 +30,7 @@ use crate::dataset::Dataset; use crate::dataset::deserialize_data; /// Get dataset +#[must_use] pub fn load_dataset() -> Dataset { let (x, y, num_samples, num_features) = match deserialize_data(std::include_bytes!("breast_cancer.xy")) { diff --git a/src/dataset/diabetes.rs b/src/dataset/diabetes.rs index f863264c..1ac038bf 100644 --- a/src/dataset/diabetes.rs +++ b/src/dataset/diabetes.rs @@ -23,6 +23,7 @@ use crate::dataset::Dataset; use crate::dataset::deserialize_data; /// Get dataset +#[must_use] pub fn load_dataset() -> Dataset { let (x, y, num_samples, num_features) = match deserialize_data(std::include_bytes!("diabetes.xy")) { diff --git a/src/dataset/digits.rs b/src/dataset/digits.rs index c39c7e7c..e48b8c2a 100644 --- a/src/dataset/digits.rs +++ b/src/dataset/digits.rs @@ -13,6 +13,7 @@ use crate::dataset::Dataset; use crate::dataset::deserialize_data; /// Get dataset +#[must_use] pub fn load_dataset() -> Dataset { let (x, y, num_samples, num_features) = match deserialize_data(std::include_bytes!("digits.xy")) { diff --git a/src/dataset/generator.rs b/src/dataset/generator.rs index bf4c7741..37f9c236 100644 --- a/src/dataset/generator.rs +++ b/src/dataset/generator.rs @@ -16,6 +16,7 @@ fn sample_normal(mean: f32, std: f32, rng: &mut impl rand::Rng) -> f32 { } /// Generate `num_centers` clusters of normally distributed points +#[must_use] pub fn make_blobs( num_samples: usize, num_features: usize, @@ -57,6 +58,7 @@ pub fn make_blobs( } /// Make a large circle containing a smaller circle in 2d. +#[must_use] pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset { if !(0.0..1.0).contains(&factor) { panic!("'factor' has to be between 0 and 1."); @@ -97,6 +99,7 @@ pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset Dataset { let num_samples_out = num_samples / 2; let num_samples_in = num_samples - num_samples_out; diff --git a/src/dataset/iris.rs b/src/dataset/iris.rs index 0b4b1dcf..223945c1 100644 --- a/src/dataset/iris.rs +++ b/src/dataset/iris.rs @@ -19,6 +19,7 @@ use crate::dataset::Dataset; use crate::dataset::deserialize_data; /// Get dataset +#[must_use] pub fn load_dataset() -> Dataset { let (x, y, num_samples, num_features): (Vec, Vec, usize, usize) = match deserialize_data(std::include_bytes!("iris.xy")) { diff --git a/src/dataset/mod.rs b/src/dataset/mod.rs index 066f4eef..63f0d377 100644 --- a/src/dataset/mod.rs +++ b/src/dataset/mod.rs @@ -38,6 +38,7 @@ pub struct Dataset { impl Dataset { /// Reshape data into a two-dimensional matrix + #[must_use] pub fn as_matrix(&self) -> Vec> { let mut result: Vec> = Vec::with_capacity(self.num_samples); diff --git a/src/decomposition/pca.rs b/src/decomposition/pca.rs index 35a53895..9e629394 100644 --- a/src/decomposition/pca.rs +++ b/src/decomposition/pca.rs @@ -95,6 +95,7 @@ impl + SVDDecomposable + EVDDecomposable #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// PCA parameters +#[must_use] pub struct PCAParameters { #[cfg_attr(feature = "serde", serde(default))] /// Number of components to keep. @@ -131,6 +132,7 @@ impl Default for PCAParameters { /// PCA grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct PCASearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Number of components to keep. diff --git a/src/decomposition/svd.rs b/src/decomposition/svd.rs index 9b88d546..632ff571 100644 --- a/src/decomposition/svd.rs +++ b/src/decomposition/svd.rs @@ -79,6 +79,7 @@ impl + SVDDecomposable + EVDDecomposable #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// SVD parameters +#[must_use] pub struct SVDParameters { #[cfg_attr(feature = "serde", serde(default))] /// Number of components to keep. @@ -102,6 +103,7 @@ impl SVDParameters { /// SVD grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct SVDSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Maximum number of iterations of the k-means algorithm for a single run. diff --git a/src/ensemble/base_forest_regressor.rs b/src/ensemble/base_forest_regressor.rs index eb5844fd..b6dbb59e 100644 --- a/src/ensemble/base_forest_regressor.rs +++ b/src/ensemble/base_forest_regressor.rs @@ -16,6 +16,7 @@ use crate::tree::base_tree_regressor::{BaseTreeRegressor, BaseTreeRegressorParam #[derive(Debug, Clone)] /// Parameters of the Forest Regressor /// Some parameters here are passed directly into base estimator. +#[must_use] pub struct BaseForestRegressorParameters { #[cfg_attr(feature = "serde", serde(default))] /// Tree max depth. See [Decision Tree Regressor](../../tree/decision_tree_regressor/index.html) diff --git a/src/ensemble/extra_trees_regressor.rs b/src/ensemble/extra_trees_regressor.rs index 818ac6c7..ab0555f7 100644 --- a/src/ensemble/extra_trees_regressor.rs +++ b/src/ensemble/extra_trees_regressor.rs @@ -70,6 +70,7 @@ use crate::tree::base_tree_regressor::Splitter; #[derive(Debug, Clone)] /// Parameters of the Extra Trees Regressor /// Some parameters here are passed directly into base estimator. +#[must_use] pub struct ExtraTreesRegressorParameters { #[cfg_attr(feature = "serde", serde(default))] /// Tree max depth. See [Decision Tree Regressor](../../tree/decision_tree_regressor/index.html) diff --git a/src/ensemble/random_forest_classifier.rs b/src/ensemble/random_forest_classifier.rs index 31e9adb2..78d80631 100644 --- a/src/ensemble/random_forest_classifier.rs +++ b/src/ensemble/random_forest_classifier.rs @@ -68,6 +68,7 @@ use crate::tree::decision_tree_classifier::{ /// Some parameters here are passed directly into base estimator. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct RandomForestClassifierParameters { #[cfg_attr(feature = "serde", serde(default))] /// Split criteria to use when building a tree. See [Decision Tree Classifier](../../tree/decision_tree_classifier/index.html) @@ -218,6 +219,7 @@ impl, Y: /// RandomForestClassifier grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct RandomForestClassifierSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Split criteria to use when building a tree. See [Decision Tree Classifier](../../tree/decision_tree_classifier/index.html) diff --git a/src/ensemble/random_forest_regressor.rs b/src/ensemble/random_forest_regressor.rs index 4d2b01b9..f03b2940 100644 --- a/src/ensemble/random_forest_regressor.rs +++ b/src/ensemble/random_forest_regressor.rs @@ -61,6 +61,7 @@ use crate::tree::base_tree_regressor::Splitter; #[derive(Debug, Clone)] /// Parameters of the Random Forest Regressor /// Some parameters here are passed directly into base estimator. +#[must_use] pub struct RandomForestRegressorParameters { #[cfg_attr(feature = "serde", serde(default))] /// Tree max depth. See [Decision Tree Regressor](../../tree/decision_tree_regressor/index.html) @@ -184,6 +185,7 @@ impl, Y: Array1 /// RandomForestRegressor grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct RandomForestRegressorSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Tree max depth. See [Decision Tree Classifier](../../tree/decision_tree_classifier/index.html) diff --git a/src/error/mod.rs b/src/error/mod.rs index f259400e..0a23dd04 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -39,11 +39,13 @@ pub enum FailedError { impl Failed { ///get type of error #[inline] + #[must_use] pub fn error(&self) -> FailedError { self.err } /// new instance of `FailedError::FitError` + #[must_use] pub fn fit(msg: &str) -> Self { Failed { err: FailedError::FitFailed, @@ -51,6 +53,7 @@ impl Failed { } } /// new instance of `FailedError::PredictFailed` + #[must_use] pub fn predict(msg: &str) -> Self { Failed { err: FailedError::PredictFailed, @@ -59,6 +62,7 @@ impl Failed { } /// new instance of `FailedError::TransformFailed` + #[must_use] pub fn transform(msg: &str) -> Self { Failed { err: FailedError::TransformFailed, @@ -67,6 +71,7 @@ impl Failed { } /// new instance of `FailedError::ParametersError` + #[must_use] pub fn input(msg: &str) -> Self { Failed { err: FailedError::ParametersError, @@ -75,6 +80,7 @@ impl Failed { } /// new instance of `FailedError::InvalidStateError` + #[must_use] pub fn invalid_state(msg: &str) -> Self { Failed { err: FailedError::InvalidStateError, @@ -83,6 +89,7 @@ impl Failed { } /// new instance of `err` + #[must_use] pub fn because(err: FailedError, msg: &str) -> Self { Failed { err, diff --git a/src/linalg/basic/arrays.rs b/src/linalg/basic/arrays.rs index 71bed879..7c082030 100644 --- a/src/linalg/basic/arrays.rs +++ b/src/linalg/basic/arrays.rs @@ -805,6 +805,7 @@ pub trait Array1: MutArrayView1 + Sized + where Self: Sized; /// create a zero array + #[must_use] fn zeros(len: usize) -> Self where T: Number, @@ -813,6 +814,7 @@ pub trait Array1: MutArrayView1 + Sized + Self::fill(len, T::zero()) } /// create an array of ones + #[must_use] fn ones(len: usize) -> Self where T: Number, @@ -821,6 +823,7 @@ pub trait Array1: MutArrayView1 + Sized + Self::fill(len, T::one()) } /// create an array of random values + #[must_use] fn rand(len: usize) -> Self where T: RealNumber, @@ -1043,6 +1046,7 @@ pub trait Array2: MutArrayView2 + Sized + where Self: Sized; /// create a zero 2d array + #[must_use] fn zeros(nrows: usize, ncols: usize) -> Self where T: Number, @@ -1050,6 +1054,7 @@ pub trait Array2: MutArrayView2 + Sized + Self::fill(nrows, ncols, T::zero()) } /// create a 2d array of ones + #[must_use] fn ones(nrows: usize, ncols: usize) -> Self where T: Number, @@ -1057,6 +1062,7 @@ pub trait Array2: MutArrayView2 + Sized + Self::fill(nrows, ncols, T::one()) } /// create an identity matrix + #[must_use] fn eye(size: usize) -> Self where T: Number, @@ -1070,6 +1076,7 @@ pub trait Array2: MutArrayView2 + Sized + matrix } /// create a 2d array of random values + #[must_use] fn rand(nrows: usize, ncols: usize) -> Self where T: RealNumber, diff --git a/src/linalg/ndarray/matrix.rs b/src/linalg/ndarray/matrix.rs index 14067e1f..941d8062 100644 --- a/src/linalg/ndarray/matrix.rs +++ b/src/linalg/ndarray/matrix.rs @@ -54,6 +54,7 @@ impl DenseMatrix { /// assert_eq!(matrix.shape(), (3, 4)); /// assert_eq!(*matrix.get((1, 2)), 6.0); /// ``` + #[must_use] pub fn from_ndarray2(a: &ndarray::Array2) -> Self { // iter() yields logical row-major order regardless of memory layout. Self::from_iterator(a.iter().copied(), a.nrows(), a.ncols(), ROW_MAJOR_AXIS) diff --git a/src/linear/elastic_net.rs b/src/linear/elastic_net.rs index d27b09da..804b9800 100644 --- a/src/linear/elastic_net.rs +++ b/src/linear/elastic_net.rs @@ -72,6 +72,7 @@ use crate::linear::lasso_optimizer::InteriorPointOptimizer; /// Elastic net parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct ElasticNetParameters { #[cfg_attr(feature = "serde", serde(default))] /// Regularization parameter. @@ -147,6 +148,7 @@ impl Default for ElasticNetParameters { /// ElasticNet grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct ElasticNetSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Regularization parameter. diff --git a/src/linear/lasso.rs b/src/linear/lasso.rs index 45b50aeb..c427bb41 100644 --- a/src/linear/lasso.rs +++ b/src/linear/lasso.rs @@ -39,6 +39,7 @@ use crate::numbers::realnum::RealNumber; /// Lasso regression parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LassoParameters { #[cfg_attr(feature = "serde", serde(default))] /// Controls the strength of the penalty to the loss function. @@ -151,6 +152,7 @@ impl, Y: Array1> Pre /// Lasso grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LassoSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Controls the strength of the penalty to the loss function. diff --git a/src/linear/linear_regression.rs b/src/linear/linear_regression.rs index cadb3953..bb541878 100644 --- a/src/linear/linear_regression.rs +++ b/src/linear/linear_regression.rs @@ -106,6 +106,7 @@ pub enum LinearRegressionSolverName { /// Linear Regression parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LinearRegressionParameters { #[cfg_attr(feature = "serde", serde(default))] /// Solver to use for estimation of regression coefficients. @@ -147,6 +148,7 @@ impl LinearRegressionParameters { /// Linear Regression grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LinearRegressionSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Solver to use for estimation of regression coefficients. diff --git a/src/linear/logistic_regression.rs b/src/linear/logistic_regression.rs index dd2d9062..92ea277a 100644 --- a/src/linear/logistic_regression.rs +++ b/src/linear/logistic_regression.rs @@ -82,6 +82,7 @@ pub enum LogisticRegressionSolverName { /// Logistic Regression parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LogisticRegressionParameters { #[cfg_attr(feature = "serde", serde(default))] /// Solver to use for estimation of regression coefficients. @@ -94,6 +95,7 @@ pub struct LogisticRegressionParameters { /// Logistic Regression grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct LogisticRegressionSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Solver to use for estimation of regression coefficients. diff --git a/src/linear/ridge_regression.rs b/src/linear/ridge_regression.rs index e0a8d11c..f1015240 100644 --- a/src/linear/ridge_regression.rs +++ b/src/linear/ridge_regression.rs @@ -84,6 +84,7 @@ pub enum RidgeRegressionSolverName { /// Ridge Regression parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct RidgeRegressionParameters { /// Solver to use for estimation of regression coefficients. pub solver: RidgeRegressionSolverName, @@ -97,6 +98,7 @@ pub struct RidgeRegressionParameters { /// Ridge Regression grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct RidgeRegressionSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Solver to use for estimation of regression coefficients. diff --git a/src/metrics/cluster_hcv.rs b/src/metrics/cluster_hcv.rs index 2be4a763..9a29394a 100644 --- a/src/metrics/cluster_hcv.rs +++ b/src/metrics/cluster_hcv.rs @@ -21,14 +21,17 @@ pub struct HCVScore { impl HCVScore { /// return homogenity score + #[must_use] pub fn homogeneity(&self) -> Option { self.homogeneity } /// return completeness score + #[must_use] pub fn completeness(&self) -> Option { self.completeness } /// return v_measure score + #[must_use] pub fn v_measure(&self) -> Option { self.v_measure } diff --git a/src/metrics/distance/cosine.rs b/src/metrics/distance/cosine.rs index 6e62eebb..df403f55 100644 --- a/src/metrics/distance/cosine.rs +++ b/src/metrics/distance/cosine.rs @@ -50,6 +50,7 @@ impl Default for Cosine { impl Cosine { /// Instantiate the initial structure + #[must_use] pub fn new() -> Cosine { Cosine { _t: PhantomData } } diff --git a/src/metrics/distance/euclidian.rs b/src/metrics/distance/euclidian.rs index 78e4a5ad..a325ed44 100644 --- a/src/metrics/distance/euclidian.rs +++ b/src/metrics/distance/euclidian.rs @@ -42,6 +42,7 @@ impl Default for Euclidian { impl Euclidian { /// instatiate the initial structure + #[must_use] pub fn new() -> Euclidian { Euclidian { _t: PhantomData } } diff --git a/src/metrics/distance/hamming.rs b/src/metrics/distance/hamming.rs index ac0c2c3e..f9326a0d 100644 --- a/src/metrics/distance/hamming.rs +++ b/src/metrics/distance/hamming.rs @@ -36,6 +36,7 @@ pub struct Hamming { impl Hamming { /// instatiate the initial structure + #[must_use] pub fn new() -> Hamming { Hamming { _t: PhantomData } } diff --git a/src/metrics/distance/jaccard.rs b/src/metrics/distance/jaccard.rs index a3a13dcf..c0a1c7da 100644 --- a/src/metrics/distance/jaccard.rs +++ b/src/metrics/distance/jaccard.rs @@ -41,6 +41,7 @@ pub struct Jaccard { impl Jaccard { /// instatiate the initial structure + #[must_use] pub fn new() -> Jaccard { Jaccard { _t: PhantomData } } diff --git a/src/metrics/distance/manhattan.rs b/src/metrics/distance/manhattan.rs index fae78684..6467e601 100644 --- a/src/metrics/distance/manhattan.rs +++ b/src/metrics/distance/manhattan.rs @@ -35,6 +35,7 @@ pub struct Manhattan { impl Manhattan { /// instatiate the initial structure + #[must_use] pub fn new() -> Manhattan { Manhattan { _t: PhantomData } } diff --git a/src/metrics/distance/minkowski.rs b/src/metrics/distance/minkowski.rs index 93e0c930..a320d01f 100644 --- a/src/metrics/distance/minkowski.rs +++ b/src/metrics/distance/minkowski.rs @@ -41,6 +41,7 @@ pub struct Minkowski { impl Minkowski { /// instatiate the initial structure + #[must_use] pub fn new(p: u16) -> Minkowski { Minkowski { p, _t: PhantomData } } diff --git a/src/metrics/distance/mod.rs b/src/metrics/distance/mod.rs index f720013e..885a31bb 100644 --- a/src/metrics/distance/mod.rs +++ b/src/metrics/distance/mod.rs @@ -49,27 +49,32 @@ pub struct Distances {} impl Distances { /// Euclidian distance, see [`Euclidian`](euclidian/index.html) + #[must_use] pub fn euclidian() -> euclidian::Euclidian { euclidian::Euclidian::new() } /// Minkowski distance, see [`Minkowski`](minkowski/index.html) /// * `p` - function order. Should be >= 1 + #[must_use] pub fn minkowski(p: u16) -> minkowski::Minkowski { minkowski::Minkowski::new(p) } /// Manhattan distance, see [`Manhattan`](manhattan/index.html) + #[must_use] pub fn manhattan() -> manhattan::Manhattan { manhattan::Manhattan::new() } /// Hamming distance, see [`Hamming`](hamming/index.html) + #[must_use] pub fn hamming() -> hamming::Hamming { hamming::Hamming::new() } /// Jaccard distance, see [`Jaccard`](jaccard/index.html) + #[must_use] pub fn jaccard() -> jaccard::Jaccard { jaccard::Jaccard::new() } diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index f4cda45a..b8dac5ab 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -124,6 +124,7 @@ impl ClassificationMetrics { /// /// Works with float and integer labels, e.g. the ordered integer labels /// accepted by `RandomForestClassifier::fit`. + #[must_use] pub fn recall() -> recall::Recall { recall::Recall::new() } @@ -132,6 +133,7 @@ impl ClassificationMetrics { /// /// Works with float and integer labels, e.g. the ordered integer labels /// accepted by `RandomForestClassifier::fit`. + #[must_use] pub fn precision() -> precision::Precision { precision::Precision::new() } @@ -140,6 +142,7 @@ impl ClassificationMetrics { /// /// Works with float and integer labels, e.g. the ordered integer labels /// accepted by `RandomForestClassifier::fit`. + #[must_use] pub fn f1(beta: f64) -> f1::F1 { f1::F1::new_with(beta) } @@ -147,6 +150,7 @@ impl ClassificationMetrics { impl ClassificationMetrics { /// Area Under the Receiver Operating Characteristic Curve (ROC AUC), see [AUC](auc/index.html). + #[must_use] pub fn roc_auc_score() -> auc::AUC { auc::AUC::::new() } @@ -154,6 +158,7 @@ impl ClassificationMetrics { impl ClassificationMetricsOrd { /// Accuracy score, see [accuracy](accuracy/index.html). + #[must_use] pub fn accuracy() -> accuracy::Accuracy { accuracy::Accuracy::new() } @@ -161,16 +166,19 @@ impl ClassificationMetricsOrd { impl RegressionMetrics { /// Mean squared error, see [mean squared error](mean_squared_error/index.html). + #[must_use] pub fn mean_squared_error() -> mean_squared_error::MeanSquareError { mean_squared_error::MeanSquareError::new() } /// Mean absolute error, see [mean absolute error](mean_absolute_error/index.html). + #[must_use] pub fn mean_absolute_error() -> mean_absolute_error::MeanAbsoluteError { mean_absolute_error::MeanAbsoluteError::new() } /// Coefficient of determination (R2), see [R2](r2/index.html). + #[must_use] pub fn r2() -> r2::R2 { r2::R2::::new() } @@ -178,6 +186,7 @@ impl RegressionMetrics { impl ClusterMetrics { /// Homogeneity and completeness and V-Measure scores at once. + #[must_use] pub fn hcv_score() -> cluster_hcv::HCVScore { cluster_hcv::HCVScore::::new() } diff --git a/src/model_selection/hyper_tuning/grid_search.rs b/src/model_selection/hyper_tuning/grid_search.rs index 74242c60..3bac0087 100644 --- a/src/model_selection/hyper_tuning/grid_search.rs +++ b/src/model_selection/hyper_tuning/grid_search.rs @@ -12,6 +12,7 @@ use crate::model_selection::{cross_validate, BaseKFold, CrossValidationResult}; /// Parameters for GridSearchCV #[derive(Debug)] +#[must_use] pub struct GridSearchCVParameters< T: Number, M: Array2, diff --git a/src/model_selection/kfold.rs b/src/model_selection/kfold.rs index d7ad22d2..f60fec97 100644 --- a/src/model_selection/kfold.rs +++ b/src/model_selection/kfold.rs @@ -81,17 +81,20 @@ impl Default for KFold { impl KFold { /// Number of folds. Must be at least 2. + #[must_use] pub fn with_n_splits(mut self, n_splits: usize) -> Self { self.n_splits = n_splits; self } /// Whether to shuffle the data before splitting into batches + #[must_use] pub fn with_shuffle(mut self, shuffle: bool) -> Self { self.shuffle = shuffle; self } /// When shuffle is True, random_state affects the ordering of the indices. + #[must_use] pub fn with_seed(mut self, seed: Option) -> Self { self.seed = seed; self diff --git a/src/model_selection/mod.rs b/src/model_selection/mod.rs index c57ebc4c..687e0711 100644 --- a/src/model_selection/mod.rs +++ b/src/model_selection/mod.rs @@ -197,6 +197,7 @@ pub struct CrossValidationResult { impl CrossValidationResult { /// Average test score + #[must_use] pub fn mean_test_score(&self) -> f64 { let mut sum = 0f64; for s in self.test_score.iter() { @@ -205,6 +206,7 @@ impl CrossValidationResult { sum / self.test_score.len() as f64 } /// Average training score + #[must_use] pub fn mean_train_score(&self) -> f64 { let mut sum = 0f64; for s in self.train_score.iter() { diff --git a/src/naive_bayes/bernoulli.rs b/src/naive_bayes/bernoulli.rs index cdd5b83d..1a552d42 100644 --- a/src/naive_bayes/bernoulli.rs +++ b/src/naive_bayes/bernoulli.rs @@ -128,6 +128,7 @@ impl NBDistribution /// `BernoulliNB` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct BernoulliNBParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). @@ -171,6 +172,7 @@ impl Default for BernoulliNBParameters { /// BernoulliNB grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct BernoulliNBSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). diff --git a/src/naive_bayes/categorical.rs b/src/naive_bayes/categorical.rs index b60ee0d3..b5882206 100644 --- a/src/naive_bayes/categorical.rs +++ b/src/naive_bayes/categorical.rs @@ -258,6 +258,7 @@ impl CategoricalNBDistribution { /// `CategoricalNB` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct CategoricalNBParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). @@ -281,6 +282,7 @@ impl Default for CategoricalNBParameters { /// CategoricalNB grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct CategoricalNBSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). @@ -383,22 +385,26 @@ impl, Y: Array1> CategoricalNB { /// Class labels known to the classifier. /// Returns a vector of size n_classes. + #[must_use] pub fn classes(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_labels } /// Number of training samples observed in each class. /// Returns a vector of size n_classes. + #[must_use] pub fn class_count(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_count } /// Number of features of each sample + #[must_use] pub fn n_features(&self) -> usize { self.inner.as_ref().unwrap().distribution.n_features } /// Number of features of each sample + #[must_use] pub fn n_categories(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.n_categories } @@ -406,12 +412,14 @@ impl, Y: Array1> CategoricalNB { /// Holds arrays of shape (n_classes, n_categories of respective feature) /// for each feature. Each array provides the number of samples /// encountered for each class and category of the specific feature. + #[must_use] pub fn category_count(&self) -> &Vec>> { &self.inner.as_ref().unwrap().distribution.category_count } /// Holds arrays of shape (n_classes, n_categories of respective feature) /// for each feature. Each array provides the empirical log probability /// of categories given the respective feature and class, ``P(x_i|y)``. + #[must_use] pub fn feature_log_prob(&self) -> &Vec>> { &self.inner.as_ref().unwrap().distribution.coefficients } diff --git a/src/naive_bayes/gaussian.rs b/src/naive_bayes/gaussian.rs index 51d45b93..a5a96b88 100644 --- a/src/naive_bayes/gaussian.rs +++ b/src/naive_bayes/gaussian.rs @@ -93,6 +93,7 @@ impl NBDistribution /// `GaussianNB` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Default, Clone)] +#[must_use] pub struct GaussianNBParameters { #[cfg_attr(feature = "serde", serde(default))] /// Prior probabilities of the classes. If specified the priors are not adjusted according to the data @@ -118,6 +119,7 @@ impl GaussianNBParameters { /// GaussianNB grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct GaussianNBSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Prior probabilities of the classes. If specified the priors are not adjusted according to the data @@ -323,30 +325,35 @@ impl, Y: Arr /// Class labels known to the classifier. /// Returns a vector of size n_classes. + #[must_use] pub fn classes(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_labels } /// Number of training samples observed in each class. /// Returns a vector of size n_classes. + #[must_use] pub fn class_count(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_count } /// Probability of each class /// Returns a vector of size n_classes. + #[must_use] pub fn class_priors(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_priors } /// Mean of each feature per class /// Returns a 2d vector of shape (n_classes, n_features). + #[must_use] pub fn theta(&self) -> &Vec> { &self.inner.as_ref().unwrap().distribution.theta } /// Variance of each feature per class /// Returns a 2d vector of shape (n_classes, n_features). + #[must_use] pub fn var(&self) -> &Vec> { &self.inner.as_ref().unwrap().distribution.var } diff --git a/src/naive_bayes/multinomial.rs b/src/naive_bayes/multinomial.rs index ad873943..0e21c75c 100644 --- a/src/naive_bayes/multinomial.rs +++ b/src/naive_bayes/multinomial.rs @@ -100,6 +100,7 @@ impl NBDistribution /// `MultinomialNB` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct MultinomialNBParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). @@ -134,6 +135,7 @@ impl Default for MultinomialNBParameters { /// MultinomialNB grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct MultinomialNBSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). @@ -365,29 +367,34 @@ impl, Y: Array /// Class labels known to the classifier. /// Returns a vector of size n_classes. + #[must_use] pub fn classes(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_labels } /// Number of training samples observed in each class. /// Returns a vector of size n_classes. + #[must_use] pub fn class_count(&self) -> &Vec { &self.inner.as_ref().unwrap().distribution.class_count } /// Empirical log probability of features given a class, P(x_i|y). /// Returns a 2d vector of shape (n_classes, n_features) + #[must_use] pub fn feature_log_prob(&self) -> &Vec> { &self.inner.as_ref().unwrap().distribution.feature_log_prob } /// Number of features of each sample + #[must_use] pub fn n_features(&self) -> usize { self.inner.as_ref().unwrap().distribution.n_features } /// Number of samples encountered for each (class, feature) /// Returns a 2d vector of shape (n_classes, n_features) + #[must_use] pub fn feature_count(&self) -> &Vec> { &self.inner.as_ref().unwrap().distribution.feature_count } diff --git a/src/neighbors/knn_classifier.rs b/src/neighbors/knn_classifier.rs index 84bcf72c..b7970f7d 100644 --- a/src/neighbors/knn_classifier.rs +++ b/src/neighbors/knn_classifier.rs @@ -48,6 +48,7 @@ use crate::numbers::basenum::Number; /// `KNNClassifier` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct KNNClassifierParameters>> { #[cfg_attr(feature = "serde", serde(default))] /// a function that defines a distance between each pair of point in training data. diff --git a/src/neighbors/knn_regressor.rs b/src/neighbors/knn_regressor.rs index 2169f954..9edb5f95 100644 --- a/src/neighbors/knn_regressor.rs +++ b/src/neighbors/knn_regressor.rs @@ -51,6 +51,7 @@ use crate::numbers::basenum::Number; /// `KNNRegressor` parameters. Use `Default::default()` for default values. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct KNNRegressorParameters>> { #[cfg_attr(feature = "serde", serde(default))] /// a function that defines a distance between each pair of point in training data. diff --git a/src/preprocessing/categorical.rs b/src/preprocessing/categorical.rs index fd3d35ba..2063e710 100644 --- a/src/preprocessing/categorical.rs +++ b/src/preprocessing/categorical.rs @@ -34,6 +34,7 @@ use crate::preprocessing::traits::{CategoricalFloat, Categorizable}; /// OneHotEncoder Parameters #[derive(Debug, Clone)] +#[must_use] pub struct OneHotEncoderParams { /// Column number that contain categorical variable pub col_idx_categorical: Option>, diff --git a/src/preprocessing/numerical.rs b/src/preprocessing/numerical.rs index ba333c42..148a2a14 100644 --- a/src/preprocessing/numerical.rs +++ b/src/preprocessing/numerical.rs @@ -41,6 +41,7 @@ use serde::{Deserialize, Serialize}; /// Configure Behaviour of `StandardScaler`. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy, Eq, PartialEq)] +#[must_use] pub struct StandardScalerParameters { /// Optionaly adjust mean to be zero. with_mean: bool, diff --git a/src/preprocessing/series_encoder.rs b/src/preprocessing/series_encoder.rs index 3eac2f6a..be62a7a7 100644 --- a/src/preprocessing/series_encoder.rs +++ b/src/preprocessing/series_encoder.rs @@ -62,6 +62,7 @@ where C: Hash + Eq + Clone, { /// Get the number of categories in the mapper + #[must_use] pub fn num_categories(&self) -> usize { self.num_categories } @@ -87,6 +88,7 @@ where } /// Build an encoder from a predefined (category -> class number) map + #[must_use] pub fn from_category_map(category_map: HashMap) -> Self { let mut _unique_cat: Vec<(C, usize)> = category_map.iter().map(|(k, v)| (k.clone(), *v)).collect(); @@ -100,6 +102,7 @@ where } /// Build an encoder from a predefined positional category-class num vector + #[must_use] pub fn from_positional_category_vec(categories: Vec) -> Self { let category_map: HashMap = categories .iter() @@ -119,11 +122,13 @@ where } /// Return category corresponding to label num + #[must_use] pub fn get_cat(&self, num: usize) -> &C { &self.categories[num] } /// List all categories (position = category number) + #[must_use] pub fn get_categories(&self) -> &[C] { &self.categories[..] } @@ -184,6 +189,7 @@ where /// let one_hot: Vec = make_one_hot(2, 3); /// assert_eq!(one_hot, vec![0.0, 0.0, 1.0]); /// ``` +#[must_use] pub fn make_one_hot(category_idx: usize, num_categories: usize) -> V where T: RealNumber, diff --git a/src/readers/error.rs b/src/readers/error.rs index 047092a7..1aac99d5 100644 --- a/src/readers/error.rs +++ b/src/readers/error.rs @@ -40,6 +40,7 @@ impl From for ReadingError { } impl ReadingError { /// Extract the error-message from a `ReadingError`. + #[must_use] pub fn message(&self) -> Option<&str> { match self { ReadingError::InvalidField { msg } => Some(msg), diff --git a/src/svm/mod.rs b/src/svm/mod.rs index 20489fd5..f5df3bdd 100644 --- a/src/svm/mod.rs +++ b/src/svm/mod.rs @@ -68,6 +68,7 @@ pub trait Kernel: Debug { /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq)] +#[must_use] pub enum Kernels { /// Linear kernel (default). /// @@ -198,7 +199,7 @@ impl Kernels { gamma: Some(gamma), coef0, }, - other => other, + Kernels::Linear => Kernels::Linear, } } @@ -214,7 +215,7 @@ impl Kernels { gamma, coef0, }, - other => other, + Kernels::Linear | Kernels::RBF { .. } | Kernels::Sigmoid { .. } => self, } } @@ -235,7 +236,7 @@ impl Kernels { gamma, coef0: Some(coef0), }, - other => other, + Kernels::Linear | Kernels::RBF { .. } => self, } } } diff --git a/src/svm/search/svr_params.rs b/src/svm/search/svr_params.rs index f455c119..5230f872 100644 --- a/src/svm/search/svr_params.rs +++ b/src/svm/search/svr_params.rs @@ -75,6 +75,7 @@ use std::marker::PhantomData; /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct SVRSearchParameters> { /// Epsilon in the epsilon-SVR model. pub eps: Vec, diff --git a/src/svm/svc.rs b/src/svm/svc.rs index f9b74b17..2a4ea13b 100644 --- a/src/svm/svc.rs +++ b/src/svm/svc.rs @@ -148,7 +148,7 @@ impl<'a, TX: Number + RealNumber, TY: Number + Ord, X: Array2, Y: Array1 /// # Returns /// A `Result` containing a `Vec` of predicted class labels (`TX`) or a `Failed` error. fn predict(&self, x: &'a X) -> Result, Failed> { - Ok(self.predict(x).unwrap()) + self.predict(x) } } @@ -218,7 +218,7 @@ impl<'a, TX: Number + RealNumber, TY: Number + Ord, X: Array2, Y: Array1 let classes = (class0, class1); let multiclass_config = MultiClassConfig { classes, indices }; // Fit a binary SVC for the current pair of classes - let svc = SVC::multiclass_fit(x, y, parameters, multiclass_config).unwrap(); + let svc = SVC::multiclass_fit(x, y, parameters, multiclass_config)?; classifiers.push(svc); } } @@ -241,13 +241,15 @@ impl<'a, TX: Number + RealNumber, TY: Number + Ord, X: Array2, Y: Array1 pub fn predict(&self, x: &X) -> Result, Failed> { // Initialize a HashMap for each data point to store votes for each class let mut polls = vec![HashMap::new(); x.shape().0]; - // Retrieve the trained binary classifiers - let classifiers = self.classifiers.as_ref().unwrap(); + // Retrieve the trained binary classifiers. The field is None only before + // fit() has run, e.g. on a deserialized model. + let classifiers = self.classifiers.as_ref().ok_or_else(|| { + Failed::because(FailedError::PredictFailed, "MultiClassSVC is not fitted") + })?; // Iterate through each binary classifier - for i in 0..classifiers.len() { - let svc = classifiers.get(i).unwrap(); - let predictions = svc.predict(x).unwrap(); // call SVC::predict for each binary classifier + for svc in classifiers { + let predictions = svc.predict(x)?; // call SVC::predict for each binary classifier // For each prediction from the current binary classifier for (j, prediction) in predictions.iter().enumerate() { @@ -262,20 +264,28 @@ impl<'a, TX: Number + RealNumber, TY: Number + Ord, X: Array2, Y: Array1 } } - // Determine the final prediction for each data point based on majority vote - Ok(polls + // Determine the final prediction for each data point based on majority vote. + // A poll stays empty when fit() ran on data with fewer than two classes. + polls .iter() .map(|v| { // Find the class with the maximum votes for each data point - TX::from(*v.iter().max_by_key(|(_, class)| *class).unwrap().0).unwrap() + let (class, _) = v.iter().max_by_key(|(_, class)| *class).ok_or_else(|| { + Failed::because( + FailedError::PredictFailed, + "MultiClassSVC must be fitted on at least two classes", + ) + })?; + Ok(TX::from(*class).unwrap()) }) - .collect()) + .collect() } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug)] /// SVC Parameters +#[must_use] pub struct SVCParameters, Y: Array1> { /// Number of epochs. pub epoch: usize, @@ -419,7 +429,7 @@ impl<'a, TX: Number + RealNumber, TY: Number + Ord, X: Array2, Y: Array1 PredictorBorrow<'a, X, TX> for SVC<'a, TX, TY, X, Y> { fn predict(&self, x: &'a X) -> Result, Failed> { - Ok(self.predict(x).unwrap()) + self.predict(x) } } diff --git a/src/svm/svr.rs b/src/svm/svr.rs index 778bdcbd..c8c1267a 100644 --- a/src/svm/svr.rs +++ b/src/svm/svr.rs @@ -86,6 +86,7 @@ use crate::svm::{Kernel, Kernels}; /// SVR Parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug)] +#[must_use] pub struct SVRParameters { /// Epsilon in the epsilon-SVR model. pub eps: T, diff --git a/src/tree/base_tree_regressor.rs b/src/tree/base_tree_regressor.rs index 46bdaea9..24229b06 100644 --- a/src/tree/base_tree_regressor.rs +++ b/src/tree/base_tree_regressor.rs @@ -25,6 +25,7 @@ pub enum Splitter { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// Parameters of Regression base_tree +#[must_use] pub struct BaseTreeRegressorParameters { #[cfg_attr(feature = "serde", serde(default))] /// The maximum depth of the base_tree. diff --git a/src/tree/decision_tree_classifier.rs b/src/tree/decision_tree_classifier.rs index 984087b8..8953d31a 100644 --- a/src/tree/decision_tree_classifier.rs +++ b/src/tree/decision_tree_classifier.rs @@ -86,6 +86,7 @@ use crate::rand_custom::get_rng_impl; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// Parameters of Decision Tree +#[must_use] pub struct DecisionTreeClassifierParameters { #[cfg_attr(feature = "serde", serde(default))] /// Split criteria to use when building a tree. @@ -140,6 +141,7 @@ impl, Y: Array1> self.classes.as_ref() } /// Get depth of tree + #[must_use] pub fn depth(&self) -> u16 { self.depth } @@ -249,6 +251,7 @@ impl Default for DecisionTreeClassifierParameters { /// DecisionTreeClassifier grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct DecisionTreeClassifierSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Split criteria to use when building a tree. See [Decision Tree Classifier](../../tree/decision_tree_classifier/index.html) @@ -860,6 +863,7 @@ impl, Y: Array1> } /// Compute feature importances for the fitted tree. + #[must_use] pub fn compute_feature_importances(&self, normalize: bool) -> Vec { let mut importances = vec![0f64; self.num_features]; diff --git a/src/tree/decision_tree_regressor.rs b/src/tree/decision_tree_regressor.rs index 2800fdab..2474155f 100644 --- a/src/tree/decision_tree_regressor.rs +++ b/src/tree/decision_tree_regressor.rs @@ -73,6 +73,7 @@ use crate::numbers::basenum::Number; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] /// Parameters of Regression Tree +#[must_use] pub struct DecisionTreeRegressorParameters { #[cfg_attr(feature = "serde", serde(default))] /// The maximum depth of the tree. @@ -128,6 +129,7 @@ impl Default for DecisionTreeRegressorParameters { /// DecisionTreeRegressor grid search parameters #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] +#[must_use] pub struct DecisionTreeRegressorSearchParameters { #[cfg_attr(feature = "serde", serde(default))] /// Tree max depth. See [Decision Tree Regressor](../../tree/decision_tree_regressor/index.html) diff --git a/src/xgboost/xgb_regressor.rs b/src/xgboost/xgb_regressor.rs index 168c21d0..fcc12490 100644 --- a/src/xgboost/xgb_regressor.rs +++ b/src/xgboost/xgb_regressor.rs @@ -391,6 +391,7 @@ impl, Y: Array1> /// This struct holds all the hyperparameters that control the training process. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] +#[must_use] pub struct XGRegressorParameters { /// The number of boosting rounds or trees to build. pub n_estimators: usize, @@ -492,7 +493,7 @@ impl XGRegressorParameters { /// /// A value of less than 1.0 introduces randomness and helps prevent overfitting. /// The value must be in the range (0, 1]. Each tree gets `floor(n_samples * subsample)` - /// rows, but a minimum of one row. + /// rows, with a minimum of one row (provided the dataset is non-empty). pub fn with_subsample(mut self, subsample: f64) -> Self { self.subsample = subsample; self @@ -609,6 +610,8 @@ impl, Y: Array1> XGRegres subsample_ratio: f64, rng: &mut impl Rng, ) -> Vec { + debug_assert!(population_size > 0); + debug_assert!(subsample_ratio > 0.0 && subsample_ratio <= 1.0); let mut indices: Vec = (0..population_size).collect(); indices.shuffle(rng); // `population_size * subsample_ratio` truncates to 0 for a small population, e.g. 3 rows @@ -809,7 +812,11 @@ mod tests { let predictions: Vec = model.unwrap().predict(&x).unwrap(); assert_eq!(predictions.len(), 1); assert!(predictions[0].is_finite()); + // Defaults are base_score 0.5 and learning_rate 0.3. Five boosted steps move + // the prediction towards the target 5.0, but do not reach it. + assert!(predictions[0] > 0.5 && predictions[0] < 5.0); } + #[test] fn test_sample_without_replacement_clamps_to_one_row() { // (population, ratio): each pair gives `floor(population * ratio) == 0`.