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
6940 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(vec![
350
Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),
351
])
352
.unwrap();
353
354
let df2 =
355
DataFrame::new(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()]).unwrap();
356
357
let options = crate::asserts::DataFrameEqualOptions::default().with_check_exact(true);
358
assert_dataframe_equal!(&df1, &df2, options);
359
}
360
361
#[test]
362
fn test_dataframe_numeric_tolerance_pass() {
363
let df1 = DataFrame::new(vec![
364
Series::new("col1".into(), &[1.0000001, 2.0000002, 3.0000003]).into(),
365
])
366
.unwrap();
367
368
let df2 =
369
DataFrame::new(vec![Series::new("col1".into(), &[1.0, 2.0, 3.0]).into()]).unwrap();
370
371
assert_dataframe_equal!(&df1, &df2);
372
}
373
374
// Testing equality with special values
375
#[test]
376
fn test_empty_dataframe_equal() {
377
let df1 = DataFrame::default();
378
let df2 = DataFrame::default();
379
380
assert_dataframe_equal!(&df1, &df2);
381
}
382
383
#[test]
384
fn test_empty_dataframe_schema_equal() {
385
let df1 = DataFrame::new(vec![
386
Series::new("col1".into(), &Vec::<i32>::new()).into(),
387
Series::new("col2".into(), &Vec::<String>::new()).into(),
388
])
389
.unwrap();
390
391
let df2 = DataFrame::new(vec![
392
Series::new("col1".into(), &Vec::<i32>::new()).into(),
393
Series::new("col2".into(), &Vec::<String>::new()).into(),
394
])
395
.unwrap();
396
397
assert_dataframe_equal!(&df1, &df2);
398
}
399
400
#[test]
401
#[should_panic(expected = "value mismatch")]
402
fn test_dataframe_single_row_mismatch() {
403
let df1 = DataFrame::new(vec![
404
Series::new("col1".into(), &[42]).into(),
405
Series::new("col2".into(), &["value"]).into(),
406
Series::new("col3".into(), &[true]).into(),
407
])
408
.unwrap();
409
410
let df2 = DataFrame::new(vec![
411
Series::new("col1".into(), &[42]).into(),
412
Series::new("col2".into(), &["different"]).into(),
413
Series::new("col3".into(), &[true]).into(),
414
])
415
.unwrap();
416
417
assert_dataframe_equal!(&df1, &df2);
418
}
419
420
#[test]
421
fn test_dataframe_single_row_match() {
422
let df1 = DataFrame::new(vec![
423
Series::new("col1".into(), &[42]).into(),
424
Series::new("col2".into(), &["value"]).into(),
425
Series::new("col3".into(), &[true]).into(),
426
])
427
.unwrap();
428
429
let df2 = DataFrame::new(vec![
430
Series::new("col1".into(), &[42]).into(),
431
Series::new("col2".into(), &["value"]).into(),
432
Series::new("col3".into(), &[true]).into(),
433
])
434
.unwrap();
435
436
assert_dataframe_equal!(&df1, &df2);
437
}
438
439
#[test]
440
#[should_panic(expected = "value mismatch")]
441
fn test_dataframe_null_values_mismatch() {
442
let df1 = DataFrame::new(vec![
443
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
444
])
445
.unwrap();
446
447
let df2 = DataFrame::new(vec![
448
Series::new("col1".into(), &[Some(1), Some(2), None]).into(),
449
])
450
.unwrap();
451
452
assert_dataframe_equal!(&df1, &df2);
453
}
454
455
#[test]
456
fn test_dataframe_null_values_match() {
457
let df1 = DataFrame::new(vec![
458
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
459
])
460
.unwrap();
461
462
let df2 = DataFrame::new(vec![
463
Series::new("col1".into(), &[Some(1), None, Some(3)]).into(),
464
])
465
.unwrap();
466
467
assert_dataframe_equal!(&df1, &df2);
468
}
469
470
#[test]
471
#[should_panic(expected = "value mismatch")]
472
fn test_dataframe_nan_values_mismatch() {
473
let df1 = DataFrame::new(vec![
474
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
475
])
476
.unwrap();
477
478
let df2 = DataFrame::new(vec![
479
Series::new("col1".into(), &[1.0, 2.0, f64::NAN]).into(),
480
])
481
.unwrap();
482
483
assert_dataframe_equal!(&df1, &df2);
484
}
485
486
#[test]
487
fn test_dataframe_nan_values_match() {
488
let df1 = DataFrame::new(vec![
489
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
490
])
491
.unwrap();
492
493
let df2 = DataFrame::new(vec![
494
Series::new("col1".into(), &[1.0, f64::NAN, 3.0]).into(),
495
])
496
.unwrap();
497
498
assert_dataframe_equal!(&df1, &df2);
499
}
500
501
#[test]
502
#[should_panic(expected = "value mismatch")]
503
fn test_dataframe_infinity_values_mismatch() {
504
let df1 = DataFrame::new(vec![
505
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
506
])
507
.unwrap();
508
509
let df2 = DataFrame::new(vec![
510
Series::new("col1".into(), &[1.0, f64::NEG_INFINITY, 3.0]).into(),
511
])
512
.unwrap();
513
514
assert_dataframe_equal!(&df1, &df2);
515
}
516
517
#[test]
518
fn test_dataframe_infinity_values_match() {
519
let df1 = DataFrame::new(vec![
520
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
521
])
522
.unwrap();
523
524
let df2 = DataFrame::new(vec![
525
Series::new("col1".into(), &[1.0, f64::INFINITY, 3.0]).into(),
526
])
527
.unwrap();
528
529
assert_dataframe_equal!(&df1, &df2);
530
}
531
532
// Testing categorical operations
533
#[test]
534
#[should_panic(expected = "value mismatch")]
535
fn test_dataframe_categorical_as_string_mismatch() {
536
let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);
537
categorical1 = categorical1
538
.cast(&DataType::from_categories(Categories::global()))
539
.unwrap();
540
let df1 = DataFrame::new(vec![categorical1.into()]).unwrap();
541
542
let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "e"]);
543
categorical2 = categorical2
544
.cast(&DataType::from_categories(Categories::global()))
545
.unwrap();
546
let df2 = DataFrame::new(vec![categorical2.into()]).unwrap();
547
548
let options =
549
crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);
550
assert_dataframe_equal!(&df1, &df2, options);
551
}
552
553
#[test]
554
fn test_dataframe_categorical_as_string_match() {
555
let mut categorical1 = Series::new("categories".into(), &["a", "b", "c", "d"]);
556
categorical1 = categorical1
557
.cast(&DataType::from_categories(Categories::global()))
558
.unwrap();
559
let df1 = DataFrame::new(vec![categorical1.into()]).unwrap();
560
561
let mut categorical2 = Series::new("categories".into(), &["a", "b", "c", "d"]);
562
categorical2 = categorical2
563
.cast(&DataType::from_categories(Categories::global()))
564
.unwrap();
565
let df2 = DataFrame::new(vec![categorical2.into()]).unwrap();
566
567
let options =
568
crate::asserts::DataFrameEqualOptions::default().with_categorical_as_str(true);
569
assert_dataframe_equal!(&df1, &df2, options);
570
}
571
572
// Testing nested types
573
#[test]
574
#[should_panic(expected = "value mismatch")]
575
fn test_dataframe_nested_values_mismatch() {
576
let df1 = DataFrame::new(vec![
577
Series::new(
578
"list_col".into(),
579
&[
580
Some(vec![1, 2, 3]),
581
Some(vec![4, 5, 6]),
582
None,
583
Some(vec![7, 8, 9]),
584
],
585
)
586
.into(),
587
])
588
.unwrap();
589
590
let df2 = DataFrame::new(vec![
591
Series::new(
592
"list_col".into(),
593
&[
594
Some(vec![1, 2, 3]),
595
Some(vec![4, 5, 99]),
596
None,
597
Some(vec![7, 8, 9]),
598
],
599
)
600
.into(),
601
])
602
.unwrap();
603
604
assert_dataframe_equal!(&df1, &df2);
605
}
606
607
#[test]
608
fn test_dataframe_nested_values_match() {
609
let df1 = DataFrame::new(vec![
610
Series::new(
611
"list_col".into(),
612
&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],
613
)
614
.into(),
615
])
616
.unwrap();
617
618
let df2 = DataFrame::new(vec![
619
Series::new(
620
"list_col".into(),
621
&[Some(vec![1, 2, 3]), Some(vec![]), None, Some(vec![7, 8, 9])],
622
)
623
.into(),
624
])
625
.unwrap();
626
627
assert_dataframe_equal!(&df1, &df2);
628
}
629
}
630
631