use crate::*;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
use scirs2_core::random::{Random, rng};
use sklears_core::prelude::SklearsError;
use sklears_core::types::Float;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct ReservoirSampler {
reservoir_size: usize,
random_state: Option<u64>,
reservoir: Vec<usize>,
seen_count: usize,
}
impl ReservoirSampler {
pub fn new() -> Self {
Self {
reservoir_size: 1000,
random_state: None,
reservoir: Vec::new(),
seen_count: 0,
}
}
pub fn reservoir_size(mut self, size: usize) -> Self {
self.reservoir_size = size;
self
}
pub fn random_state(mut self, seed: u64) -> Self {
self.random_state = Some(seed);
self
}
pub fn sample_indices(&mut self, dataset_size: usize) -> SklResult<Vec<usize>> {
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let actual_size = self.reservoir_size.min(dataset_size);
let mut reservoir = Vec::with_capacity(actual_size);
for i in 0..actual_size {
reservoir.push(i);
}
for i in actual_size..dataset_size {
let j = rng.gen_range(0..i + 1);
if j < actual_size {
reservoir[j] = i;
}
}
self.reservoir = reservoir.clone();
self.seen_count = dataset_size;
Ok(reservoir)
}
pub fn sample_data(&mut self, data: &ArrayView2<Float>) -> SklResult<Array2<Float>> {
let indices = self.sample_indices(data.nrows())?;
let sampled_data = data.select(Axis(0), &indices);
Ok(sampled_data)
}
pub fn get_reservoir_indices(&self) -> &[usize] {
&self.reservoir
}
pub fn get_seen_count(&self) -> usize {
self.seen_count
}
}
impl Default for ReservoirSampler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ImportanceSampler {
sample_size: usize,
replacement: bool,
random_state: Option<u64>,
}
impl ImportanceSampler {
pub fn new() -> Self {
Self {
sample_size: 1000,
replacement: false,
random_state: None,
}
}
pub fn sample_size(mut self, size: usize) -> Self {
self.sample_size = size;
self
}
pub fn replacement(mut self, replacement: bool) -> Self {
self.replacement = replacement;
self
}
pub fn random_state(mut self, seed: u64) -> Self {
self.random_state = Some(seed);
self
}
pub fn sample_indices(&self, weights: &ArrayView1<Float>) -> SklResult<Vec<usize>> {
if weights.is_empty() {
return Err(SklearsError::InvalidInput(
"Weights cannot be empty".to_string(),
));
}
let weight_sum: Float = weights.sum();
if weight_sum <= 0.0 {
return Err(SklearsError::InvalidInput(
"Weights must be positive".to_string(),
));
}
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let mut cumulative = Vec::with_capacity(weights.len());
let mut sum = 0.0;
for &weight in weights.iter() {
sum += weight / weight_sum;
cumulative.push(sum);
}
let mut selected_indices = Vec::with_capacity(self.sample_size);
let mut used_indices = HashSet::new();
for _ in 0..self.sample_size {
let random_value: Float = rng.random_range(0.0..1.0);
let mut idx = 0;
for (i, &cum_prob) in cumulative.iter().enumerate() {
if random_value <= cum_prob {
idx = i;
break;
}
}
if self.replacement || !used_indices.contains(&idx) {
selected_indices.push(idx);
if !self.replacement {
used_indices.insert(idx);
}
} else if !self.replacement {
for i in 0..weights.len() {
if !used_indices.contains(&i) {
selected_indices.push(i);
used_indices.insert(i);
break;
}
}
}
if !self.replacement && used_indices.len() == weights.len() {
break;
}
}
Ok(selected_indices)
}
pub fn sample_data(
&self,
data: &ArrayView2<Float>,
weights: &ArrayView1<Float>,
) -> SklResult<Array2<Float>> {
if data.nrows() != weights.len() {
return Err(SklearsError::InvalidInput(
"Data rows and weights length must match".to_string(),
));
}
let indices = self.sample_indices(weights)?;
let sampled_data = data.select(Axis(0), &indices);
Ok(sampled_data)
}
}
impl Default for ImportanceSampler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct StratifiedSampler {
sample_size: usize,
random_state: Option<u64>,
}
impl StratifiedSampler {
pub fn new() -> Self {
Self {
sample_size: 1000,
random_state: None,
}
}
pub fn sample_size(mut self, size: usize) -> Self {
self.sample_size = size;
self
}
pub fn random_state(mut self, seed: u64) -> Self {
self.random_state = Some(seed);
self
}
pub fn sample_indices(&self, strata: &ArrayView1<Float>) -> SklResult<Vec<usize>> {
if strata.is_empty() {
return Err(SklearsError::InvalidInput(
"Strata cannot be empty".to_string(),
));
}
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let mut stratum_indices: HashMap<String, Vec<usize>> = HashMap::new();
for (idx, &stratum) in strata.iter().enumerate() {
let key = format!("{:.6}", stratum); stratum_indices
.entry(key)
.or_insert_with(Vec::new)
.push(idx);
}
let total_samples = strata.len();
let mut selected_indices = Vec::new();
for (_, indices) in stratum_indices.iter() {
let stratum_proportion = indices.len() as f64 / total_samples as f64;
let stratum_sample_size =
(self.sample_size as f64 * stratum_proportion).round() as usize;
let stratum_sample_size = stratum_sample_size.min(indices.len());
if stratum_sample_size > 0 {
let mut stratum_indices = indices.clone();
Self::shuffle(&mut stratum_indices, &mut rng);
selected_indices.extend(stratum_indices.into_iter().take(stratum_sample_size));
}
}
while selected_indices.len() < self.sample_size && selected_indices.len() < total_samples {
let all_indices: Vec<usize> = (0..total_samples).collect();
let mut remaining: Vec<usize> = all_indices
.into_iter()
.filter(|&idx| !selected_indices.contains(&idx))
.collect();
if remaining.is_empty() {
break;
}
Self::shuffle(&mut remaining, &mut rng);
let needed = self.sample_size - selected_indices.len();
selected_indices.extend(remaining.into_iter().take(needed));
}
selected_indices.truncate(self.sample_size);
Ok(selected_indices)
}
pub fn sample_data(
&self,
data: &ArrayView2<Float>,
strata: &ArrayView1<Float>,
) -> SklResult<Array2<Float>> {
if data.nrows() != strata.len() {
return Err(SklearsError::InvalidInput(
"Data rows and strata length must match".to_string(),
));
}
let indices = self.sample_indices(strata)?;
let sampled_data = data.select(Axis(0), &indices);
Ok(sampled_data)
}
fn shuffle<T>(slice: &mut [T], rng: &mut Random) {
for i in (1..slice.len()).rev() {
let j = rng.gen_range(0..i + 1);
slice.swap(i, j);
}
}
}
impl Default for StratifiedSampler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct BootstrapSampler {
n_bootstrap_samples: usize,
sample_size: Option<usize>,
random_state: Option<u64>,
}
impl BootstrapSampler {
pub fn new() -> Self {
Self {
n_bootstrap_samples: 100,
sample_size: None,
random_state: None,
}
}
pub fn n_bootstrap_samples(mut self, n_samples: usize) -> Self {
self.n_bootstrap_samples = n_samples;
self
}
pub fn sample_size(mut self, size: usize) -> Self {
self.sample_size = Some(size);
self
}
pub fn random_state(mut self, seed: u64) -> Self {
self.random_state = Some(seed);
self
}
pub fn sample_single(&self, data: &ArrayView2<Float>) -> SklResult<Array2<Float>> {
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let sample_size = self.sample_size.unwrap_or(data.nrows());
let mut indices = Vec::with_capacity(sample_size);
for _ in 0..sample_size {
let idx = rng.gen_range(0..data.nrows());
indices.push(idx);
}
let sampled_data = data.select(Axis(0), &indices);
Ok(sampled_data)
}
pub fn sample_multiple(&self, data: &ArrayView2<Float>) -> SklResult<Vec<Array2<Float>>> {
let mut samples = Vec::with_capacity(self.n_bootstrap_samples);
for i in 0..self.n_bootstrap_samples {
let sampler = if let Some(base_seed) = self.random_state {
self.clone().random_state(base_seed + i as u64)
} else {
self.clone()
};
let sample = sampler.sample_single(data)?;
samples.push(sample);
}
Ok(samples)
}
pub fn sample_indices(&self, n_samples: usize) -> SklResult<Vec<Vec<usize>>> {
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let sample_size = self.sample_size.unwrap_or(n_samples);
let mut all_indices = Vec::with_capacity(self.n_bootstrap_samples);
for _ in 0..self.n_bootstrap_samples {
let mut indices = Vec::with_capacity(sample_size);
for _ in 0..sample_size {
let idx = rng.gen_range(0..n_samples);
indices.push(idx);
}
all_indices.push(indices);
}
Ok(all_indices)
}
}
impl Default for BootstrapSampler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SystematicSampler {
sample_size: usize,
random_state: Option<u64>,
}
impl SystematicSampler {
pub fn new() -> Self {
Self {
sample_size: 100,
random_state: None,
}
}
pub fn sample_size(mut self, size: usize) -> Self {
self.sample_size = size;
self
}
pub fn random_state(mut self, seed: u64) -> Self {
self.random_state = Some(seed);
self
}
pub fn sample_indices(&self, population_size: usize) -> SklResult<Vec<usize>> {
if population_size == 0 {
return Err(SklearsError::InvalidInput(
"Population size cannot be zero".to_string(),
));
}
let sample_size = self.sample_size.min(population_size);
if sample_size == 0 {
return Ok(Vec::new());
}
let mut rng = if let Some(seed) = self.random_state {
Random::with_seed(seed)
} else {
Random::new()
};
let interval = population_size as f64 / sample_size as f64;
let start = rng.random_range(0.0..interval);
let mut indices = Vec::with_capacity(sample_size);
for i in 0..sample_size {
let idx = (start + i as f64 * interval) as usize;
if idx < population_size {
indices.push(idx);
}
}
Ok(indices)
}
pub fn sample_data(&self, data: &ArrayView2<Float>) -> SklResult<Array2<Float>> {
let indices = self.sample_indices(data.nrows())?;
let sampled_data = data.select(Axis(0), &indices);
Ok(sampled_data)
}
}
impl Default for SystematicSampler {
fn default() -> Self {
Self::new()
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::{Array1, Array2};
#[test]
fn test_reservoir_sampler_basic() {
let mut sampler = ReservoirSampler::new()
.reservoir_size(10)
.random_state(42);
let indices = sampler.sample_indices(20).expect("operation should succeed");
assert_eq!(indices.len(), 10);
assert_eq!(sampler.get_seen_count(), 20);
for &idx in &indices {
assert!(idx < 20);
}
}
#[test]
fn test_importance_sampler_basic() {
let weights = Array1::from_vec(vec![0.1, 0.3, 0.2, 0.4]);
let sampler = ImportanceSampler::new()
.sample_size(3)
.random_state(42);
let indices = sampler.sample_indices(&weights.view()).expect("operation should succeed");
assert_eq!(indices.len(), 3);
for &idx in &indices {
assert!(idx < 4);
}
}
#[test]
fn test_stratified_sampler_basic() {
let strata = Array1::from_vec(vec![1.0, 1.0, 2.0, 2.0, 3.0, 3.0]);
let sampler = StratifiedSampler::new()
.sample_size(4)
.random_state(42);
let indices = sampler.sample_indices(&strata.view()).expect("operation should succeed");
assert_eq!(indices.len(), 4);
for &idx in &indices {
assert!(idx < 6);
}
}
#[test]
fn test_bootstrap_sampler_basic() {
let data = Array2::from_shape_vec((10, 3), (0..30).map(|x| x as f64).collect()).expect("operation should succeed");
let sampler = BootstrapSampler::new()
.n_bootstrap_samples(5)
.random_state(42);
let samples = sampler.sample_multiple(&data.view()).expect("operation should succeed");
assert_eq!(samples.len(), 5);
for sample in &samples {
assert_eq!(sample.nrows(), 10);
assert_eq!(sample.ncols(), 3);
}
}
#[test]
fn test_systematic_sampler_basic() {
let data = Array2::from_shape_vec((100, 2), (0..200).map(|x| x as f64).collect()).expect("operation should succeed");
let sampler = SystematicSampler::new()
.sample_size(10)
.random_state(42);
let sampled_data = sampler.sample_data(&data.view()).expect("operation should succeed");
assert_eq!(sampled_data.nrows(), 10);
assert_eq!(sampled_data.ncols(), 2);
}
#[test]
fn test_sampling_with_empty_data() {
let empty_data = Array2::zeros((0, 3));
let mut reservoir_sampler = ReservoirSampler::new().reservoir_size(5);
let result = reservoir_sampler.sample_data(&empty_data.view());
assert!(result.is_ok());
assert_eq!(result.expect("operation should succeed").nrows(), 0);
let systematic_sampler = SystematicSampler::new().sample_size(5);
let result = systematic_sampler.sample_data(&empty_data.view());
assert!(result.is_ok());
assert_eq!(result.expect("operation should succeed").nrows(), 0);
}
#[test]
fn test_sampling_consistency_with_seed() {
let data = Array2::from_shape_vec((50, 2), (0..100).map(|x| x as f64).collect()).expect("operation should succeed");
let mut sampler1 = ReservoirSampler::new()
.reservoir_size(10)
.random_state(42);
let mut sampler2 = ReservoirSampler::new()
.reservoir_size(10)
.random_state(42);
let sample1 = sampler1.sample_data(&data.view()).expect("operation should succeed");
let sample2 = sampler2.sample_data(&data.view()).expect("operation should succeed");
assert_eq!(sample1.shape(), sample2.shape());
for (a, b) in sample1.iter().zip(sample2.iter()) {
assert_eq!(a, b);
}
}
}