use crate::column::{BooleanColumn, Column, ColumnTrait, Float64Column, Int64Column, StringColumn};
use crate::error::{Error, Result};
use crate::index::DataFrameIndex;
use crate::optimized::dataframe::OptimizedDataFrame;
use crate::optimized::split_dataframe::core::OptimizedDataFrame as SplitDataFrame;
fn build_column_from_series(df: &crate::dataframe::DataFrame, col_name: &str) -> Result<Column> {
if let Ok(col) = df.get_column::<String>(col_name) {
let values: Vec<String> = col.values().to_vec();
let non_empty_values: Vec<&String> = values.iter().filter(|s| !s.is_empty()).collect();
if non_empty_values.is_empty() {
let nulls = vec![true; values.len()];
return Ok(Column::String(StringColumn::with_nulls(values, nulls)));
}
let all_ints = non_empty_values.iter().all(|&s| s.parse::<i64>().is_ok());
if all_ints {
let mut int_values = Vec::with_capacity(values.len());
let mut nulls = Vec::with_capacity(values.len());
for s in &values {
if s.is_empty() {
int_values.push(0);
nulls.push(true);
} else {
let parsed = s.parse::<i64>().map_err(|e| {
Error::Cast(format!(
"Column '{}': failed to parse '{}' as i64: {}",
col_name, s, e
))
})?;
int_values.push(parsed);
nulls.push(false);
}
}
return Ok(Column::Int64(Int64Column::with_nulls(int_values, nulls)));
}
let all_floats = non_empty_values.iter().all(|&s| s.parse::<f64>().is_ok());
if all_floats {
let mut float_values = Vec::with_capacity(values.len());
let mut nulls = Vec::with_capacity(values.len());
for s in &values {
if s.is_empty() {
float_values.push(0.0);
nulls.push(true);
} else {
let parsed = s.parse::<f64>().map_err(|e| {
Error::Cast(format!(
"Column '{}': failed to parse '{}' as f64: {}",
col_name, s, e
))
})?;
float_values.push(parsed);
nulls.push(false);
}
}
return Ok(Column::Float64(Float64Column::with_nulls(
float_values,
nulls,
)));
}
let all_bools = non_empty_values.iter().all(|&s| {
let lower = s.to_lowercase();
lower == "true" || lower == "false" || lower == "1" || lower == "0"
});
if all_bools {
let mut bool_values = Vec::with_capacity(values.len());
let mut nulls = Vec::with_capacity(values.len());
for s in &values {
if s.is_empty() {
bool_values.push(false);
nulls.push(true);
} else {
let lower = s.to_lowercase();
bool_values.push(lower == "true" || lower == "1");
nulls.push(false);
}
}
return Ok(Column::Boolean(BooleanColumn::with_nulls(
bool_values,
nulls,
)));
}
let nulls: Vec<bool> = values.iter().map(|s| s.is_empty()).collect();
return Ok(Column::String(StringColumn::with_nulls(values, nulls)));
}
if let Ok(col) = df.get_column::<f64>(col_name) {
let mut values = Vec::with_capacity(col.len());
let mut nulls = Vec::with_capacity(col.len());
for &v in col.values() {
let is_null = v.is_nan();
nulls.push(is_null);
values.push(if is_null { 0.0 } else { v });
}
return Ok(Column::Float64(Float64Column::with_nulls(values, nulls)));
}
if let Ok(col) = df.get_column::<f32>(col_name) {
let mut values = Vec::with_capacity(col.len());
let mut nulls = Vec::with_capacity(col.len());
for &v in col.values() {
let is_null = v.is_nan();
nulls.push(is_null);
values.push(if is_null { 0.0 } else { v as f64 });
}
return Ok(Column::Float64(Float64Column::with_nulls(values, nulls)));
}
if let Ok(col) = df.get_column::<bool>(col_name) {
return Ok(Column::Boolean(BooleanColumn::new(col.values().to_vec())));
}
if let Ok(col) = df.get_column::<i64>(col_name) {
return Ok(Column::Int64(Int64Column::new(col.values().to_vec())));
}
if let Ok(col) = df.get_column::<i32>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<i16>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<i8>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<u32>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<u16>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<u8>(col_name) {
let values: Vec<i64> = col.values().iter().map(|&v| v as i64).collect();
return Ok(Column::Int64(Int64Column::new(values)));
}
if let Ok(col) = df.get_column::<u64>(col_name) {
let mut values = Vec::with_capacity(col.len());
for &v in col.values() {
let converted = i64::try_from(v).map_err(|_| {
Error::Cast(format!(
"Column '{}' contains a u64 value {} that does not fit in i64; \
the OptimizedDataFrame bridge has no unsigned 64-bit column type",
col_name, v
))
})?;
values.push(converted);
}
return Ok(Column::Int64(Int64Column::new(values)));
}
Err(Error::NotImplemented(format!(
"Column '{}' has an element type that the DataFrame -> OptimizedDataFrame bridge does \
not support (supported element types: String, bool, 8/16/32/64-bit signed or \
unsigned integers, and 32/64-bit floats)",
col_name
)))
}
pub(crate) fn from_standard_dataframe(
df: &crate::dataframe::DataFrame,
) -> Result<OptimizedDataFrame> {
let mut split_df = SplitDataFrame::new();
for col_name in df.column_names() {
let column = build_column_from_series(df, &col_name)?;
split_df.add_column(col_name.clone(), column)?;
}
let df_index = df.get_index();
match df_index {
DataFrameIndex::Simple(simple_index)
if simple_index.len() == 0 && split_df.row_count() > 0 =>
{
split_df.set_default_index()?;
}
DataFrameIndex::Simple(simple_index) => {
split_df.set_index_from_simple_index(simple_index.clone())?;
}
DataFrameIndex::Multi(multi_index) => {
split_df.set_index(DataFrameIndex::Multi(multi_index.clone()))?;
}
}
let mut opt_df = OptimizedDataFrame::new();
for name in split_df.column_names() {
if let Ok(column_view) = split_df.column(name) {
let column = column_view.column().clone();
opt_df.add_column(name.clone(), column)?;
}
}
if let Some(split_index) = split_df.get_index() {
opt_df.set_index_directly(split_index.clone())?;
}
Ok(opt_df)
}
pub(crate) fn to_standard_dataframe(
df: &OptimizedDataFrame,
) -> Result<crate::dataframe::DataFrame> {
let mut split_df = SplitDataFrame::new();
for col_name in df.column_names() {
let col_view = df.column(col_name)?;
let col = col_view.column();
split_df.add_column(col_name.clone(), col.clone())?;
}
if let Some(df_index) = df.get_index() {
if let DataFrameIndex::Simple(simple_index) = df_index {
split_df.set_index_from_simple_index(simple_index.clone())?;
} else if let DataFrameIndex::Multi(multi_index) = df_index {
split_df.set_index(DataFrameIndex::Multi(multi_index.clone()))?;
}
}
let mut std_df = crate::dataframe::DataFrame::new();
for col_name in split_df.column_names() {
let col_view = split_df.column(col_name)?;
let col = col_view.column();
match col {
Column::Int64(int_col) => {
let mut opts: Vec<Option<i64>> = Vec::with_capacity(int_col.len());
for i in 0..int_col.len() {
opts.push(int_col.get(i)?);
}
if opts.iter().any(Option::is_none) {
let values: Vec<f64> = opts
.into_iter()
.map(|v| v.map(|x| x as f64).unwrap_or(f64::NAN))
.collect();
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
} else {
let values: Vec<i64> = opts.into_iter().map(|v| v.unwrap_or(0)).collect();
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
}
}
Column::Float64(float_col) => {
let mut values = Vec::with_capacity(float_col.len());
for i in 0..float_col.len() {
values.push(float_col.get(i)?.unwrap_or(f64::NAN));
}
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
}
Column::String(str_col) => {
let mut values = Vec::with_capacity(str_col.len());
for i in 0..str_col.len() {
values.push(str_col.get(i)?.map(|s| s.to_string()).unwrap_or_default());
}
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
}
Column::Boolean(bool_col) => {
let mut opts: Vec<Option<bool>> = Vec::with_capacity(bool_col.len());
for i in 0..bool_col.len() {
opts.push(bool_col.get(i)?);
}
if opts.iter().any(Option::is_none) {
let values: Vec<String> = opts
.into_iter()
.map(|v| match v {
Some(true) => "true".to_string(),
Some(false) => "false".to_string(),
None => String::new(),
})
.collect();
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
} else {
let values: Vec<bool> = opts.into_iter().map(|v| v.unwrap_or(false)).collect();
let series = crate::series::Series::new(values, Some(col_name.clone()))?;
std_df.add_column(col_name.clone(), series)?;
}
}
}
}
if let Some(split_index) = split_df.get_index() {
match split_index {
DataFrameIndex::Simple(simple_index) => {
std_df.set_index(simple_index.clone())?;
}
DataFrameIndex::Multi(multi_index) => {
std_df.set_multi_index(multi_index.clone())?;
}
}
}
Ok(std_df)
}
pub fn optimize_dataframe(df: &crate::dataframe::DataFrame) -> Result<OptimizedDataFrame> {
from_standard_dataframe(df)
}
pub fn standard_dataframe(df: &OptimizedDataFrame) -> Result<crate::dataframe::DataFrame> {
to_standard_dataframe(df)
}