use crate::core::error::{Error, Result};
use crate::dataframe::base::DataFrame;
use crate::index::{DataFrameIndex, Index, MultiIndex};
use crate::series::Series;
pub(crate) fn take_rows(dataframe: &DataFrame, indices: &[usize]) -> Result<DataFrame> {
let mut result = DataFrame::new();
for column_name in dataframe.column_names() {
take_column(dataframe, &mut result, column_name, indices)?;
}
carry_index(dataframe, &mut result, indices)?;
Ok(result)
}
fn take_column(
source: &DataFrame,
result: &mut DataFrame,
column_name: &str,
indices: &[usize],
) -> Result<()> {
macro_rules! try_take {
($($ty:ty),+ $(,)?) => {
$(
if let Ok(series) = source.get_column::<$ty>(column_name) {
let values = series.values();
let mut taken: Vec<$ty> = Vec::with_capacity(indices.len());
for &idx in indices {
match values.get(idx) {
Some(value) => taken.push(value.clone()),
None => {
return Err(Error::IndexOutOfBounds {
index: idx,
size: values.len(),
})
}
}
}
result.add_column(
column_name.to_string(),
Series::new(taken, Some(column_name.to_string()))?,
)?;
return Ok(());
}
)+
};
}
try_take!(
String,
i64,
f64,
bool,
i32,
f32,
u64,
u32,
usize,
i16,
u16,
i8,
u8,
isize,
i128,
u128,
chrono::NaiveDateTime,
chrono::NaiveDate,
chrono::NaiveTime,
chrono::DateTime<chrono::Utc>,
);
let values = source.get_column_string_values(column_name)?;
let mut taken: Vec<String> = Vec::with_capacity(indices.len());
for &idx in indices {
match values.get(idx) {
Some(value) => taken.push(value.clone()),
None => {
return Err(Error::IndexOutOfBounds {
index: idx,
size: values.len(),
})
}
}
}
result.add_column(
column_name.to_string(),
Series::new(taken, Some(column_name.to_string()))?,
)?;
Ok(())
}
fn carry_index(source: &DataFrame, result: &mut DataFrame, indices: &[usize]) -> Result<()> {
let row_count = source.row_count();
if row_count == 0 {
return Ok(());
}
match source.get_index() {
DataFrameIndex::Simple(index) => {
if index.len() != row_count {
return Ok(());
}
let labels: Vec<String> = indices
.iter()
.filter_map(|&idx| index.get_value(idx).cloned())
.collect();
if labels.len() != indices.len() {
return Ok(());
}
let filtered = Index::with_name(labels, index.name().cloned())?;
result.set_index(filtered)?;
}
DataFrameIndex::Multi(multi) => {
if multi.len() != row_count {
return Ok(());
}
let levels = multi.levels().to_vec();
let mut codes = Vec::with_capacity(multi.codes().len());
for level_codes in multi.codes() {
let mut taken = Vec::with_capacity(indices.len());
for &idx in indices {
match level_codes.get(idx) {
Some(code) => taken.push(*code),
None => return Ok(()),
}
}
codes.push(taken);
}
let names = multi.names().to_vec();
let filtered = MultiIndex::new(levels, codes, Some(names))?;
result.set_multi_index(filtered)?;
}
}
Ok(())
}