use crate::data::column::ColumnMeta;
use crate::data::dataframe::DataFrame;
use polars::prelude::{Column, NamedFrom, Series};
const LABEL_COLUMN: &str = "column";
pub fn transpose_row(df: &DataFrame, physical_row: usize) -> Result<DataFrame, String> {
if physical_row >= df.df.height() {
return Err(format!(
"row {} is out of range; the table has {} rows",
physical_row,
df.df.height()
));
}
let names: Vec<String> = df.columns.iter().map(|c| c.name.clone()).collect();
let values: Vec<String> = (0..df.columns.len())
.map(|i| df.get_physical(physical_row, i))
.collect();
let pdf = polars::prelude::DataFrame::new_infer_height(vec![
Column::from(Series::new("Column".into(), &names)),
Column::from(Series::new("Value".into(), &values)),
])
.map_err(|e| e.to_string())?;
let mut out = DataFrame::from_parts(
pdf,
vec![
ColumnMeta::new("Column".to_string()),
ColumnMeta::new("Value".to_string()),
],
);
out.calc_widths(40, 500);
Ok(out)
}
pub fn is_transposed(df: &DataFrame) -> bool {
df.transposed
}
pub fn transpose_table(df: &DataFrame) -> Result<DataFrame, String> {
let ncols = df.columns.len();
let nrows = df.visible_row_count();
if ncols == 0 || nrows == 0 {
return Err("nothing to transpose: the table is empty".to_string());
}
let inverting = is_transposed(df);
let (row_labels, new_col_names, data_start): (Vec<String>, Vec<String>, usize) = if inverting {
(
df.columns[1..].iter().map(|c| c.name.clone()).collect(),
(0..nrows)
.map(|r| df.get_physical(df.row_order[r], 0))
.collect(),
1,
)
} else {
(
df.columns.iter().map(|c| c.name.clone()).collect(),
(0..nrows)
.map(|r| format!("row_{}", df.row_order[r]))
.collect(),
0,
)
};
let data_ncols = ncols - data_start;
let cells: Vec<Vec<String>> = (data_start..ncols)
.map(|i| {
(0..nrows)
.map(|r| df.get_physical(df.row_order[r], i))
.collect()
})
.collect();
let mut series_vec: Vec<Column> = Vec::with_capacity(new_col_names.len() + 1);
if !inverting {
series_vec.push(Column::from(Series::new(LABEL_COLUMN.into(), &row_labels)));
}
for (col_idx, name) in new_col_names.iter().enumerate() {
let values: Vec<String> = (0..data_ncols).map(|i| cells[i][col_idx].clone()).collect();
series_vec.push(Column::from(Series::new(name.as_str().into(), &values)));
}
let pdf =
polars::prelude::DataFrame::new_infer_height(series_vec).map_err(|e| e.to_string())?;
let mut metas: Vec<ColumnMeta> = if inverting {
new_col_names.iter().cloned().map(ColumnMeta::new).collect()
} else {
std::iter::once(LABEL_COLUMN.to_string())
.chain(new_col_names.iter().cloned())
.map(ColumnMeta::new)
.collect()
};
if !inverting {
if let Some(first) = metas.first_mut() {
first.pinned = true;
}
}
let mut out = DataFrame::from_parts(pdf, metas);
out.transposed = !inverting;
out.calc_widths(40, 500);
Ok(out)
}