use std::collections::{HashMap, HashSet};
use std::ops::Range;
use crate::core::error::{Error, Result};
use crate::dataframe::base::DataFrame;
use crate::series::base::Series;
#[derive(Debug, Clone)]
pub enum RowSelector {
Single(String),
Position(usize),
Multiple(Vec<String>),
Positions(Vec<usize>),
Boolean(Vec<bool>),
Range(IndexRange),
All,
}
#[derive(Debug, Clone)]
pub enum ColumnSelector {
Single(String),
Multiple(Vec<String>),
All,
}
#[derive(Debug, Clone)]
pub enum IndexRange {
Standard { start: usize, end: usize },
From { start: usize },
To { end: usize },
Full,
Inclusive { start: usize, end: usize },
ToInclusive { end: usize },
}
#[derive(Debug, Clone)]
pub enum AlignmentStrategy {
Outer,
Inner,
Left,
Right,
}
#[derive(Debug, Clone)]
pub struct MultiLevelIndex {
pub names: Vec<String>,
pub levels: Vec<Vec<String>>,
pub tuples: Vec<Vec<String>>,
}
impl MultiLevelIndex {
pub fn new(names: Vec<String>, levels: Vec<Vec<String>>) -> Result<Self> {
if names.len() != levels.len() {
return Err(Error::InvalidValue(
"Number of names must match number of levels".to_string(),
));
}
if levels.is_empty() {
return Err(Error::InvalidValue(
"At least one level required".to_string(),
));
}
let row_count = levels[0].len();
for level in &levels {
if level.len() != row_count {
return Err(Error::InvalidValue(
"All levels must have the same length".to_string(),
));
}
}
let mut tuples = Vec::with_capacity(row_count);
for i in 0..row_count {
let mut tuple = Vec::with_capacity(levels.len());
for level in &levels {
tuple.push(level[i].clone());
}
tuples.push(tuple);
}
Ok(Self {
names,
levels,
tuples,
})
}
pub fn len(&self) -> usize {
if self.levels.is_empty() {
0
} else {
self.levels[0].len()
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn level_values(&self, level: usize) -> Result<Vec<String>> {
if level >= self.levels.len() {
return Err(Error::IndexOutOfBounds {
index: level,
size: self.levels.len(),
});
}
let mut unique_values: Vec<String> = self.levels[level].iter().cloned().collect();
unique_values.sort();
unique_values.dedup();
Ok(unique_values)
}
pub fn find_tuple(&self, tuple: &[String]) -> Vec<usize> {
self.tuples
.iter()
.enumerate()
.filter_map(|(i, t)| {
if t.len() >= tuple.len() && &t[..tuple.len()] == tuple {
Some(i)
} else {
None
}
})
.collect()
}
}
fn get_cell_string(df: &DataFrame, column: &str, row: usize) -> Result<String> {
if !df.contains_column(column) {
return Err(Error::ColumnNotFound(column.to_string()));
}
macro_rules! try_cell {
($ty:ty) => {
if let Ok(series) = df.get_column::<$ty>(column) {
return match series.get(row) {
Some(value) => Ok(value.to_string()),
None => Err(Error::IndexOutOfBounds {
index: row,
size: series.len(),
}),
};
}
};
}
try_cell!(String);
try_cell!(i64);
try_cell!(f64);
try_cell!(i32);
try_cell!(f32);
try_cell!(bool);
try_cell!(i8);
try_cell!(i16);
try_cell!(i128);
try_cell!(isize);
try_cell!(u8);
try_cell!(u16);
try_cell!(u32);
try_cell!(u64);
try_cell!(u128);
try_cell!(usize);
try_cell!(chrono::NaiveDate);
try_cell!(chrono::NaiveDateTime);
try_cell!(chrono::DateTime<chrono::Utc>);
Err(Error::InvalidValue(format!(
"Column '{}' has an element type that is not supported for string conversion",
column
)))
}
fn gather_column_typed(
dst: &mut DataFrame,
src: &DataFrame,
name: &str,
positions: &[usize],
) -> Result<()> {
if !src.contains_column(name) {
return Err(Error::ColumnNotFound(name.to_string()));
}
macro_rules! try_gather {
($ty:ty) => {
if let Ok(series) = src.get_column::<$ty>(name) {
let mut values: Vec<$ty> = Vec::with_capacity(positions.len());
for &i in positions {
match series.get(i) {
Some(value) => values.push(value.clone()),
None => {
return Err(Error::IndexOutOfBounds {
index: i,
size: series.len(),
})
}
}
}
dst.add_column(
name.to_string(),
Series::new(values, Some(name.to_string()))?,
)?;
return Ok(());
}
};
}
try_gather!(String);
try_gather!(i64);
try_gather!(f64);
try_gather!(i32);
try_gather!(f32);
try_gather!(bool);
try_gather!(i8);
try_gather!(i16);
try_gather!(i128);
try_gather!(isize);
try_gather!(u8);
try_gather!(u16);
try_gather!(u32);
try_gather!(u64);
try_gather!(u128);
try_gather!(usize);
try_gather!(chrono::NaiveDate);
try_gather!(chrono::NaiveDateTime);
try_gather!(chrono::DateTime<chrono::Utc>);
Err(Error::InvalidValue(format!(
"Column '{}' has an element type that is not supported for this operation",
name
)))
}
fn select_rows_by_positions(df: &DataFrame, positions: &[usize]) -> Result<DataFrame> {
let row_count = df.row_count();
for &pos in positions {
if pos >= row_count {
return Err(Error::IndexOutOfBounds {
index: pos,
size: row_count,
});
}
}
let mut result = DataFrame::new();
for name in df.column_names() {
gather_column_typed(&mut result, df, name, positions)?;
}
if let crate::index::DataFrameIndex::Simple(idx) = df.get_index() {
if !idx.is_empty() {
let sliced_labels: Vec<String> = positions
.iter()
.map(|&i| idx.get_value(i).cloned().unwrap_or_default())
.collect();
if let Ok(sliced_index) = crate::index::Index::new(sliced_labels) {
DataFrame::set_index(&mut result, sliced_index)?;
}
}
}
Ok(result)
}
fn resolve_label_position(df: &DataFrame, label: &str) -> Result<usize> {
match df.get_index() {
crate::index::DataFrameIndex::Simple(idx) if !idx.is_empty() => idx
.get_loc(&label.to_string())
.ok_or_else(|| Error::InvalidValue(format!("Label '{}' not found in index", label))),
crate::index::DataFrameIndex::Multi(midx) if !midx.is_empty() => {
Err(Error::InvalidValue(format!(
"Label '{}' cannot be resolved against a multi-level index; use tuple-based \
selection instead",
label
)))
}
_ => {
let pos = label.parse::<usize>().map_err(|_| {
Error::InvalidValue(format!("Label '{}' not found in index", label))
})?;
if pos >= df.row_count() {
return Err(Error::InvalidValue(format!(
"Label '{}' not found in index",
label
)));
}
Ok(pos)
}
}
}
fn set_mutated_column(
dst: &mut DataFrame,
src: &DataFrame,
name: &str,
row: usize,
value: &str,
) -> Result<()> {
if let Ok(series) = src.get_column::<String>(name) {
let mut values = series.values().to_vec();
if row >= values.len() {
return Err(Error::IndexOutOfBounds {
index: row,
size: values.len(),
});
}
values[row] = value.to_string();
dst.add_column(
name.to_string(),
Series::new(values, Some(name.to_string()))?,
)?;
return Ok(());
}
macro_rules! try_mutate_numeric {
($ty:ty) => {
if let Ok(series) = src.get_column::<$ty>(name) {
let mut values: Vec<$ty> = series.values().to_vec();
if row >= values.len() {
return Err(Error::IndexOutOfBounds {
index: row,
size: values.len(),
});
}
values[row] = value.trim().parse::<$ty>().map_err(|_| {
Error::InvalidValue(format!(
"Cannot set value '{}' into column '{}': not a valid {}",
value,
name,
stringify!($ty)
))
})?;
dst.add_column(
name.to_string(),
Series::new(values, Some(name.to_string()))?,
)?;
return Ok(());
}
};
}
try_mutate_numeric!(i64);
try_mutate_numeric!(f64);
try_mutate_numeric!(i32);
try_mutate_numeric!(f32);
if let Ok(series) = src.get_column::<bool>(name) {
let mut values: Vec<bool> = series.values().to_vec();
if row >= values.len() {
return Err(Error::IndexOutOfBounds {
index: row,
size: values.len(),
});
}
values[row] = match value.trim().to_lowercase().as_str() {
"true" | "1" => true,
"false" | "0" => false,
_ => {
return Err(Error::InvalidValue(format!(
"Cannot set value '{}' into column '{}': not a valid bool",
value, name
)))
}
};
dst.add_column(
name.to_string(),
Series::new(values, Some(name.to_string()))?,
)?;
return Ok(());
}
if !src.contains_column(name) {
return Err(Error::ColumnNotFound(name.to_string()));
}
Err(Error::NotImplemented(format!(
"Mutating column '{}' via .at/.iat is not supported for its element type",
name
)))
}
fn set_cell_value(df: &DataFrame, column: &str, row: usize, value: &str) -> Result<DataFrame> {
if !df.contains_column(column) {
return Err(Error::ColumnNotFound(column.to_string()));
}
if row >= df.row_count() {
return Err(Error::IndexOutOfBounds {
index: row,
size: df.row_count(),
});
}
let all_positions: Vec<usize> = (0..df.row_count()).collect();
let mut result = DataFrame::new();
for name in df.column_names() {
if name == column {
set_mutated_column(&mut result, df, name, row, value)?;
} else {
gather_column_typed(&mut result, df, name, &all_positions)?;
}
}
match df.get_index() {
crate::index::DataFrameIndex::Simple(idx) if !idx.is_empty() => {
DataFrame::set_index(&mut result, idx)?;
}
crate::index::DataFrameIndex::Multi(midx) if !midx.is_empty() => {
DataFrame::set_multi_index(&mut result, midx)?;
}
_ => {}
}
Ok(result)
}
pub struct ILocIndexer<'a> {
dataframe: &'a DataFrame,
}
impl<'a> ILocIndexer<'a> {
pub fn new(dataframe: &'a DataFrame) -> Self {
Self { dataframe }
}
pub fn get(&self, row: usize) -> Result<HashMap<String, String>> {
if row >= self.dataframe.row_count() {
return Err(Error::IndexOutOfBounds {
index: row,
size: self.dataframe.row_count(),
});
}
let mut result = HashMap::new();
for col_name in self.dataframe.column_names() {
let value = get_cell_string(self.dataframe, col_name, row)?;
result.insert(col_name.to_string(), value);
}
Ok(result)
}
pub fn get_at(&self, row: usize, col: usize) -> Result<String> {
let col_names = self.dataframe.column_names();
if col >= col_names.len() {
return Err(Error::IndexOutOfBounds {
index: col,
size: col_names.len(),
});
}
if row >= self.dataframe.row_count() {
return Err(Error::IndexOutOfBounds {
index: row,
size: self.dataframe.row_count(),
});
}
get_cell_string(self.dataframe, &col_names[col], row)
}
pub fn get_range(&self, rows: Range<usize>) -> Result<DataFrame> {
self.select_rows(RowSelector::Range(IndexRange::Standard {
start: rows.start,
end: rows.end,
}))
}
pub fn get_slice(&self, rows: Range<usize>, cols: Range<usize>) -> Result<DataFrame> {
let result = self.get_range(rows)?;
let col_names = self.dataframe.column_names();
let selected_cols: Vec<String> = col_names
.iter()
.skip(cols.start)
.take(cols.end - cols.start)
.cloned()
.collect();
let col_refs: Vec<&str> = selected_cols.iter().map(|s| s.as_str()).collect();
result.select_columns(&col_refs)
}
pub fn get_positions(&self, positions: &[usize]) -> Result<DataFrame> {
self.select_rows(RowSelector::Positions(positions.to_vec()))
}
pub fn get_boolean(&self, mask: &[bool]) -> Result<DataFrame> {
self.select_rows(RowSelector::Boolean(mask.to_vec()))
}
fn select_rows(&self, selector: RowSelector) -> Result<DataFrame> {
let indices: Vec<usize> = match selector {
RowSelector::Range(range) => {
let (start, end) = match range {
IndexRange::Standard { start, end } => (start, end),
IndexRange::From { start } => (start, self.dataframe.row_count()),
IndexRange::To { end } => (0, end),
IndexRange::Full => (0, self.dataframe.row_count()),
IndexRange::Inclusive { start, end } => (start, end + 1),
IndexRange::ToInclusive { end } => (0, end + 1),
};
(start..end.min(self.dataframe.row_count())).collect()
}
RowSelector::Positions(positions) => positions,
RowSelector::Boolean(mask) => mask
.iter()
.enumerate()
.filter_map(|(i, &include)| if include { Some(i) } else { None })
.collect(),
_ => {
return Err(Error::InvalidValue(
"Unsupported selector for iloc".to_string(),
))
}
};
select_rows_by_positions(self.dataframe, &indices)
}
}
pub struct LocIndexer<'a> {
dataframe: &'a DataFrame,
index: Option<&'a MultiLevelIndex>,
}
impl<'a> LocIndexer<'a> {
pub fn new(dataframe: &'a DataFrame) -> Self {
Self {
dataframe,
index: None,
}
}
pub fn with_index(dataframe: &'a DataFrame, index: &'a MultiLevelIndex) -> Self {
Self {
dataframe,
index: Some(index),
}
}
pub fn get(&self, label: &str) -> Result<HashMap<String, String>> {
let position = self.find_label_position(label)?;
let iloc = ILocIndexer::new(self.dataframe);
iloc.get(position)
}
pub fn get_at(&self, label: &str, column: &str) -> Result<String> {
let position = self.find_label_position(label)?;
get_cell_string(self.dataframe, column, position)
}
pub fn get_labels(&self, labels: &[String]) -> Result<DataFrame> {
let positions: Result<Vec<usize>> = labels
.iter()
.map(|label| self.find_label_position(label))
.collect();
let iloc = ILocIndexer::new(self.dataframe);
iloc.get_positions(&positions?)
}
pub fn get_tuple(&self, tuple: &[String]) -> Result<DataFrame> {
if let Some(index) = self.index {
let positions = index.find_tuple(tuple);
if positions.is_empty() {
return Err(Error::InvalidValue(format!("Tuple {:?} not found", tuple)));
}
let iloc = ILocIndexer::new(self.dataframe);
iloc.get_positions(&positions)
} else {
Err(Error::InvalidValue(
"Multi-level index required for tuple selection".to_string(),
))
}
}
fn find_label_position(&self, label: &str) -> Result<usize> {
resolve_label_position(self.dataframe, label)
}
}
pub struct AtIndexer<'a> {
dataframe: &'a DataFrame,
}
impl<'a> AtIndexer<'a> {
pub fn new(dataframe: &'a DataFrame) -> Self {
Self { dataframe }
}
pub fn get(&self, label: &str, column: &str) -> Result<String> {
let loc = LocIndexer::new(self.dataframe);
loc.get_at(label, column)
}
pub fn set(&self, label: &str, column: &str, value: String) -> Result<DataFrame> {
let position = resolve_label_position(self.dataframe, label)?;
set_cell_value(self.dataframe, column, position, &value)
}
}
pub struct IAtIndexer<'a> {
dataframe: &'a DataFrame,
}
impl<'a> IAtIndexer<'a> {
pub fn new(dataframe: &'a DataFrame) -> Self {
Self { dataframe }
}
pub fn get(&self, row: usize, col: usize) -> Result<String> {
let iloc = ILocIndexer::new(self.dataframe);
iloc.get_at(row, col)
}
pub fn set(&self, row: usize, col: usize, value: String) -> Result<DataFrame> {
let col_names = self.dataframe.column_names();
if col >= col_names.len() {
return Err(Error::IndexOutOfBounds {
index: col,
size: col_names.len(),
});
}
let column = col_names[col].clone();
set_cell_value(self.dataframe, &column, row, &value)
}
}
pub struct SelectionBuilder<'a> {
dataframe: &'a DataFrame,
row_selector: Option<RowSelector>,
column_selector: Option<ColumnSelector>,
}
impl<'a> SelectionBuilder<'a> {
pub fn new(dataframe: &'a DataFrame) -> Self {
Self {
dataframe,
row_selector: None,
column_selector: None,
}
}
pub fn rows(mut self, selector: RowSelector) -> Self {
self.row_selector = Some(selector);
self
}
pub fn columns(mut self, selector: ColumnSelector) -> Self {
self.column_selector = Some(selector);
self
}
pub fn select(self) -> Result<DataFrame> {
let mut result = match self.row_selector {
Some(row_selector) => {
let iloc = ILocIndexer::new(self.dataframe);
iloc.select_rows(row_selector)?
}
None => self.dataframe.clone(),
};
if let Some(column_selector) = self.column_selector {
match column_selector {
ColumnSelector::Single(col) => {
result = result.select_columns(&[&col])?;
}
ColumnSelector::Multiple(cols) => {
let col_refs: Vec<&str> = cols.iter().map(|s| s.as_str()).collect();
result = result.select_columns(&col_refs)?;
}
ColumnSelector::All => {
}
}
}
Ok(result)
}
}
pub struct IndexAligner;
impl IndexAligner {
pub fn align(
left: &DataFrame,
right: &DataFrame,
strategy: AlignmentStrategy,
) -> Result<(DataFrame, DataFrame)> {
let left_len = left.row_count();
let right_len = right.row_count();
match strategy {
AlignmentStrategy::Outer => {
let max_len = left_len.max(right_len);
let aligned_left = Self::extend_dataframe(left, max_len)?;
let aligned_right = Self::extend_dataframe(right, max_len)?;
Ok((aligned_left, aligned_right))
}
AlignmentStrategy::Inner => {
let min_len = left_len.min(right_len);
let aligned_left = Self::truncate_dataframe(left, min_len)?;
let aligned_right = Self::truncate_dataframe(right, min_len)?;
Ok((aligned_left, aligned_right))
}
AlignmentStrategy::Left => {
let aligned_right = if right_len < left_len {
Self::extend_dataframe(right, left_len)?
} else {
Self::truncate_dataframe(right, left_len)?
};
Ok((left.clone(), aligned_right))
}
AlignmentStrategy::Right => {
let aligned_left = if left_len < right_len {
Self::extend_dataframe(left, right_len)?
} else {
Self::truncate_dataframe(left, right_len)?
};
Ok((aligned_left, right.clone()))
}
}
}
fn extend_dataframe(df: &DataFrame, target_len: usize) -> Result<DataFrame> {
let current_len = df.row_count();
if current_len >= target_len {
return Ok(df.clone());
}
let pad_len = target_len - current_len;
let mut result = DataFrame::new();
for col_name in df.column_names() {
if df.is_numeric_column(col_name) {
let mut values = df.get_column_numeric_values(col_name)?;
values.extend(std::iter::repeat(f64::NAN).take(pad_len));
result.add_column(
col_name.clone(),
Series::new(values, Some(col_name.clone()))?,
)?;
} else {
return Err(Error::NotImplemented(format!(
"extend_dataframe: column '{}' is not numeric, so it cannot be NA-padded \
from {} to {} rows; only numeric columns (upcast to f64 with NaN) support \
alignment padding",
col_name, current_len, target_len
)));
}
}
Ok(result)
}
fn truncate_dataframe(df: &DataFrame, target_len: usize) -> Result<DataFrame> {
if df.row_count() <= target_len {
return Ok(df.clone());
}
let iloc = ILocIndexer::new(df);
iloc.get_range(0..target_len)
}
pub fn reindex(df: &DataFrame, new_index: &[String]) -> Result<DataFrame> {
let positions: Vec<Option<usize>> = new_index
.iter()
.map(|label| resolve_label_position(df, label).ok())
.collect();
let any_missing = positions.iter().any(Option::is_none);
let resolved_positions: Option<Vec<usize>> = if any_missing {
None
} else {
Some(positions.iter().map(|p| p.unwrap_or(0)).collect())
};
let mut result = DataFrame::new();
for col_name in df.column_names() {
if let Some(resolved) = &resolved_positions {
gather_column_typed(&mut result, df, col_name, resolved)?;
continue;
}
if df.is_numeric_column(col_name) {
let current = df.get_column_numeric_values(col_name)?;
let values: Vec<f64> = positions
.iter()
.map(|pos| pos.map(|p| current[p]).unwrap_or(f64::NAN))
.collect();
result.add_column(
col_name.clone(),
Series::new(values, Some(col_name.clone()))?,
)?;
} else {
return Err(Error::NotImplemented(format!(
"reindex: column '{}' is not numeric and at least one requested label is \
absent from the source index, so the missing rows cannot be NA-filled for \
it; numeric columns upcast to f64 with NaN for missing rows",
col_name
)));
}
}
let row_labels = crate::index::Index::new(new_index.to_vec())?;
DataFrame::set_index(&mut result, row_labels)?;
Ok(result)
}
}
pub trait AdvancedIndexingExt {
fn iloc(&self) -> ILocIndexer;
fn loc(&self) -> LocIndexer;
fn at(&self) -> AtIndexer;
fn iat(&self) -> IAtIndexer;
fn select(&self) -> SelectionBuilder;
fn reset_index(&self) -> Result<DataFrame>;
fn set_index(&self, column: &str) -> Result<DataFrame>;
fn set_multi_index(&self, columns: &[String]) -> Result<(DataFrame, MultiLevelIndex)>;
fn select_columns(&self, columns: &[String]) -> Result<DataFrame>;
fn drop_columns(&self, columns: &[String]) -> Result<DataFrame>;
fn sample(&self, n: usize) -> Result<DataFrame>;
fn head(&self, n: usize) -> Result<DataFrame>;
fn tail(&self, n: usize) -> Result<DataFrame>;
}
impl AdvancedIndexingExt for DataFrame {
fn iloc(&self) -> ILocIndexer {
ILocIndexer::new(self)
}
fn loc(&self) -> LocIndexer {
LocIndexer::new(self)
}
fn at(&self) -> AtIndexer {
AtIndexer::new(self)
}
fn iat(&self) -> IAtIndexer {
IAtIndexer::new(self)
}
fn select(&self) -> SelectionBuilder {
SelectionBuilder::new(self)
}
fn reset_index(&self) -> Result<DataFrame> {
let index = self.get_index();
let row_count = self.row_count();
let mut result = DataFrame::new();
match &index {
crate::index::DataFrameIndex::Simple(idx) if !idx.is_empty() => {
let col_name = idx.name().cloned().unwrap_or_else(|| "index".to_string());
let values: Vec<String> = idx.values().to_vec();
result.add_column(col_name.clone(), Series::new(values, Some(col_name))?)?;
}
crate::index::DataFrameIndex::Multi(midx) if !midx.is_empty() => {
let names = midx.names().to_vec();
for level in 0..midx.n_levels() {
let col_name = names
.get(level)
.and_then(|n| n.clone())
.unwrap_or_else(|| format!("level_{}", level));
let mut values = Vec::with_capacity(midx.len());
for i in 0..midx.len() {
let tuple = midx.get_tuple(i).ok_or(Error::IndexOutOfBounds {
index: i,
size: midx.len(),
})?;
values.push(tuple.get(level).cloned().unwrap_or_default());
}
result.add_column(col_name.clone(), Series::new(values, Some(col_name))?)?;
}
}
_ => {
let values: Vec<i64> = (0..row_count as i64).collect();
result.add_column(
"index".to_string(),
Series::new(values, Some("index".to_string()))?,
)?;
}
}
let all_positions: Vec<usize> = (0..row_count).collect();
for col_name in self.column_names() {
gather_column_typed(&mut result, self, col_name, &all_positions)?;
}
Ok(result)
}
fn set_index(&self, column: &str) -> Result<DataFrame> {
if !self.contains_column(column) {
return Err(Error::ColumnNotFound(column.to_string()));
}
let label_values = self.get_column_string_values(column)?;
let new_index = crate::index::Index::with_name(label_values, Some(column.to_string()))?;
let mut result = self.drop_columns(&[column.to_string()])?;
DataFrame::set_index(&mut result, new_index)?;
Ok(result)
}
fn set_multi_index(&self, columns: &[String]) -> Result<(DataFrame, MultiLevelIndex)> {
let mut level_values = Vec::new();
let names = columns.to_vec();
for col_name in columns {
let values = self.get_column_string_values(col_name)?;
level_values.push(values);
}
let multi_index = MultiLevelIndex::new(names.clone(), level_values)?;
let result_df = self.drop_columns(columns)?;
Ok((result_df, multi_index))
}
fn select_columns(&self, columns: &[String]) -> Result<DataFrame> {
let column_refs: Vec<&str> = columns.iter().map(|s| s.as_str()).collect();
self.select_columns(&column_refs)
}
fn drop_columns(&self, columns: &[String]) -> Result<DataFrame> {
let to_drop: HashSet<&str> = columns.iter().map(|s| s.as_str()).collect();
let to_keep: Vec<&str> = self
.column_names()
.iter()
.map(|s| s.as_str())
.filter(|name| !to_drop.contains(name))
.collect();
self.select_columns(&to_keep)
}
fn sample(&self, n: usize) -> Result<DataFrame> {
use scirs2_core::random::SliceRandom;
let row_count = self.row_count();
if n >= row_count {
return Ok(self.clone());
}
let mut indices: Vec<usize> = (0..row_count).collect();
indices.shuffle(&mut scirs2_core::random::rng());
indices.truncate(n);
let iloc = self.iloc();
iloc.get_positions(&indices)
}
fn head(&self, n: usize) -> Result<DataFrame> {
let iloc = self.iloc();
iloc.get_range(0..n.min(self.row_count()))
}
fn tail(&self, n: usize) -> Result<DataFrame> {
let row_count = self.row_count();
let start = if n >= row_count { 0 } else { row_count - n };
let iloc = self.iloc();
iloc.get_range(start..row_count)
}
}
pub mod selectors {
use super::*;
pub fn row(index: String) -> RowSelector {
RowSelector::Single(index)
}
pub fn rows(indices: Vec<String>) -> RowSelector {
RowSelector::Multiple(indices)
}
pub fn pos(position: usize) -> RowSelector {
RowSelector::Position(position)
}
pub fn positions(positions: Vec<usize>) -> RowSelector {
RowSelector::Positions(positions)
}
pub fn mask(mask: Vec<bool>) -> RowSelector {
RowSelector::Boolean(mask)
}
pub fn col(name: String) -> ColumnSelector {
ColumnSelector::Single(name)
}
pub fn cols(names: Vec<String>) -> ColumnSelector {
ColumnSelector::Multiple(names)
}
pub fn range(start: usize, end: usize) -> RowSelector {
RowSelector::Range(IndexRange::Standard { start, end })
}
pub fn range_inclusive(start: usize, end: usize) -> RowSelector {
RowSelector::Range(IndexRange::Inclusive { start, end })
}
}
#[macro_export]
macro_rules! iloc {
($df:expr, $row:expr) => {
$df.iloc().get($row)
};
($df:expr, $row:expr, $col:expr) => {
$df.iloc().get_at($row, $col)
};
($df:expr, $rows:expr, $cols:expr) => {
$df.iloc().get_slice($rows, $cols)
};
}
#[macro_export]
macro_rules! loc {
($df:expr, $label:expr) => {
$df.loc().get($label)
};
($df:expr, $label:expr, $col:expr) => {
$df.loc().get_at($label, $col)
};
}
#[macro_export]
macro_rules! select {
($df:expr, rows: $rows:expr) => {
$df.select().rows($rows).select()
};
($df:expr, cols: $cols:expr) => {
$df.select().columns($cols).select()
};
($df:expr, rows: $rows:expr, cols: $cols:expr) => {
$df.select().rows($rows).columns($cols).select()
};
}