Path: blob/main/crates/polars-testing/src/asserts/frame.rs
8415 views
/// Asserts that two DataFrames are equal according to the specified options.1///2/// This macro compares two Polars DataFrame objects and panics with a detailed error message if they are not equal.3/// It provides two forms:4/// - With custom comparison options5/// - With default comparison options6///7/// # Example8///9/// ```10/// use polars_core::prelude::*;11/// use polars_testing::assert_dataframe_equal;12/// use polars_testing::asserts::DataFrameEqualOptions;13///14/// // Create two DataFrames to compare15/// let df1 = df! {16/// "a" => [1, 2, 3],17/// "b" => [4.0, 5.0, 6.0],18/// }.unwrap();19/// let df2 = df! {20/// "a" => [1, 2, 3],21/// "b" => [4.0, 5.0, 6.0],22/// }.unwrap();23///24/// // Assert with default options25/// assert_dataframe_equal!(&df1, &df2);26///27/// // Assert with custom options28/// let options = DataFrameEqualOptions::default()29/// .with_check_exact(true)30/// .with_check_row_order(false);31/// assert_dataframe_equal!(&df1, &df2, options);32/// ```33///34/// # Panics35///36/// Panics when the DataFrames are not equal according to the specified comparison criteria.37///38#[macro_export]39macro_rules! assert_dataframe_equal {40($left:expr, $right:expr $(, $options:expr)?) => {41#[allow(unused_assignments)]42#[allow(unused_mut)]43let mut options = $crate::asserts::DataFrameEqualOptions::default();44$(options = $options;)?4546match $crate::asserts::assert_dataframe_equal($left, $right, options) {47Ok(_) => {},48Err(e) => panic!("{}", e),49}50};51}5253#[cfg(test)]54mod tests {55#[allow(unused_imports)]56use polars_core::prelude::*;5758// Testing default struct implementation59#[test]60fn test_dataframe_equal_options() {61let options = crate::asserts::DataFrameEqualOptions::default();6263assert!(options.check_row_order);64assert!(options.check_column_order);65assert!(options.check_dtypes);66assert!(!options.check_exact);67assert_eq!(options.rel_tol, 1e-5);68assert_eq!(options.abs_tol, 1e-8);69assert!(!options.categorical_as_str);70}7172// Testing dataframe schema equality parameters73#[test]74#[should_panic(expected = "height (row count) mismatch")]75fn test_dataframe_height_mismatch() {76let df1 = DataFrame::new_infer_height(vec![77Series::new("col1".into(), &[1, 2]).into(),78Series::new("col2".into(), &["a", "b"]).into(),79])80.unwrap();8182let df2 = DataFrame::new_infer_height(vec![83Series::new("col1".into(), &[1, 2, 3]).into(),84Series::new("col2".into(), &["a", "b", "c"]).into(),85])86.unwrap();8788assert_dataframe_equal!(&df1, &df2);89}9091#[test]92#[should_panic(expected = "columns mismatch")]93fn test_dataframe_column_mismatch() {94let df1 = DataFrame::new_infer_height(vec![95Series::new("col1".into(), &[1, 2, 3]).into(),96Series::new("col2".into(), &["a", "b", "c"]).into(),97])98.unwrap();99100let df2 = DataFrame::new_infer_height(vec![101Series::new("col1".into(), &[1, 2, 3]).into(),102Series::new("different_col".into(), &["a", "b", "c"]).into(),103])104.unwrap();105106assert_dataframe_equal!(&df1, &df2);107}108109#[test]110#[should_panic(expected = "dtypes do not match")]111fn test_dataframe_dtype_mismatch() {112let df1 = DataFrame::new_infer_height(vec![113Series::new("col1".into(), &[1, 2, 3]).into(),114Series::new("col2".into(), &["a", "b", "c"]).into(),115])116.unwrap();117118let df2 = DataFrame::new_infer_height(vec![119Series::new("col1".into(), &[1.0, 2.0, 3.0]).into(),120Series::new("col2".into(), &["a", "b", "c"]).into(),121])122.unwrap();123124assert_dataframe_equal!(&df1, &df2);125}126127#[test]128fn test_dataframe_dtype_mismatch_ignored() {129let df1 = DataFrame::new_infer_height(vec![130Series::new("col1".into(), &[1, 2, 3]).into(),131Series::new("col2".into(), &["a", "b", "c"]).into(),132])133.unwrap();134135let df2 = DataFrame::new_infer_height(vec![136Series::new("col1".into(), &[1.0, 2.0, 3.0]).into(),137Series::new("col2".into(), &["a", "b", "c"]).into(),138])139.unwrap();140141let options = crate::asserts::DataFrameEqualOptions::default().with_check_dtypes(false);142assert_dataframe_equal!(&df1, &df2, options);143}144145#[test]146#[should_panic(expected = "columns are not in the same order")]147fn test_dataframe_column_order_mismatch() {148let df1 = DataFrame::new_infer_height(vec![149Series::new("col1".into(), &[1, 2, 3]).into(),150Series::new("col2".into(), &["a", "b", "c"]).into(),151])152.unwrap();153154let df2 = DataFrame::new_infer_height(vec![155Series::new("col2".into(), &["a", "b", "c"]).into(),156Series::new("col1".into(), &[1, 2, 3]).into(),157])158.unwrap();159160assert_dataframe_equal!(&df1, &df2);161}162163#[test]164fn test_dataframe_column_order_mismatch_ignored() {165let df1 = DataFrame::new_infer_height(vec![166Series::new("col1".into(), &[1, 2, 3]).into(),167Series::new("col2".into(), &["a", "b", "c"]).into(),168])169.unwrap();170171let df2 = DataFrame::new_infer_height(vec![172Series::new("col2".into(), &["a", "b", "c"]).into(),173Series::new("col1".into(), &[1, 2, 3]).into(),174])175.unwrap();176177let options =178crate::asserts::DataFrameEqualOptions::default().with_check_column_order(false);179assert_dataframe_equal!(&df1, &df2, options);180}181182#[test]183#[should_panic(expected = "columns mismatch: [\"col3\"] in left, but not in right")]184fn test_dataframe_left_has_extra_column() {185let df1 = DataFrame::new_infer_height(vec![186Series::new("col1".into(), &[1, 2, 3]).into(),187Series::new("col2".into(), &["a", "b", "c"]).into(),188Series::new("col3".into(), &[true, false, true]).into(),189])190.unwrap();191192let df2 = DataFrame::new_infer_height(vec![193Series::new("col1".into(), &[1, 2, 3]).into(),194Series::new("col2".into(), &["a", "b", "c"]).into(),195])196.unwrap();197198assert_dataframe_equal!(&df1, &df2);199}200201#[test]202#[should_panic(expected = "columns mismatch: [\"col3\"] in right, but not in left")]203fn test_dataframe_right_has_extra_column() {204let df1 = DataFrame::new_infer_height(vec![205Series::new("col1".into(), &[1, 2, 3]).into(),206Series::new("col2".into(), &["a", "b", "c"]).into(),207])208.unwrap();209210let df2 = DataFrame::new_infer_height(vec![211Series::new("col1".into(), &[1, 2, 3]).into(),212Series::new("col2".into(), &["a", "b", "c"]).into(),213Series::new("col3".into(), &[true, false, true]).into(),214])215.unwrap();216217assert_dataframe_equal!(&df1, &df2);218}219220// Testing basic equality221#[test]222#[should_panic(expected = "value mismatch for column")]223fn test_dataframe_value_mismatch() {224let df1 = DataFrame::new_infer_height(vec![225Series::new("col1".into(), &[1, 2, 3]).into(),226Series::new("col2".into(), &["a", "b", "c"]).into(),227Series::new("col3".into(), &[true, false, true]).into(),228])229.unwrap();230231let df2 = DataFrame::new_infer_height(vec![232Series::new("col1".into(), &[1, 2, 3]).into(),233Series::new("col2".into(), &["a", "b", "changed"]).into(),234Series::new("col3".into(), &[true, false, true]).into(),235])236.unwrap();237238assert_dataframe_equal!(&df1, &df2);239}240241#[test]242fn test_dataframe_equal() {243let df1 = DataFrame::new_infer_height(vec![244Series::new("col1".into(), &[1, 2, 3]).into(),245Series::new("col2".into(), &["a", "b", "c"]).into(),246Series::new("col3".into(), &[true, false, true]).into(),247])248.unwrap();249250let df2 = DataFrame::new_infer_height(vec![251Series::new("col1".into(), &[1, 2, 3]).into(),252Series::new("col2".into(), &["a", "b", "c"]).into(),253Series::new("col3".into(), &[true, false, true]).into(),254])255.unwrap();256257assert_dataframe_equal!(&df1, &df2);258}259260#[test]261#[should_panic(expected = "value mismatch")]262fn test_dataframe_row_order_mismatch() {263let df1 = DataFrame::new_infer_height(vec![264Series::new("col1".into(), &[1, 2, 3]).into(),265Series::new("col2".into(), &["a", "b", "c"]).into(),266])267.unwrap();268269let df2 = DataFrame::new_infer_height(vec![270Series::new("col1".into(), &[3, 1, 2]).into(),271Series::new("col2".into(), &["c", "a", "b"]).into(),272])273.unwrap();274275assert_dataframe_equal!(&df1, &df2);276}277278#[test]279fn test_dataframe_row_order_ignored() {280let df1 = DataFrame::new_infer_height(vec![281Series::new("col1".into(), &[1, 2, 3]).into(),282Series::new("col2".into(), &["a", "b", "c"]).into(),283])284.unwrap();285286let df2 = DataFrame::new_infer_height(vec![287Series::new("col1".into(), &[3, 1, 2]).into(),288Series::new("col2".into(), &["c", "a", "b"]).into(),289])290.unwrap();291292let options = crate::asserts::DataFrameEqualOptions::default().with_check_row_order(false);293assert_dataframe_equal!(&df1, &df2, options);294}295296// Testing more comprehensive equality297#[test]298#[should_panic(expected = "value mismatch")]299fn test_dataframe_complex_mismatch() {300let df1 = DataFrame::new_infer_height(vec![301Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),302Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),303Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),304Series::new("booleans".into(), &[true, false, true, false, true]).into(),305Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),306])307.unwrap();308309let df2 = DataFrame::new_infer_height(vec![310Series::new("integers".into(), &[1, 2, 99, 4, 5]).into(),311Series::new("floats".into(), &[1.1, 2.2, 3.3, 9.9, 5.5]).into(),312Series::new("strings".into(), &["a", "b", "c", "CHANGED", "e"]).into(),313Series::new("booleans".into(), &[true, false, false, false, true]).into(),314Series::new("opt_ints".into(), &[Some(1), None, Some(3), None, None]).into(),315])316.unwrap();317318assert_dataframe_equal!(&df1, &df2);319}320321#[test]322fn test_dataframe_complex_match() {323let df1 = DataFrame::new_infer_height(vec![324Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),325Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),326Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),327Series::new("booleans".into(), &[true, false, true, false, true]).into(),328Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),329])330.unwrap();331332let df2 = DataFrame::new_infer_height(vec![333Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),334Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),335Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),336Series::new("booleans".into(), &[true, false, true, false, true]).into(),337Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),338])339.unwrap();340341assert_dataframe_equal!(&df1, &df2);342}343344// Testing float value precision equality345#[test]346#[should_panic(expected = "value mismatch")]347fn test_dataframe_numeric_exact_fail() {348let df1 = DataFrame::new_infer_height(vec![349Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),350])351.unwrap();352353let df2 =354DataFrame::new_infer_height(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()])355.unwrap();356357let options = crate::asserts::DataFrameEqualOptions::default().with_check_exact(true);358assert_dataframe_equal!(&df1, &df2, options);359}360361#[test]362fn test_dataframe_numeric_tolerance_pass() {363let df1 = DataFrame::new_infer_height(vec![364Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),365])366.unwrap();367368let df2 =369DataFrame::new_infer_height(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()])370.unwrap();371372assert_dataframe_equal!(&df1, &df2);373}374375// Testing equality with special values376#[test]377fn test_empty_dataframe_equal() {378let df1 = DataFrame::empty();379let df2 = DataFrame::empty();380381assert_dataframe_equal!(&df1, &df2);382}383384#[test]385fn test_empty_dataframe_schema_equal() {386let df1 = DataFrame::new_infer_height(vec![387Series::new("col1".into(), &Vec::<i32>::new()).into(),388Series::new("col2".into(), &Vec::<String>::new()).into(),389])390.unwrap();391392let df2 = DataFrame::new_infer_height(vec![393Series::new("col1".into(), &Vec::<i32>::new()).into(),394Series::new("col2".into(), &Vec::<String>::new()).into(),395])396.unwrap();397398assert_dataframe_equal!(&df1, &df2);399}400401#[test]402#[should_panic(expected = "value mismatch")]403fn test_dataframe_single_row_mismatch() {404let df1 = DataFrame::new_infer_height(vec![405Series::new("col1".into(), &[42]).into(),406Series::new("col2".into(), &["value"]).into(),407Series::new("col3".into(), &[true]).into(),408])409.unwrap();410411let df2 = DataFrame::new_infer_height(vec![412Series::new("col1".into(), &[42]).into(),413Series::new("col2".into(), &["different"]).into(),414Series::new("col3".into(), &[true]).into(),415])416.unwrap();417418assert_dataframe_equal!(&df1, &df2);419}420421#[test]422fn test_dataframe_single_row_match() {423let df1 = DataFrame::new_infer_height(vec![424Series::new("col1".into(), &[42]).into(),425Series::new("col2".into(), &["value"]).into(),426Series::new("col3".into(), &[true]).into(),427])428.unwrap();429430let df2 = DataFrame::new_infer_height(vec![431Series::new("col1".into(), &[42]).into(),432Series::new("col2".into(), &["value"]).into(),433Series::new("col3".into(), &[true]).into(),434])435.unwrap();436437assert_dataframe_equal!(&df1, &df2);438}439440#[test]441#[should_panic(expected = "value mismatch")]442fn test_dataframe_null_values_mismatch() {443let df1 = DataFrame::new_infer_height(vec![444Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),445])446.unwrap();447448let df2 = DataFrame::new_infer_height(vec![449Series::new("col1".into(), &[Some(1), Some(2), None]).into(),450])451.unwrap();452453assert_dataframe_equal!(&df1, &df2);454}455456#[test]457fn test_dataframe_null_values_match() {458let df1 = DataFrame::new_infer_height(vec![459Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),460])461.unwrap();462463let df2 = DataFrame::new_infer_height(vec![464Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),465])466.unwrap();467468assert_dataframe_equal!(&df1, &df2);469}470471#[test]472#[should_panic(expected = "value mismatch")]473fn test_dataframe_nan_values_mismatch() {474let df1 = DataFrame::new_infer_height(vec![475Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),476])477.unwrap();478479let df2 = DataFrame::new_infer_height(vec![480Series::new("col1".into(), &[1.0, 2.0, f64::NAN]).into(),481])482.unwrap();483484assert_dataframe_equal!(&df1, &df2);485}486487#[test]488fn test_dataframe_nan_values_match() {489let df1 = DataFrame::new_infer_height(vec![490Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),491])492.unwrap();493494let df2 = DataFrame::new_infer_height(vec![495Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),496])497.unwrap();498499assert_dataframe_equal!(&df1, &df2);500}501502#[test]503#[should_panic(expected = "value mismatch")]504fn test_dataframe_infinity_values_mismatch() {505let df1 = DataFrame::new_infer_height(vec![506Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),507])508.unwrap();509510let df2 = DataFrame::new_infer_height(vec![511Series::new("col1".into(), &[1.0, f64::NEG_INFINITY, 3.0]).into(),512])513.unwrap();514515assert_dataframe_equal!(&df1, &df2);516}517518#[test]519fn test_dataframe_infinity_values_match() {520let df1 = DataFrame::new_infer_height(vec![521Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),522])523.unwrap();524525let df2 = DataFrame::new_infer_height(vec![526Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),527])528.unwrap();529530assert_dataframe_equal!(&df1, &df2);531}532533// Testing categorical operations534#[test]535#[should_panic(expected = "value mismatch")]536fn test_dataframe_categorical_as_string_mismatch() {537let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);538categorical1 = categorical1539.cast(&DataType::from_categories(Categories::global()))540.unwrap();541let df1 = DataFrame::new_infer_height(vec![categorical1.into()]).unwrap();542543let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "e"]);544categorical2 = categorical2545.cast(&DataType::from_categories(Categories::global()))546.unwrap();547let df2 = DataFrame::new_infer_height(vec![categorical2.into()]).unwrap();548549let options =550crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);551assert_dataframe_equal!(&df1, &df2, options);552}553554#[test]555fn test_dataframe_categorical_as_string_match() {556let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);557categorical1 = categorical1558.cast(&DataType::from_categories(Categories::global()))559.unwrap();560let df1 = DataFrame::new_infer_height(vec![categorical1.into()]).unwrap();561562let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "d"]);563categorical2 = categorical2564.cast(&DataType::from_categories(Categories::global()))565.unwrap();566let df2 = DataFrame::new_infer_height(vec![categorical2.into()]).unwrap();567568let options =569crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);570assert_dataframe_equal!(&df1, &df2, options);571}572573// Testing nested types574#[test]575#[should_panic(expected = "value mismatch")]576fn test_dataframe_nested_values_mismatch() {577let df1 = DataFrame::new_infer_height(vec![578Series::new(579"list_col".into(),580&[581Some(vec![1, 2, 3]),582Some(vec![4, 5, 6]),583None,584Some(vec![7, 8, 9]),585],586)587.into(),588])589.unwrap();590591let df2 = DataFrame::new_infer_height(vec![592Series::new(593"list_col".into(),594&[595Some(vec![1, 2, 3]),596Some(vec![4, 5, 99]),597None,598Some(vec![7, 8, 9]),599],600)601.into(),602])603.unwrap();604605assert_dataframe_equal!(&df1, &df2);606}607608#[test]609fn test_dataframe_nested_values_match() {610let df1 = DataFrame::new_infer_height(vec![611Series::new(612"list_col".into(),613&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],614)615.into(),616])617.unwrap();618619let df2 = DataFrame::new_infer_height(vec![620Series::new(621"list_col".into(),622&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],623)624.into(),625])626.unwrap();627628assert_dataframe_equal!(&df1, &df2);629}630}631632633