use std::collections::HashSet;
use crate::column::{BooleanColumn, Column, Float64Column, Int64Column, StringColumn};
use crate::core::error::OptionExt;
use crate::error::Result;
use crate::index::DataFrameIndex;
use crate::optimized::split_dataframe::core::OptimizedDataFrame;
const PARALLEL_TAKE_THRESHOLD: usize = 8192;
impl OptimizedDataFrame {
pub fn select_columns(&self, columns: &[&str]) -> Result<Self> {
let mut df = Self::new();
let column_set: HashSet<&str> = self.column_names.iter().map(|s| s.as_str()).collect();
for &col_name in columns {
if !column_set.contains(col_name) {
return Err(crate::error::Error::ColumnNotFound(col_name.to_string()));
}
let col_idx = self
.column_indices
.get(col_name)
.ok_or_column_error(col_name)?;
let column = &self.columns[*col_idx];
df.add_column(col_name.to_string(), column.clone())?;
}
if let Some(ref index) = self.index {
df.index = Some(index.clone());
}
Ok(df)
}
pub fn select_rows_by_indices(&self, indices: &[usize]) -> Result<Self> {
let mut df = take_rows(self, indices)?;
if df.index.is_none() {
df.set_default_index()?;
}
Ok(df)
}
pub fn select_rows_columns(&self, row_indices: &[usize], columns: &[&str]) -> Result<Self> {
let cols_selected = self.select_columns(columns)?;
cols_selected.select_rows_by_indices(row_indices)
}
pub fn select_by_mask(&self, mask: &[bool]) -> Result<Self> {
if mask.len() != self.row_count {
return Err(crate::error::Error::Format(format!(
"Mask length ({}) does not match DataFrame row count ({})",
mask.len(),
self.row_count
)));
}
let indices: Vec<usize> = mask
.iter()
.enumerate()
.filter_map(|(i, &keep)| if keep { Some(i) } else { None })
.collect();
self.select_rows_by_indices(&indices)
}
}
pub(crate) fn take_rows(df: &OptimizedDataFrame, indices: &[usize]) -> Result<OptimizedDataFrame> {
let positions: Vec<usize> = if indices.iter().all(|&i| i < df.row_count) {
indices.to_vec()
} else {
indices
.iter()
.copied()
.filter(|&i| i < df.row_count)
.collect()
};
let mut result = OptimizedDataFrame::new();
let resolve = |name: &String| -> Result<Column> {
let column_idx = *df
.column_indices
.get(name)
.ok_or_else(|| crate::error::Error::ColumnNotFound(name.clone()))?;
let column = df
.columns
.get(column_idx)
.ok_or_else(|| crate::error::Error::ColumnNotFound(name.clone()))?;
Ok(take_column(column, &positions))
};
let workload = positions.len().saturating_mul(df.column_names.len());
let taken: Vec<Column> = if workload >= PARALLEL_TAKE_THRESHOLD {
use rayon::prelude::*;
df.column_names
.par_iter()
.map(&resolve)
.collect::<Result<Vec<Column>>>()?
} else {
df.column_names
.iter()
.map(&resolve)
.collect::<Result<Vec<Column>>>()?
};
for (name, column) in df.column_names.iter().zip(taken) {
result.add_column(name.clone(), column)?;
}
if !df.column_names.is_empty() {
if let Some(ref index) = df.index {
match index {
DataFrameIndex::Simple(simple_idx) => {
let values: Vec<String> = positions
.iter()
.map(|&pos| {
simple_idx
.get_value(pos)
.cloned()
.unwrap_or_else(|| pos.to_string())
})
.collect();
match crate::index::Index::with_name(values, simple_idx.name().cloned()) {
Ok(new_index) => result.set_index_from_simple_index(new_index)?,
Err(_) => result.set_default_index()?,
}
}
DataFrameIndex::Multi(multi_idx) => {
let tuples: Option<Vec<Vec<String>>> = positions
.iter()
.map(|&pos| multi_idx.get_tuple(pos))
.collect();
match tuples {
Some(tuples) if !tuples.is_empty() => {
let level_names: Vec<Option<String>> = multi_idx.names().to_vec();
match crate::index::MultiIndex::from_tuples(tuples, Some(level_names)) {
Ok(new_index) => result.set_index_from_multi_index(new_index)?,
Err(_) => result.set_default_index()?,
}
}
_ => result.set_default_index()?,
}
}
}
}
}
Ok(result)
}
pub(crate) fn take_column(column: &Column, positions: &[usize]) -> Column {
match column {
Column::Int64(col) => {
let mut values = Vec::with_capacity(positions.len());
let mut nulls = Vec::with_capacity(positions.len());
for &pos in positions {
match col.get(pos) {
Ok(Some(value)) => {
values.push(value);
nulls.push(false);
}
_ => {
values.push(0);
nulls.push(true);
}
}
}
let mut new_col = Int64Column::with_nulls(values, nulls);
if let Some(name) = col.get_name() {
new_col.set_name(name.to_string());
}
Column::Int64(new_col)
}
Column::Float64(col) => {
let mut values = Vec::with_capacity(positions.len());
let mut nulls = Vec::with_capacity(positions.len());
for &pos in positions {
match col.get(pos) {
Ok(Some(value)) => {
values.push(value);
nulls.push(false);
}
_ => {
values.push(0.0);
nulls.push(true);
}
}
}
let mut new_col = Float64Column::with_nulls(values, nulls);
if let Some(name) = col.get_name() {
new_col.set_name(name.to_string());
}
Column::Float64(new_col)
}
Column::String(col) => {
let mut values = Vec::with_capacity(positions.len());
let mut nulls = Vec::with_capacity(positions.len());
for &pos in positions {
match col.get(pos) {
Ok(Some(value)) => {
values.push(value.to_string());
nulls.push(false);
}
_ => {
values.push(String::new());
nulls.push(true);
}
}
}
let mut new_col = StringColumn::with_nulls(values, nulls);
if let Some(name) = col.get_name() {
new_col.set_name(name.to_string());
}
Column::String(new_col)
}
Column::Boolean(col) => {
let mut values = Vec::with_capacity(positions.len());
let mut nulls = Vec::with_capacity(positions.len());
for &pos in positions {
match col.get(pos) {
Ok(Some(value)) => {
values.push(value);
nulls.push(false);
}
_ => {
values.push(false);
nulls.push(true);
}
}
}
let mut new_col = BooleanColumn::with_nulls(values, nulls);
if let Some(name) = col.get_name() {
new_col.set_name(name.to_string());
}
Column::Boolean(new_col)
}
}
}
pub(crate) fn select_rows_by_indices_impl(
df: &OptimizedDataFrame,
indices: &[usize],
) -> Result<OptimizedDataFrame> {
take_rows(df, indices)
}