datarust 0.6.1

Scikit-learn-style preprocessing and classical ML in Rust
Documentation
//! Output container for column-transformer results, preserving the separation
//! between numeric and categorical columns after transformation.

use crate::error::{DatarustError, Result};
use crate::matrix::{Matrix, StrMatrix};

/// The result of applying a [`ColumnTransformer`](super::ColumnTransformer),
/// keeping numeric and categorical columns in separate matrices.
///
/// Unlike the input [`Table`](super::Table), an `Output` may have a different
/// number or arrangement of columns.  Categorical passthrough columns are
/// preserved as strings rather than being one-hot encoded.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Output {
    /// Numeric columns after transformation.
    pub numeric: Matrix,
    /// Categorical (string) columns after transformation.
    pub categorical: StrMatrix,
}

impl Output {
    /// Creates an `Output` from separate numeric and categorical matrices.
    ///
    /// Returns an error if the row counts do not match.
    pub fn new(numeric: Matrix, categorical: StrMatrix) -> Result<Self> {
        if numeric.nrows() != categorical.nrows() {
            return Err(DatarustError::ShapeMismatch {
                expected: format!("{} rows", numeric.nrows()),
                actual: format!("{} rows", categorical.nrows()),
            });
        }
        Ok(Self {
            numeric,
            categorical,
        })
    }

    /// Returns the number of rows (must be equal in both matrices).
    pub fn nrows(&self) -> usize {
        self.numeric.nrows()
    }

    /// Returns the total number of columns (numeric + categorical).
    pub fn ncols(&self) -> usize {
        self.numeric.ncols() + self.categorical.ncols()
    }
}

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

    #[test]
    fn reports_combined_shape() {
        let output = Output::new(
            Matrix::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]]).unwrap(),
            StrMatrix::new(vec![vec!["a".into()], vec!["b".into()]]).unwrap(),
        )
        .unwrap();

        assert_eq!(output.nrows(), 2);
        assert_eq!(output.ncols(), 3);
    }

    #[test]
    fn rejects_mismatched_row_counts() {
        let error = Output::new(
            Matrix::new(vec![vec![1.0], vec![2.0]]).unwrap(),
            StrMatrix::new(vec![vec!["a".into()]]).unwrap(),
        )
        .unwrap_err();

        assert!(matches!(error, DatarustError::ShapeMismatch { .. }));
    }
}