Refactor TypedFrame methods using macros for common functionality and improve column accessors

This commit is contained in:
Palash Tyagi 2025-07-01 23:26:57 +01:00
parent 58acea8467
commit 8b6f16236a

View File

@ -13,60 +13,56 @@ pub enum TypedFrame {
// Add more types as needed
}
macro_rules! impl_typed_frame_common_methods {
($($method:ident $(($($arg:ident: $arg_ty:ty),*))? -> $ret_ty:ty),*) => {
impl TypedFrame {
$(
pub fn $method(&self $(, $($arg: $arg_ty),*)?) -> $ret_ty {
match self {
TypedFrame::F64(f) => f.$method($($($arg),*)?),
TypedFrame::I64(f) => f.$method($($($arg),*)?),
TypedFrame::Bool(f) => f.$method($($($arg),*)?),
TypedFrame::String(f) => f.$method($($($arg),*)?),
}
}
)*
}
};
}
impl_typed_frame_common_methods! {
rows -> usize,
cols -> usize,
columns -> &[String],
index -> &RowIndex
}
macro_rules! impl_typed_frame_column_accessors {
($fn_name:ident, $ret_type:ident, $frame_method:ident) => {
pub fn $fn_name(&self, name: &str) -> $ret_type<'_> {
match self {
TypedFrame::F64(f) => $ret_type::F64(f.$frame_method(name)),
TypedFrame::I64(f) => $ret_type::I64(f.$frame_method(name)),
TypedFrame::Bool(f) => $ret_type::Bool(f.$frame_method(name)),
TypedFrame::String(f) => $ret_type::String(f.$frame_method(name)),
}
}
};
($fn_name:ident, $ret_type:ident, $frame_method:ident, mut) => {
pub fn $fn_name(&mut self, name: &str) -> $ret_type<'_> {
match self {
TypedFrame::F64(f) => $ret_type::F64(f.$frame_method(name)),
TypedFrame::I64(f) => $ret_type::I64(f.$frame_method(name)),
TypedFrame::Bool(f) => $ret_type::Bool(f.$frame_method(name)),
TypedFrame::String(f) => $ret_type::String(f.$frame_method(name)),
}
}
};
}
impl TypedFrame {
pub fn rows(&self) -> usize {
match self {
TypedFrame::F64(f) => f.rows(),
TypedFrame::I64(f) => f.rows(),
TypedFrame::Bool(f) => f.rows(),
TypedFrame::String(f) => f.rows(),
}
}
pub fn cols(&self) -> usize {
match self {
TypedFrame::F64(f) => f.cols(),
TypedFrame::I64(f) => f.cols(),
TypedFrame::Bool(f) => f.cols(),
TypedFrame::String(f) => f.cols(),
}
}
pub fn columns(&self) -> &[String] {
match self {
TypedFrame::F64(f) => f.columns(),
TypedFrame::I64(f) => f.columns(),
TypedFrame::Bool(f) => f.columns(),
TypedFrame::String(f) => f.columns(),
}
}
pub fn index(&self) -> &RowIndex {
match self {
TypedFrame::F64(f) => f.index(),
TypedFrame::I64(f) => f.index(),
TypedFrame::Bool(f) => f.index(),
TypedFrame::String(f) => f.index(),
}
}
/// Returns an immutable slice of the specified column's data by name.
/// Panics if the column name is not found.
pub fn column(&self, name: &str) -> &[f64] {
match self {
TypedFrame::F64(f) => f.column(name),
_ => panic!("Column type mismatch"),
}
}
/// Returns a mutable slice of the specified column's data by name.
/// Panics if the column name is not found.
pub fn column_mut(&mut self, name: &str) -> &mut [f64] {
match self {
TypedFrame::F64(f) => f.column_mut(name),
_ => panic!("Column type mismatch"),
}
}
impl_typed_frame_column_accessors!(column, DataFrameColumn, column);
impl_typed_frame_column_accessors!(column_mut, DataFrameColumnMut, column_mut, mut);
}
/// Represents a view of a single column within a DataFrame.
@ -443,45 +439,36 @@ impl DataFrame {
self.column_names.remove(pos);
}
let mut subframe = self.subframes.get_mut(&subframe_id).unwrap_or_else(|| {
let subframe = self.subframes.get_mut(&subframe_id).unwrap_or_else(|| {
panic!(
"DataFrame::delete_column: internal error, subframe ID {} not found",
subframe_id
)
});
macro_rules! delete_column_from_typed_frame {
($frame_type:ident, $frame_variant:ident, $f:ident, $col_name:expr) => {{
let data = $f.delete_column(&$col_name);
TypedFrame::$frame_variant(Frame::new(
Matrix::from_cols(vec![data]),
vec![$col_name.clone()],
Some($f.index().clone()),
))
}};
}
let deleted_data_frame = match subframe {
TypedFrame::F64(f) => {
let data = f.delete_column(&col_in_subframe_name);
TypedFrame::F64(Frame::new(
Matrix::from_cols(vec![data]),
vec![col_in_subframe_name.clone()],
Some(f.index().clone()),
))
delete_column_from_typed_frame!(f64, F64, f, col_in_subframe_name)
}
TypedFrame::I64(f) => {
let data = f.delete_column(&col_in_subframe_name);
TypedFrame::I64(Frame::new(
Matrix::from_cols(vec![data]),
vec![col_in_subframe_name.clone()],
Some(f.index().clone()),
))
delete_column_from_typed_frame!(i64, I64, f, col_in_subframe_name)
}
TypedFrame::Bool(f) => {
let data = f.delete_column(&col_in_subframe_name);
TypedFrame::Bool(Frame::new(
Matrix::from_cols(vec![data]),
vec![col_in_subframe_name.clone()],
Some(f.index().clone()),
))
delete_column_from_typed_frame!(bool, Bool, f, col_in_subframe_name)
}
TypedFrame::String(f) => {
let data = f.delete_column(&col_in_subframe_name);
TypedFrame::String(Frame::new(
Matrix::from_cols(vec![data]),
vec![col_in_subframe_name.clone()],
Some(f.index().clone()),
))
delete_column_from_typed_frame!(String, String, f, col_in_subframe_name)
}
};
@ -654,6 +641,300 @@ mod tests {
}
}
#[test]
fn test_dataframe_rows_cols_columns_index() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0, 3.0], None);
let col_b = create_i64_typed_frame("B", vec![4, 5, 6], None);
let df = DataFrame::new(
vec![col_a, col_b],
vec!["A".to_string(), "B".to_string()],
None,
);
assert_eq!(df.rows(), 3);
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["A", "B"]);
assert_eq!(df.index(), &RowIndex::Range(0..3));
let empty_df = DataFrame::new(vec![], vec![], None);
assert_eq!(empty_df.rows(), 0);
assert_eq!(empty_df.cols(), 0);
assert_eq!(empty_df.columns(), &[] as &[String]);
assert_eq!(empty_df.index(), &RowIndex::Range(0..0));
}
#[test]
fn test_dataframe_column_access() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let col_b = create_i64_typed_frame("B", vec![10, 20], None);
let col_c =
create_string_typed_frame("C", vec!["foo".to_string(), "bar".to_string()], None);
let df = DataFrame::new(
vec![col_a, col_b, col_c],
vec!["A".to_string(), "B".to_string(), "C".to_string()],
None,
);
// Test f64 column
let col_a_view = df.column("A");
assert!(col_a_view.as_f64().is_some());
assert_eq!(col_a_view.as_f64().unwrap(), &[1.0, 2.0]);
assert!(col_a_view.as_i64().is_none());
assert_eq!(col_a_view.len(), 2);
assert!(!col_a_view.is_empty());
// Test i64 column
let col_b_view = df.column("B");
assert!(col_b_view.as_i64().is_some());
assert_eq!(col_b_view.as_i64().unwrap(), &[10, 20]);
// Test String column
let col_c_view = df.column("C");
assert!(col_c_view.as_string().is_some());
assert_eq!(
col_c_view.as_string().unwrap(),
&["foo".to_string(), "bar".to_string()]
);
}
#[test]
#[should_panic(expected = "unknown column label: 'D'")]
fn test_dataframe_column_panic_unknown() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.column("D");
}
#[test]
fn test_dataframe_column_mut_access() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let col_b = create_i64_typed_frame("B", vec![10, 20], None);
let col_c =
create_string_typed_frame("C", vec!["foo".to_string(), "bar".to_string()], None);
let mut df = DataFrame::new(
vec![col_a, col_b, col_c],
vec!["A".to_string(), "B".to_string(), "C".to_string()],
None,
);
// Test f64 column mut
if let DataFrameColumnMut::F64(slice_a_mut) = df.column_mut("A") {
slice_a_mut[0] = 100.0;
} else {
panic!("Column A is not f64 mut");
}
assert_eq!(df.column("A").as_f64().unwrap(), &[100.0, 2.0]);
// Test i64 column mut
if let DataFrameColumnMut::I64(slice_b_mut) = df.column_mut("B") {
slice_b_mut[1] = 200;
} else {
panic!("Column B is not i64 mut");
}
assert_eq!(df.column("B").as_i64().unwrap(), &[10, 200]);
// Test String column mut
if let DataFrameColumnMut::String(slice_c_mut) = df.column_mut("C") {
slice_c_mut[0] = "baz".to_string();
} else {
panic!("Column C is not String mut");
}
assert_eq!(
df.column("C").as_string().unwrap(),
&["baz".to_string(), "bar".to_string()]
);
}
#[test]
#[should_panic(expected = "unknown column label: 'D'")]
fn test_dataframe_column_mut_panic_unknown() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.column_mut("D");
}
#[test]
fn test_dataframe_add_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b = create_i64_typed_frame("B", vec![10, 20], None);
df.add_column("B".to_string(), new_col_b);
assert_eq!(df.rows(), 2);
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["A", "B"]);
assert_eq!(df.column("A").as_f64().unwrap(), &[1.0, 2.0]);
assert_eq!(df.column("B").as_i64().unwrap(), &[10, 20]);
let new_col_c =
create_string_typed_frame("C", vec!["x".to_string(), "y".to_string()], None);
df.add_column("C".to_string(), new_col_c);
assert_eq!(df.cols(), 3);
assert_eq!(df.columns(), &["A", "B", "C"]);
assert_eq!(
df.column("C").as_string().unwrap(),
&["x".to_string(), "y".to_string()]
);
}
#[test]
#[should_panic(expected = "duplicate column label: B")]
fn test_dataframe_add_column_panic_duplicate_name() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b = create_i64_typed_frame("B", vec![10], None);
df.add_column("B".to_string(), new_col_b);
let another_col_b = create_i64_typed_frame("B", vec![20], None);
df.add_column("B".to_string(), another_col_b);
}
#[test]
#[should_panic(expected = "new column 'B' has inconsistent row count (1 vs 2)")]
fn test_dataframe_add_column_panic_inconsistent_rows() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b = create_i64_typed_frame("B", vec![10], None); // Mismatch
df.add_column("B".to_string(), new_col_b);
}
#[test]
#[should_panic(expected = "provided TypedFrame must contain exactly one column named 'B'")]
fn test_dataframe_add_column_panic_multi_column_typedframe() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let multi_col_b = create_multi_f64_typed_frame(
vec!["B", "C"],
vec![vec![10.0, 20.0], vec![30.0, 40.0]],
None,
);
df.add_column("B".to_string(), multi_col_b);
}
#[test]
fn test_dataframe_delete_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let col_b = create_i64_typed_frame("B", vec![10, 20], None);
let col_c = create_string_typed_frame("C", vec!["x".to_string(), "y".to_string()], None);
let mut df = DataFrame::new(
vec![col_a, col_b, col_c],
vec!["A".to_string(), "B".to_string(), "C".to_string()],
None,
);
let deleted_col_b = df.delete_column("B");
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["A", "C"]);
assert_eq!(df.column("A").as_f64().unwrap(), &[1.0, 2.0]);
assert_eq!(
df.column("C").as_string().unwrap(),
&["x".to_string(), "y".to_string()]
);
if let TypedFrame::I64(frame_b) = deleted_col_b {
assert_eq!(frame_b.column("B"), &[10, 20]);
} else {
panic!("Deleted column B is not i64 TypedFrame");
}
let deleted_col_a = df.delete_column("A");
assert_eq!(df.cols(), 1);
assert_eq!(df.columns(), &["C"]);
assert_eq!(
df.column("C").as_string().unwrap(),
&["x".to_string(), "y".to_string()]
);
if let TypedFrame::F64(frame_a) = deleted_col_a {
assert_eq!(frame_a.column("A"), &[1.0, 2.0]);
} else {
panic!("Deleted column A is not f64 TypedFrame");
}
let deleted_col_c = df.delete_column("C");
assert_eq!(df.cols(), 0);
assert_eq!(df.columns(), &[] as &[String]);
if let TypedFrame::String(frame_c) = deleted_col_c {
assert_eq!(frame_c.column("C"), &["x".to_string(), "y".to_string()]);
} else {
panic!("Deleted column C is not String TypedFrame");
}
}
#[test]
#[should_panic(expected = "unknown column label: 'D'")]
fn test_dataframe_delete_column_panic_unknown() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.delete_column("D");
}
#[test]
fn test_dataframe_rename_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let col_b = create_i64_typed_frame("B", vec![10, 20], None);
let mut df = DataFrame::new(
vec![col_a, col_b],
vec!["A".to_string(), "B".to_string()],
None,
);
df.rename_column("A", "Alpha".to_string());
assert_eq!(df.columns(), &["Alpha", "B"]);
assert_eq!(df.column("Alpha").as_f64().unwrap(), &[1.0, 2.0]);
assert!(!df.column_locations.contains_key("A"));
assert!(df.column_locations.contains_key("Alpha"));
df.rename_column("B", "Beta".to_string());
assert_eq!(df.columns(), &["Alpha", "Beta"]);
assert_eq!(df.column("Beta").as_i64().unwrap(), &[10, 20]);
}
#[test]
#[should_panic(expected = "unknown column label: 'C'")]
fn test_dataframe_rename_column_panic_unknown_old_name() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.rename_column("C", "D".to_string());
}
#[test]
#[should_panic(expected = "new column name 'B' already exists")]
fn test_dataframe_rename_column_panic_new_name_exists() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let col_b = create_i64_typed_frame("B", vec![10], None);
let mut df = DataFrame::new(
vec![col_a, col_b],
vec!["A".to_string(), "B".to_string()],
None,
);
df.rename_column("A", "B".to_string());
}
#[test]
fn test_dataframe_sort_columns() {
let col_c = create_f64_typed_frame("C", vec![1.0], None);
let col_a = create_i64_typed_frame("A", vec![2], None);
let col_b = create_string_typed_frame("B", vec!["x".to_string()], None);
let mut df = DataFrame::new(
vec![col_c, col_a, col_b],
vec!["C".to_string(), "A".to_string(), "B".to_string()],
None,
);
assert_eq!(df.columns(), &["C", "A", "B"]);
df.sort_columns();
assert_eq!(df.columns(), &["A", "B", "C"]);
// Ensure data integrity after sort
assert_eq!(df.column("A").as_i64().unwrap(), &[2]);
assert_eq!(df.column("B").as_string().unwrap(), &["x".to_string()]);
assert_eq!(df.column("C").as_f64().unwrap(), &[1.0]);
}
#[test]
fn test_dataframe_new_with_multi_column_subframe() {
let multi_f64_frame = create_multi_f64_typed_frame(
@ -760,265 +1041,4 @@ mod tests {
None,
);
}
#[test]
#[should_panic(expected = "provided index length (2) mismatch column rows (3)")]
fn test_dataframe_new_panic_index_len_mismatch() {
let index = RowIndex::Int(vec![10, 20]); // Length 2
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0, 3.0], None); // Length 3
DataFrame::new(vec![col_a], vec!["A".to_string()], Some(index));
}
#[test]
#[should_panic(expected = "TypedFrame has inconsistent index with common index")]
fn test_dataframe_new_panic_inconsistent_column_index() {
let common_index = RowIndex::Int(vec![10, 20]);
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None); // Uses Range index by default
DataFrame::new(vec![col_a], vec!["A".to_string()], Some(common_index));
}
#[test]
fn test_dataframe_add_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b_typed_frame = create_i64_typed_frame("B", vec![10, 20], None);
df.add_column("B".to_string(), new_col_b_typed_frame);
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["A", "B"]);
if let DataFrameColumn::I64(slice_b) = df.column("B") {
assert_eq!(slice_b, &[10, 20]);
} else {
panic!("Column B is not i64");
}
let new_col_c_typed_frame =
create_string_typed_frame("C", vec!["foo".to_string(), "bar".to_string()], None);
df.add_column("C".to_string(), new_col_c_typed_frame);
assert_eq!(df.cols(), 3);
assert_eq!(df.columns(), &["A", "B", "C"]);
if let DataFrameColumn::String(slice_c) = df.column("C") {
assert_eq!(slice_c, &["foo".to_string(), "bar".to_string()]);
} else {
panic!("Column C is not String");
}
}
#[test]
#[should_panic(expected = "duplicate column label: B")]
fn test_dataframe_add_column_panic_duplicate_name() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b = create_i64_typed_frame("B", vec![10, 20], None);
df.add_column("B".to_string(), new_col_b);
let another_col_b = create_i64_typed_frame("B", vec![30, 40], None);
df.add_column("B".to_string(), another_col_b);
}
#[test]
#[should_panic(expected = "new column 'B' has inconsistent row count (3 vs 2)")]
fn test_dataframe_add_column_panic_inconsistent_rows() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let new_col_b = create_i64_typed_frame("B", vec![10, 20, 30], None); // Mismatch
df.add_column("B".to_string(), new_col_b);
}
#[test]
#[should_panic(expected = "new column 'B' has inconsistent index with DataFrame's index")]
fn test_dataframe_add_column_panic_inconsistent_index() {
let df_index = RowIndex::Int(vec![1, 2]);
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], Some(df_index.clone()));
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], Some(df_index));
let new_col_b_index = RowIndex::Int(vec![10, 20]); // Different index
let new_col_b = create_i64_typed_frame("B", vec![10, 20], Some(new_col_b_index));
df.add_column("B".to_string(), new_col_b);
}
#[test]
#[should_panic(expected = "provided TypedFrame must contain exactly one column named 'B'")]
fn test_dataframe_add_column_panic_multi_column_typedframe() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let multi_col_b = create_multi_f64_typed_frame(
vec!["B", "C"],
vec![vec![10.0, 20.0], vec![30.0, 40.0]],
None,
);
df.add_column("B".to_string(), multi_col_b);
}
#[test]
#[should_panic(expected = "provided TypedFrame must contain exactly one column named 'B'")]
fn test_dataframe_add_column_panic_typedframe_name_mismatch() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
let col_c = create_f64_typed_frame("C", vec![10.0, 20.0], None);
df.add_column("B".to_string(), col_c); // Adding column named "B" but TypedFrame is named "C"
}
#[test]
fn test_dataframe_delete_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0, 3.0], None);
let col_b = create_i64_typed_frame("B", vec![4, 5, 6], None);
let col_c = create_string_typed_frame(
"C",
vec!["x".to_string(), "y".to_string(), "z".to_string()],
None,
);
let mut df = DataFrame::new(
vec![col_a, col_b, col_c],
vec!["A".to_string(), "B".to_string(), "C".to_string()],
None,
);
let deleted_col_b = df.delete_column("B");
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["A", "C"]);
assert_eq!(df.rows(), 3);
if let TypedFrame::I64(frame_b) = deleted_col_b {
assert_eq!(frame_b.columns(), &["B"]);
assert_eq!(frame_b.column("B"), &[4, 5, 6]);
} else {
panic!("Deleted column B is not i64 TypedFrame");
}
// Verify remaining columns
if let DataFrameColumn::F64(slice_a) = df.column("A") {
assert_eq!(slice_a, &[1.0, 2.0, 3.0]);
} else {
panic!("Column A is not f64");
}
if let DataFrameColumn::String(slice_c) = df.column("C") {
assert_eq!(
slice_c,
&["x".to_string(), "y".to_string(), "z".to_string()]
);
} else {
panic!("Column C is not String");
}
// Delete another column, ensuring subframe is removed if empty
let deleted_col_a = df.delete_column("A");
assert_eq!(df.cols(), 1);
assert_eq!(df.columns(), &["C"]);
assert_eq!(df.subframes.len(), 1); // Only the String subframe should remain
if let TypedFrame::F64(frame_a) = deleted_col_a {
assert_eq!(frame_a.columns(), &["A"]);
assert_eq!(frame_a.column("A"), &[1.0, 2.0, 3.0]);
} else {
panic!("Deleted column A is not f64 TypedFrame");
}
}
#[test]
#[should_panic(expected = "unknown column label: 'D'")]
fn test_dataframe_delete_column_panic_not_found() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.delete_column("D");
}
#[test]
fn test_dataframe_rename_column() {
let col_a = create_f64_typed_frame("A", vec![1.0, 2.0], None);
let col_b = create_i64_typed_frame("B", vec![10, 20], None);
let mut df = DataFrame::new(
vec![col_a, col_b],
vec!["A".to_string(), "B".to_string()],
None,
);
df.rename_column("A", "Alpha".to_string());
assert_eq!(df.cols(), 2);
assert_eq!(df.columns(), &["Alpha", "B"]);
if let DataFrameColumn::F64(slice_alpha) = df.column("Alpha") {
assert_eq!(slice_alpha, &[1.0, 2.0]);
} else {
panic!("Column Alpha is not f64");
}
// Rename a column in a multi-column subframe
let multi_f64_frame = create_multi_f64_typed_frame(
vec!["X", "Y"],
vec![vec![1.0, 2.0], vec![10.0, 20.0]],
None,
);
let mut df_multi = DataFrame::new(
vec![multi_f64_frame],
vec!["X".to_string(), "Y".to_string()],
None,
);
df_multi.rename_column("X", "Ex".to_string());
assert_eq!(df_multi.columns(), &["Ex", "Y"]);
if let DataFrameColumn::F64(slice_ex) = df_multi.column("Ex") {
assert_eq!(slice_ex, &[1.0, 2.0]);
} else {
panic!("Column Ex is not f64");
}
}
#[test]
#[should_panic(expected = "unknown column label: 'D'")]
fn test_dataframe_rename_column_panic_old_name_not_found() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let mut df = DataFrame::new(vec![col_a], vec!["A".to_string()], None);
df.rename_column("D", "E".to_string());
}
#[test]
#[should_panic(expected = "new column name 'B' already exists")]
fn test_dataframe_rename_column_panic_new_name_exists() {
let col_a = create_f64_typed_frame("A", vec![1.0], None);
let col_b = create_i64_typed_frame("B", vec![2], None);
let mut df = DataFrame::new(
vec![col_a, col_b],
vec!["A".to_string(), "B".to_string()],
None,
);
df.rename_column("A", "B".to_string());
}
#[test]
fn test_dataframe_sort_columns() {
let col_c = create_f64_typed_frame("C", vec![1.0, 2.0], None);
let col_a = create_i64_typed_frame("A", vec![10, 20], None);
let col_b = create_string_typed_frame("B", vec!["x".to_string(), "y".to_string()], None);
let mut df = DataFrame::new(
vec![col_c, col_a, col_b],
vec!["C".to_string(), "A".to_string(), "B".to_string()],
None,
);
assert_eq!(df.columns(), &["C", "A", "B"]);
df.sort_columns();
assert_eq!(df.columns(), &["A", "B", "C"]);
// Verify data integrity after sort
if let DataFrameColumn::I64(slice_a) = df.column("A") {
assert_eq!(slice_a, &[10, 20]);
} else {
panic!("Column A is not i64");
}
if let DataFrameColumn::String(slice_b) = df.column("B") {
assert_eq!(slice_b, &["x".to_string(), "y".to_string()]);
} else {
panic!("Column B is not String");
}
if let DataFrameColumn::F64(slice_c) = df.column("C") {
assert_eq!(slice_c, &[1.0, 2.0]);
} else {
panic!("Column C is not f64");
}
}
}