Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-testing/src/asserts/frame.rs
8415 views
1
/// Asserts that two DataFrames are equal according to the specified options.
2
///
3
/// This macro compares two Polars DataFrame objects and panics with a detailed error message if they are not equal.
4
/// It provides two forms:
5
/// - With custom comparison options
6
/// - With default comparison options
7
///
8
/// # Example
9
///
10
/// ```
11
/// use polars_core::prelude::*;
12
/// use polars_testing::assert_dataframe_equal;
13
/// use polars_testing::asserts::DataFrameEqualOptions;
14
///
15
/// // Create two DataFrames to compare
16
/// let df1 = df! {
17
/// "a" => [1, 2, 3],
18
/// "b" => [4.0, 5.0, 6.0],
19
/// }.unwrap();
20
/// let df2 = df! {
21
/// "a" => [1, 2, 3],
22
/// "b" => [4.0, 5.0, 6.0],
23
/// }.unwrap();
24
///
25
/// // Assert with default options
26
/// assert_dataframe_equal!(&df1, &df2);
27
///
28
/// // Assert with custom options
29
/// let options = DataFrameEqualOptions::default()
30
/// .with_check_exact(true)
31
/// .with_check_row_order(false);
32
/// assert_dataframe_equal!(&df1, &df2, options);
33
/// ```
34
///
35
/// # Panics
36
///
37
/// Panics when the DataFrames are not equal according to the specified comparison criteria.
38
///
39
#[macro_export]
40
macro_rules! assert_dataframe_equal {
41
($left:expr, $right:expr $(, $options:expr)?) => {
42
#[allow(unused_assignments)]
43
#[allow(unused_mut)]
44
let mut options = $crate::asserts::DataFrameEqualOptions::default();
45
$(options = $options;)?
46
47
match $crate::asserts::assert_dataframe_equal($left, $right, options) {
48
Ok(_) => {},
49
Err(e) => panic!("{}", e),
50
}
51
};
52
}
53
54
#[cfg(test)]
55
mod tests {
56
#[allow(unused_imports)]
57
use polars_core::prelude::*;
58
59
// Testing default struct implementation
60
#[test]
61
fn test_dataframe_equal_options() {
62
let options = crate::asserts::DataFrameEqualOptions::default();
63
64
assert!(options.check_row_order);
65
assert!(options.check_column_order);
66
assert!(options.check_dtypes);
67
assert!(!options.check_exact);
68
assert_eq!(options.rel_tol, 1e-5);
69
assert_eq!(options.abs_tol, 1e-8);
70
assert!(!options.categorical_as_str);
71
}
72
73
// Testing dataframe schema equality parameters
74
#[test]
75
#[should_panic(expected = "height (row count) mismatch")]
76
fn test_dataframe_height_mismatch() {
77
let df1 = DataFrame::new_infer_height(vec![
78
Series::new("col1".into(), &[1, 2]).into(),
79
Series::new("col2".into(), &["a", "b"]).into(),
80
])
81
.unwrap();
82
83
let df2 = DataFrame::new_infer_height(vec![
84
Series::new("col1".into(), &[1, 2, 3]).into(),
85
Series::new("col2".into(), &["a", "b", "c"]).into(),
86
])
87
.unwrap();
88
89
assert_dataframe_equal!(&df1, &df2);
90
}
91
92
#[test]
93
#[should_panic(expected = "columns mismatch")]
94
fn test_dataframe_column_mismatch() {
95
let df1 = DataFrame::new_infer_height(vec![
96
Series::new("col1".into(), &[1, 2, 3]).into(),
97
Series::new("col2".into(), &["a", "b", "c"]).into(),
98
])
99
.unwrap();
100
101
let df2 = DataFrame::new_infer_height(vec![
102
Series::new("col1".into(), &[1, 2, 3]).into(),
103
Series::new("different_col".into(), &["a", "b", "c"]).into(),
104
])
105
.unwrap();
106
107
assert_dataframe_equal!(&df1, &df2);
108
}
109
110
#[test]
111
#[should_panic(expected = "dtypes do not match")]
112
fn test_dataframe_dtype_mismatch() {
113
let df1 = DataFrame::new_infer_height(vec![
114
Series::new("col1".into(), &[1, 2, 3]).into(),
115
Series::new("col2".into(), &["a", "b", "c"]).into(),
116
])
117
.unwrap();
118
119
let df2 = DataFrame::new_infer_height(vec![
120
Series::new("col1".into(), &[1.0, 2.0, 3.0]).into(),
121
Series::new("col2".into(), &["a", "b", "c"]).into(),
122
])
123
.unwrap();
124
125
assert_dataframe_equal!(&df1, &df2);
126
}
127
128
#[test]
129
fn test_dataframe_dtype_mismatch_ignored() {
130
let df1 = DataFrame::new_infer_height(vec![
131
Series::new("col1".into(), &[1, 2, 3]).into(),
132
Series::new("col2".into(), &["a", "b", "c"]).into(),
133
])
134
.unwrap();
135
136
let df2 = DataFrame::new_infer_height(vec![
137
Series::new("col1".into(), &[1.0, 2.0, 3.0]).into(),
138
Series::new("col2".into(), &["a", "b", "c"]).into(),
139
])
140
.unwrap();
141
142
let options = crate::asserts::DataFrameEqualOptions::default().with_check_dtypes(false);
143
assert_dataframe_equal!(&df1, &df2, options);
144
}
145
146
#[test]
147
#[should_panic(expected = "columns are not in the same order")]
148
fn test_dataframe_column_order_mismatch() {
149
let df1 = DataFrame::new_infer_height(vec![
150
Series::new("col1".into(), &[1, 2, 3]).into(),
151
Series::new("col2".into(), &["a", "b", "c"]).into(),
152
])
153
.unwrap();
154
155
let df2 = DataFrame::new_infer_height(vec![
156
Series::new("col2".into(), &["a", "b", "c"]).into(),
157
Series::new("col1".into(), &[1, 2, 3]).into(),
158
])
159
.unwrap();
160
161
assert_dataframe_equal!(&df1, &df2);
162
}
163
164
#[test]
165
fn test_dataframe_column_order_mismatch_ignored() {
166
let df1 = DataFrame::new_infer_height(vec![
167
Series::new("col1".into(), &[1, 2, 3]).into(),
168
Series::new("col2".into(), &["a", "b", "c"]).into(),
169
])
170
.unwrap();
171
172
let df2 = DataFrame::new_infer_height(vec![
173
Series::new("col2".into(), &["a", "b", "c"]).into(),
174
Series::new("col1".into(), &[1, 2, 3]).into(),
175
])
176
.unwrap();
177
178
let options =
179
crate::asserts::DataFrameEqualOptions::default().with_check_column_order(false);
180
assert_dataframe_equal!(&df1, &df2, options);
181
}
182
183
#[test]
184
#[should_panic(expected = "columns mismatch: [\"col3\"] in left, but not in right")]
185
fn test_dataframe_left_has_extra_column() {
186
let df1 = DataFrame::new_infer_height(vec![
187
Series::new("col1".into(), &[1, 2, 3]).into(),
188
Series::new("col2".into(), &["a", "b", "c"]).into(),
189
Series::new("col3".into(), &[true, false, true]).into(),
190
])
191
.unwrap();
192
193
let df2 = DataFrame::new_infer_height(vec![
194
Series::new("col1".into(), &[1, 2, 3]).into(),
195
Series::new("col2".into(), &["a", "b", "c"]).into(),
196
])
197
.unwrap();
198
199
assert_dataframe_equal!(&df1, &df2);
200
}
201
202
#[test]
203
#[should_panic(expected = "columns mismatch: [\"col3\"] in right, but not in left")]
204
fn test_dataframe_right_has_extra_column() {
205
let df1 = DataFrame::new_infer_height(vec![
206
Series::new("col1".into(), &[1, 2, 3]).into(),
207
Series::new("col2".into(), &["a", "b", "c"]).into(),
208
])
209
.unwrap();
210
211
let df2 = DataFrame::new_infer_height(vec![
212
Series::new("col1".into(), &[1, 2, 3]).into(),
213
Series::new("col2".into(), &["a", "b", "c"]).into(),
214
Series::new("col3".into(), &[true, false, true]).into(),
215
])
216
.unwrap();
217
218
assert_dataframe_equal!(&df1, &df2);
219
}
220
221
// Testing basic equality
222
#[test]
223
#[should_panic(expected = "value mismatch for column")]
224
fn test_dataframe_value_mismatch() {
225
let df1 = DataFrame::new_infer_height(vec![
226
Series::new("col1".into(), &[1, 2, 3]).into(),
227
Series::new("col2".into(), &["a", "b", "c"]).into(),
228
Series::new("col3".into(), &[true, false, true]).into(),
229
])
230
.unwrap();
231
232
let df2 = DataFrame::new_infer_height(vec![
233
Series::new("col1".into(), &[1, 2, 3]).into(),
234
Series::new("col2".into(), &["a", "b", "changed"]).into(),
235
Series::new("col3".into(), &[true, false, true]).into(),
236
])
237
.unwrap();
238
239
assert_dataframe_equal!(&df1, &df2);
240
}
241
242
#[test]
243
fn test_dataframe_equal() {
244
let df1 = DataFrame::new_infer_height(vec![
245
Series::new("col1".into(), &[1, 2, 3]).into(),
246
Series::new("col2".into(), &["a", "b", "c"]).into(),
247
Series::new("col3".into(), &[true, false, true]).into(),
248
])
249
.unwrap();
250
251
let df2 = DataFrame::new_infer_height(vec![
252
Series::new("col1".into(), &[1, 2, 3]).into(),
253
Series::new("col2".into(), &["a", "b", "c"]).into(),
254
Series::new("col3".into(), &[true, false, true]).into(),
255
])
256
.unwrap();
257
258
assert_dataframe_equal!(&df1, &df2);
259
}
260
261
#[test]
262
#[should_panic(expected = "value mismatch")]
263
fn test_dataframe_row_order_mismatch() {
264
let df1 = DataFrame::new_infer_height(vec![
265
Series::new("col1".into(), &[1, 2, 3]).into(),
266
Series::new("col2".into(), &["a", "b", "c"]).into(),
267
])
268
.unwrap();
269
270
let df2 = DataFrame::new_infer_height(vec![
271
Series::new("col1".into(), &[3, 1, 2]).into(),
272
Series::new("col2".into(), &["c", "a", "b"]).into(),
273
])
274
.unwrap();
275
276
assert_dataframe_equal!(&df1, &df2);
277
}
278
279
#[test]
280
fn test_dataframe_row_order_ignored() {
281
let df1 = DataFrame::new_infer_height(vec![
282
Series::new("col1".into(), &[1, 2, 3]).into(),
283
Series::new("col2".into(), &["a", "b", "c"]).into(),
284
])
285
.unwrap();
286
287
let df2 = DataFrame::new_infer_height(vec![
288
Series::new("col1".into(), &[3, 1, 2]).into(),
289
Series::new("col2".into(), &["c", "a", "b"]).into(),
290
])
291
.unwrap();
292
293
let options = crate::asserts::DataFrameEqualOptions::default().with_check_row_order(false);
294
assert_dataframe_equal!(&df1, &df2, options);
295
}
296
297
// Testing more comprehensive equality
298
#[test]
299
#[should_panic(expected = "value mismatch")]
300
fn test_dataframe_complex_mismatch() {
301
let df1 = DataFrame::new_infer_height(vec![
302
Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),
303
Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),
304
Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),
305
Series::new("booleans".into(), &[true, false, true, false, true]).into(),
306
Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),
307
])
308
.unwrap();
309
310
let df2 = DataFrame::new_infer_height(vec![
311
Series::new("integers".into(), &[1, 2, 99, 4, 5]).into(),
312
Series::new("floats".into(), &[1.1, 2.2, 3.3, 9.9, 5.5]).into(),
313
Series::new("strings".into(), &["a", "b", "c", "CHANGED", "e"]).into(),
314
Series::new("booleans".into(), &[true, false, false, false, true]).into(),
315
Series::new("opt_ints".into(), &[Some(1), None, Some(3), None, None]).into(),
316
])
317
.unwrap();
318
319
assert_dataframe_equal!(&df1, &df2);
320
}
321
322
#[test]
323
fn test_dataframe_complex_match() {
324
let df1 = DataFrame::new_infer_height(vec![
325
Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),
326
Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),
327
Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),
328
Series::new("booleans".into(), &[true, false, true, false, true]).into(),
329
Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),
330
])
331
.unwrap();
332
333
let df2 = DataFrame::new_infer_height(vec![
334
Series::new("integers".into(), &[1, 2, 3, 4, 5]).into(),
335
Series::new("floats".into(), &[1.1, 2.2, 3.3, 4.4, 5.5]).into(),
336
Series::new("strings".into(), &["a", "b", "c", "d", "e"]).into(),
337
Series::new("booleans".into(), &[true, false, true, false, true]).into(),
338
Series::new("opt_ints".into(), &[Some(1), None, Some(3), Some(4), None]).into(),
339
])
340
.unwrap();
341
342
assert_dataframe_equal!(&df1, &df2);
343
}
344
345
// Testing float value precision equality
346
#[test]
347
#[should_panic(expected = "value mismatch")]
348
fn test_dataframe_numeric_exact_fail() {
349
let df1 = DataFrame::new_infer_height(vec![
350
Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),
351
])
352
.unwrap();
353
354
let df2 =
355
DataFrame::new_infer_height(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()])
356
.unwrap();
357
358
let options = crate::asserts::DataFrameEqualOptions::default().with_check_exact(true);
359
assert_dataframe_equal!(&df1, &df2, options);
360
}
361
362
#[test]
363
fn test_dataframe_numeric_tolerance_pass() {
364
let df1 = DataFrame::new_infer_height(vec![
365
Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),
366
])
367
.unwrap();
368
369
let df2 =
370
DataFrame::new_infer_height(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()])
371
.unwrap();
372
373
assert_dataframe_equal!(&df1, &df2);
374
}
375
376
// Testing equality with special values
377
#[test]
378
fn test_empty_dataframe_equal() {
379
let df1 = DataFrame::empty();
380
let df2 = DataFrame::empty();
381
382
assert_dataframe_equal!(&df1, &df2);
383
}
384
385
#[test]
386
fn test_empty_dataframe_schema_equal() {
387
let df1 = DataFrame::new_infer_height(vec![
388
Series::new("col1".into(), &Vec::<i32>::new()).into(),
389
Series::new("col2".into(), &Vec::<String>::new()).into(),
390
])
391
.unwrap();
392
393
let df2 = DataFrame::new_infer_height(vec![
394
Series::new("col1".into(), &Vec::<i32>::new()).into(),
395
Series::new("col2".into(), &Vec::<String>::new()).into(),
396
])
397
.unwrap();
398
399
assert_dataframe_equal!(&df1, &df2);
400
}
401
402
#[test]
403
#[should_panic(expected = "value mismatch")]
404
fn test_dataframe_single_row_mismatch() {
405
let df1 = DataFrame::new_infer_height(vec![
406
Series::new("col1".into(), &[42]).into(),
407
Series::new("col2".into(), &["value"]).into(),
408
Series::new("col3".into(), &[true]).into(),
409
])
410
.unwrap();
411
412
let df2 = DataFrame::new_infer_height(vec![
413
Series::new("col1".into(), &[42]).into(),
414
Series::new("col2".into(), &["different"]).into(),
415
Series::new("col3".into(), &[true]).into(),
416
])
417
.unwrap();
418
419
assert_dataframe_equal!(&df1, &df2);
420
}
421
422
#[test]
423
fn test_dataframe_single_row_match() {
424
let df1 = DataFrame::new_infer_height(vec![
425
Series::new("col1".into(), &[42]).into(),
426
Series::new("col2".into(), &["value"]).into(),
427
Series::new("col3".into(), &[true]).into(),
428
])
429
.unwrap();
430
431
let df2 = DataFrame::new_infer_height(vec![
432
Series::new("col1".into(), &[42]).into(),
433
Series::new("col2".into(), &["value"]).into(),
434
Series::new("col3".into(), &[true]).into(),
435
])
436
.unwrap();
437
438
assert_dataframe_equal!(&df1, &df2);
439
}
440
441
#[test]
442
#[should_panic(expected = "value mismatch")]
443
fn test_dataframe_null_values_mismatch() {
444
let df1 = DataFrame::new_infer_height(vec![
445
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
446
])
447
.unwrap();
448
449
let df2 = DataFrame::new_infer_height(vec![
450
Series::new("col1".into(), &[Some(1), Some(2), None]).into(),
451
])
452
.unwrap();
453
454
assert_dataframe_equal!(&df1, &df2);
455
}
456
457
#[test]
458
fn test_dataframe_null_values_match() {
459
let df1 = DataFrame::new_infer_height(vec![
460
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
461
])
462
.unwrap();
463
464
let df2 = DataFrame::new_infer_height(vec![
465
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
466
])
467
.unwrap();
468
469
assert_dataframe_equal!(&df1, &df2);
470
}
471
472
#[test]
473
#[should_panic(expected = "value mismatch")]
474
fn test_dataframe_nan_values_mismatch() {
475
let df1 = DataFrame::new_infer_height(vec![
476
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
477
])
478
.unwrap();
479
480
let df2 = DataFrame::new_infer_height(vec![
481
Series::new("col1".into(), &[1.0, 2.0, f64::NAN]).into(),
482
])
483
.unwrap();
484
485
assert_dataframe_equal!(&df1, &df2);
486
}
487
488
#[test]
489
fn test_dataframe_nan_values_match() {
490
let df1 = DataFrame::new_infer_height(vec![
491
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
492
])
493
.unwrap();
494
495
let df2 = DataFrame::new_infer_height(vec![
496
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
497
])
498
.unwrap();
499
500
assert_dataframe_equal!(&df1, &df2);
501
}
502
503
#[test]
504
#[should_panic(expected = "value mismatch")]
505
fn test_dataframe_infinity_values_mismatch() {
506
let df1 = DataFrame::new_infer_height(vec![
507
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
508
])
509
.unwrap();
510
511
let df2 = DataFrame::new_infer_height(vec![
512
Series::new("col1".into(), &[1.0, f64::NEG_INFINITY, 3.0]).into(),
513
])
514
.unwrap();
515
516
assert_dataframe_equal!(&df1, &df2);
517
}
518
519
#[test]
520
fn test_dataframe_infinity_values_match() {
521
let df1 = DataFrame::new_infer_height(vec![
522
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
523
])
524
.unwrap();
525
526
let df2 = DataFrame::new_infer_height(vec![
527
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
528
])
529
.unwrap();
530
531
assert_dataframe_equal!(&df1, &df2);
532
}
533
534
// Testing categorical operations
535
#[test]
536
#[should_panic(expected = "value mismatch")]
537
fn test_dataframe_categorical_as_string_mismatch() {
538
let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);
539
categorical1 = categorical1
540
.cast(&DataType::from_categories(Categories::global()))
541
.unwrap();
542
let df1 = DataFrame::new_infer_height(vec![categorical1.into()]).unwrap();
543
544
let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "e"]);
545
categorical2 = categorical2
546
.cast(&DataType::from_categories(Categories::global()))
547
.unwrap();
548
let df2 = DataFrame::new_infer_height(vec![categorical2.into()]).unwrap();
549
550
let options =
551
crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);
552
assert_dataframe_equal!(&df1, &df2, options);
553
}
554
555
#[test]
556
fn test_dataframe_categorical_as_string_match() {
557
let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);
558
categorical1 = categorical1
559
.cast(&DataType::from_categories(Categories::global()))
560
.unwrap();
561
let df1 = DataFrame::new_infer_height(vec![categorical1.into()]).unwrap();
562
563
let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "d"]);
564
categorical2 = categorical2
565
.cast(&DataType::from_categories(Categories::global()))
566
.unwrap();
567
let df2 = DataFrame::new_infer_height(vec![categorical2.into()]).unwrap();
568
569
let options =
570
crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);
571
assert_dataframe_equal!(&df1, &df2, options);
572
}
573
574
// Testing nested types
575
#[test]
576
#[should_panic(expected = "value mismatch")]
577
fn test_dataframe_nested_values_mismatch() {
578
let df1 = DataFrame::new_infer_height(vec![
579
Series::new(
580
"list_col".into(),
581
&[
582
Some(vec![1, 2, 3]),
583
Some(vec![4, 5, 6]),
584
None,
585
Some(vec![7, 8, 9]),
586
],
587
)
588
.into(),
589
])
590
.unwrap();
591
592
let df2 = DataFrame::new_infer_height(vec![
593
Series::new(
594
"list_col".into(),
595
&[
596
Some(vec![1, 2, 3]),
597
Some(vec![4, 5, 99]),
598
None,
599
Some(vec![7, 8, 9]),
600
],
601
)
602
.into(),
603
])
604
.unwrap();
605
606
assert_dataframe_equal!(&df1, &df2);
607
}
608
609
#[test]
610
fn test_dataframe_nested_values_match() {
611
let df1 = DataFrame::new_infer_height(vec![
612
Series::new(
613
"list_col".into(),
614
&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],
615
)
616
.into(),
617
])
618
.unwrap();
619
620
let df2 = DataFrame::new_infer_height(vec![
621
Series::new(
622
"list_col".into(),
623
&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],
624
)
625
.into(),
626
])
627
.unwrap();
628
629
assert_dataframe_equal!(&df1, &df2);
630
}
631
}
632
633