use sklears_core::{error::Result as SklResult, types::Float};
use std::fmt::Debug;
pub trait FeatureExtractor: Debug + Send + Sync {
type Input;
type Output;
fn extract_features(&self, input: &[Self::Input]) -> SklResult<Self::Output>;
fn feature_names(&self) -> Option<Vec<String>> {
None
}
fn n_features(&self) -> Option<usize> {
None
}
fn validate_input(&self, _input: &[Self::Input]) -> SklResult<()> {
Ok(())
}
}
pub trait ConfigurableExtractor: FeatureExtractor {
type Config: Clone + Debug;
fn config(&self) -> Self::Config;
fn with_config(&self, config: Self::Config) -> Self;
}
pub trait FittableExtractor: FeatureExtractor {
type Fitted: FeatureExtractor<Input = Self::Input, Output = Self::Output>;
fn fit(&self, data: &[Self::Input]) -> SklResult<Self::Fitted>;
}
pub trait StreamingExtractor: FeatureExtractor {
fn extract_batch(&mut self, batch: &[Self::Input]) -> SklResult<Self::Output>;
fn reset(&mut self);
fn finalize(&mut self) -> SklResult<Self::Output>;
}
pub trait ParallelExtractor: FeatureExtractor {
fn extract_parallel(
&self,
input: &[Self::Input],
n_jobs: Option<usize>,
) -> SklResult<Self::Output>;
}
pub trait FeatureTransformer: Debug + Send + Sync {
type Input;
type Output;
fn transform(&self, features: &Self::Input) -> SklResult<Self::Output>;
fn is_invertible(&self) -> bool {
false
}
fn inverse_transform(&self, _features: &Self::Output) -> SklResult<Self::Input> {
Err(sklears_core::prelude::SklearsError::NotImplemented(
"Inverse transform not supported".to_string(),
))
}
}
pub trait FeatureSelector: Debug + Send + Sync {
fn selected_features(&self) -> Vec<usize>;
fn n_selected(&self) -> usize {
self.selected_features().len()
}
fn is_selected(&self, feature_idx: usize) -> bool {
self.selected_features().contains(&feature_idx)
}
}
pub trait TextFeatureExtractor: FeatureExtractor<Input = String> {
fn vocabulary(&self) -> Option<Vec<String>> {
None
}
fn vocabulary_size(&self) -> Option<usize> {
self.vocabulary().map(|v| v.len())
}
}
pub trait ImageFeatureExtractor: FeatureExtractor {
fn image_shape(&self) -> Option<(usize, usize, usize)> {
None
}
fn supports_color(&self) -> bool {
true
}
fn supports_grayscale(&self) -> bool {
true
}
}
pub trait TimeSeriesFeatureExtractor: FeatureExtractor {
fn window_size(&self) -> Option<usize> {
None
}
fn step_size(&self) -> Option<usize> {
None
}
fn supports_variable_length(&self) -> bool {
false
}
}
pub trait GraphFeatureExtractor: FeatureExtractor {
fn supports_directed(&self) -> bool {
true
}
fn supports_weighted(&self) -> bool {
true
}
fn supports_attributes(&self) -> bool {
false
}
}
pub trait FeaturePipeline: Debug + Send + Sync {
type Input;
type Output;
fn execute(&self, input: &[Self::Input]) -> SklResult<Self::Output>;
fn n_stages(&self) -> usize;
fn stage_names(&self) -> Vec<String> {
(0..self.n_stages())
.map(|i| format!("stage_{}", i))
.collect()
}
}
pub trait FeatureUnion: Debug + Send + Sync {
type Input;
type Output;
fn extract_union(&self, input: &[Self::Input]) -> SklResult<Self::Output>;
fn n_extractors(&self) -> usize;
fn feature_splits(&self) -> Vec<usize>;
}
pub trait FeatureMetadata {
fn feature_importances(&self) -> Option<Vec<Float>> {
None
}
fn feature_types(&self) -> Option<Vec<String>> {
None
}
fn feature_statistics(&self) -> Option<Vec<(Float, Float, Float, Float)>> {
None
}
fn feature_descriptions(&self) -> Option<Vec<String>> {
None
}
}
pub trait ComplexityEstimator {
fn time_complexity(&self) -> String {
"O(n)".to_string()
}
fn space_complexity(&self) -> String {
"O(n)".to_string()
}
fn estimate_operations(&self, _input_size: usize) -> Option<usize> {
None
}
}
#[cfg(feature = "serde")]
pub trait SerializableExtractor:
FeatureExtractor + serde::Serialize + serde::de::DeserializeOwned
{
}
pub trait ClonableExtractor: FeatureExtractor {
fn clone_extractor(
&self,
) -> Box<dyn FeatureExtractor<Input = Self::Input, Output = Self::Output>>;
}
#[derive(Debug, Clone)]
pub struct ExtractionConfig {
pub validate_input: bool,
pub normalize: bool,
pub parallel: bool,
pub n_jobs: Option<usize>,
pub cache: bool,
}
impl Default for ExtractionConfig {
fn default() -> Self {
Self {
validate_input: true,
normalize: false,
parallel: false,
n_jobs: None,
cache: false,
}
}
}
#[derive(Debug, Clone)]
pub struct ExtractionResult<T> {
pub features: T,
pub feature_names: Option<Vec<String>>,
pub n_features: usize,
pub n_samples: usize,
pub metadata: ExtractionMetadata,
}
#[derive(Debug, Clone, Default)]
pub struct ExtractionMetadata {
pub duration_ms: Option<f64>,
pub original_n_features: Option<usize>,
pub normalized: bool,
pub parallel: bool,
pub custom: std::collections::HashMap<String, String>,
}
impl<T> ExtractionResult<T> {
pub fn new(features: T, n_features: usize, n_samples: usize) -> Self {
Self {
features,
feature_names: None,
n_features,
n_samples,
metadata: ExtractionMetadata::default(),
}
}
pub fn with_names(mut self, names: Vec<String>) -> Self {
self.feature_names = Some(names);
self
}
pub fn with_metadata(mut self, metadata: ExtractionMetadata) -> Self {
self.metadata = metadata;
self
}
}