Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-testing/src/asserts/series.rs
6940 views
1
/// Asserts that two series are equal according to the specified options.
2
///
3
/// This macro compares two Polars Series 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_series_equal;
13
/// use polars_testing::asserts::SeriesEqualOptions;
14
///
15
/// // Create two series to compare
16
/// let s1 = Series::new("a".into(), &[1, 2, 3]);
17
/// let s2 = Series::new("a".into(), &[1, 2, 3]);
18
///
19
/// // Assert with default options
20
/// assert_series_equal!(&s1, &s2);
21
///
22
/// // Assert with custom options
23
/// let options = SeriesEqualOptions::default()
24
/// .with_check_exact(true)
25
/// .with_check_dtypes(false);
26
/// assert_series_equal!(&s1, &s2, options);
27
/// ```
28
///
29
/// # Panics
30
///
31
/// Panics when the series are not equal according to the specified comparison criteria.
32
///
33
#[macro_export]
34
macro_rules! assert_series_equal {
35
($left:expr, $right:expr $(, $options:expr)?) => {
36
{
37
#[allow(unused_assignments)]
38
#[allow(unused_mut)]
39
let mut options = $crate::asserts::SeriesEqualOptions::default();
40
$(options = $options;)?
41
42
match $crate::asserts::assert_series_equal($left, $right, options) {
43
Ok(_) => {},
44
Err(e) => panic!("{}", e),
45
}
46
}
47
};
48
}
49
50
#[cfg(test)]
51
mod tests {
52
use polars_core::prelude::*;
53
54
// Testing default struct implementation
55
#[test]
56
fn test_series_equal_options() {
57
let options = crate::asserts::SeriesEqualOptions::default();
58
59
assert!(options.check_dtypes);
60
assert!(options.check_names);
61
assert!(options.check_order);
62
assert!(options.check_exact);
63
assert_eq!(options.rel_tol, 1e-5);
64
assert_eq!(options.abs_tol, 1e-8);
65
assert!(!options.categorical_as_str);
66
}
67
68
// Testing with basic parameters
69
#[test]
70
#[should_panic(expected = "length mismatch")]
71
fn test_series_length_mismatch() {
72
let s1 = Series::new("".into(), &[1, 2]);
73
let s2 = Series::new("".into(), &[1, 2, 3]);
74
75
assert_series_equal!(&s1, &s2);
76
}
77
78
#[test]
79
#[should_panic(expected = "name mismatch")]
80
fn test_series_names_mismatch() {
81
let s1 = Series::new("s1".into(), &[1, 2, 3]);
82
let s2 = Series::new("s2".into(), &[1, 2, 3]);
83
84
assert_series_equal!(&s1, &s2);
85
}
86
87
#[test]
88
fn test_series_check_names_false() {
89
let s1 = Series::new("s1".into(), &[1, 2, 3]);
90
let s2 = Series::new("s2".into(), &[1, 2, 3]);
91
92
let options = crate::asserts::SeriesEqualOptions::default().with_check_names(false);
93
94
assert_series_equal!(&s1, &s2, options);
95
}
96
97
#[test]
98
#[should_panic(expected = "dtype mismatch")]
99
fn test_series_dtype_mismatch() {
100
let s1 = Series::new("".into(), &[1, 2, 3]);
101
let s2 = Series::new("".into(), &["1", "2", "3"]);
102
103
assert_series_equal!(&s1, &s2);
104
}
105
106
#[test]
107
fn test_series_check_dtypes_false() {
108
let s1 = Series::new("s1".into(), &[1, 2, 3]);
109
let s2 = Series::new("s1".into(), &[1.0, 2.0, 3.0]);
110
111
let options = crate::asserts::SeriesEqualOptions::default().with_check_dtypes(false);
112
113
assert_series_equal!(&s1, &s2, options);
114
}
115
116
// Testing basic equality
117
#[test]
118
#[should_panic(expected = "exact value mismatch")]
119
fn test_series_value_mismatch_int() {
120
let s1 = Series::new("".into(), &[1, 2, 3]);
121
let s2 = Series::new("".into(), &[2, 3, 4]);
122
123
assert_series_equal!(&s1, &s2);
124
}
125
126
#[test]
127
fn test_series_values_match_int() {
128
let s1 = Series::new("".into(), &[1, 2, 3]);
129
let s2 = Series::new("".into(), &[1, 2, 3]);
130
131
assert_series_equal!(&s1, &s2);
132
}
133
134
#[test]
135
#[should_panic(expected = "exact value mismatch")]
136
fn test_series_value_mismatch_str() {
137
let s1 = Series::new("".into(), &["foo", "bar"]);
138
let s2 = Series::new("".into(), &["moo", "car"]);
139
140
assert_series_equal!(&s1, &s2);
141
}
142
143
#[test]
144
fn test_series_values_match_str() {
145
let s1 = Series::new("".into(), &["foo", "bar"]);
146
let s2 = Series::new("".into(), &["foo", "bar"]);
147
148
assert_series_equal!(&s1, &s2);
149
}
150
151
#[test]
152
#[should_panic(expected = "exact value mismatch")]
153
fn test_series_values_mismatch_float() {
154
let s1 = Series::new("".into(), &[1.1, 2.2, 3.3]);
155
let s2 = Series::new("".into(), &[2.2, 3.3, 4.4]);
156
157
assert_series_equal!(&s1, &s2);
158
}
159
160
#[test]
161
fn test_series_values_match_float() {
162
let s1 = Series::new("".into(), &[1.1, 2.2, 3.3]);
163
let s2 = Series::new("".into(), &[1.1, 2.2, 3.3]);
164
165
assert_series_equal!(&s1, &s2);
166
}
167
168
// Testing float value precision equality
169
#[test]
170
#[should_panic(expected = "values not within tolerance")]
171
fn test_series_float_exceeded_tol() {
172
let s1 = Series::new("".into(), &[1.0, 2.2, 3.3]);
173
let s2 = Series::new("".into(), &[1.00012, 2.200025, 3.300035]);
174
175
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
176
177
assert_series_equal!(&s1, &s2, options);
178
}
179
180
#[test]
181
fn test_series_float_within_tol() {
182
let s1 = Series::new("".into(), &[1.0, 2.0, 3.0]);
183
let s2 = Series::new("".into(), &[1.000005, 2.000015, 3.000025]);
184
185
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
186
187
assert_series_equal!(&s1, &s2, options);
188
}
189
190
#[test]
191
fn test_series_float_exact_tolerance_boundary() {
192
let s1 = Series::new("".into(), &[1.0, 2.0, 3.0]);
193
let s2 = Series::new("".into(), &[1.0, 2.0 + 1e-5, 3.0]);
194
195
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
196
197
assert_series_equal!(&s1, &s2, options);
198
}
199
200
#[test]
201
fn test_series_float_custom_rel_tol() {
202
let s1 = Series::new("".into(), &[10.0, 100.0, 1000.0]);
203
let s2 = Series::new("".into(), &[10.05, 100.1, 1000.2]);
204
205
let options = crate::asserts::SeriesEqualOptions::default()
206
.with_check_exact(false)
207
.with_rel_tol(0.01);
208
209
assert_series_equal!(&s1, &s2, options);
210
}
211
212
#[test]
213
#[should_panic(expected = "values not within tolerance")]
214
fn test_series_float_custom_abs_tol() {
215
let s1 = Series::new("".into(), &[0.001, 0.01, 0.1]);
216
let s2 = Series::new("".into(), &[0.001, 0.02, 0.1]);
217
218
let options = crate::asserts::SeriesEqualOptions::default()
219
.with_check_exact(false)
220
.with_abs_tol(0.005);
221
222
assert_series_equal!(&s1, &s2, options);
223
}
224
225
// Testing equality with special values
226
#[test]
227
fn test_series_empty_equal() {
228
let s1 = Series::default();
229
let s2 = Series::default();
230
231
assert_series_equal!(&s1, &s2);
232
}
233
234
#[test]
235
fn test_series_nan_equal() {
236
let s1 = Series::new("".into(), &[f64::NAN, f64::NAN, f64::NAN]);
237
let s2 = Series::new("".into(), &[f64::NAN, f64::NAN, f64::NAN]);
238
239
assert_series_equal!(&s1, &s2);
240
}
241
242
#[test]
243
fn test_series_null_equal() {
244
let s1 = Series::new("".into(), &[None::<i32>, None::<i32>, None::<i32>]);
245
let s2 = Series::new("".into(), &[None::<i32>, None::<i32>, None::<i32>]);
246
247
assert_series_equal!(&s1, &s2);
248
}
249
250
#[test]
251
#[should_panic(expected = "exact value mismatch")]
252
fn test_series_infinity_values_mismatch() {
253
let s1 = Series::new("".into(), &[1.0, f64::INFINITY, 3.0]);
254
let s2 = Series::new("".into(), &[1.0, f64::NEG_INFINITY, 3.0]);
255
256
assert_series_equal!(&s1, &s2);
257
}
258
259
#[test]
260
fn test_series_infinity_values_match() {
261
let s1 = Series::new("".into(), &[1.0, f64::INFINITY, f64::NEG_INFINITY]);
262
let s2 = Series::new("".into(), &[1.0, f64::INFINITY, f64::NEG_INFINITY]);
263
264
assert_series_equal!(&s1, &s2);
265
}
266
267
// Testing null and nan counts for floats
268
#[test]
269
#[should_panic(expected = "null value mismatch")]
270
fn test_series_check_exact_false_null() {
271
let s1 = Series::new("".into(), &[Some(1.0), None::<f64>, Some(3.0)]);
272
let s2 = Series::new("".into(), &[Some(1.0), Some(2.0), Some(3.0)]);
273
274
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
275
276
assert_series_equal!(&s1, &s2, options);
277
}
278
279
#[test]
280
#[should_panic(expected = "nan value mismatch")]
281
fn test_series_check_exact_false_nan() {
282
let s1 = Series::new("".into(), &[1.0, f64::NAN, 3.0]);
283
let s2 = Series::new("".into(), &[1.0, 2.0, 3.0]);
284
285
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
286
287
assert_series_equal!(&s1, &s2, options);
288
}
289
290
// Testing sorting operations
291
#[test]
292
#[should_panic(expected = "exact value mismatch")]
293
fn test_series_sorting_unequal() {
294
let s1 = Series::new("".into(), &[Some(1), Some(2), Some(3), None::<i32>]);
295
let s2 = Series::new("".into(), &[Some(2), None::<i32>, Some(3), Some(1)]);
296
297
let options = crate::asserts::SeriesEqualOptions::default();
298
299
assert_series_equal!(&s1, &s2, options);
300
}
301
302
#[test]
303
fn test_series_sorting_equal() {
304
let s1 = Series::new("".into(), &[Some(1), Some(2), Some(3), None::<i32>]);
305
let s2 = Series::new("".into(), &[Some(2), None::<i32>, Some(3), Some(1)]);
306
307
let options = crate::asserts::SeriesEqualOptions::default().with_check_order(false);
308
309
assert_series_equal!(&s1, &s2, options);
310
}
311
312
// Testing categorical operations
313
#[test]
314
#[should_panic(expected = "exact value mismatch")]
315
fn test_series_categorical_mismatch() {
316
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
317
.cast(&DataType::from_categories(Categories::global()))
318
.unwrap();
319
let s2 = Series::new("".into(), &["apple", "orange", "cherry"])
320
.cast(&DataType::from_categories(Categories::global()))
321
.unwrap();
322
323
assert_series_equal!(&s1, &s2);
324
}
325
326
#[test]
327
fn test_series_categorical_match() {
328
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
329
.cast(&DataType::from_categories(Categories::global()))
330
.unwrap();
331
let s2 = Series::new("".into(), &["apple", "banana", "cherry"])
332
.cast(&DataType::from_categories(Categories::global()))
333
.unwrap();
334
335
assert_series_equal!(&s1, &s2);
336
}
337
338
#[test]
339
#[should_panic(expected = "exact value mismatch")]
340
fn test_series_categorical_str_mismatch() {
341
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
342
.cast(&DataType::from_categories(Categories::global()))
343
.unwrap();
344
let s2 = Series::new("".into(), &["apple", "orange", "cherry"])
345
.cast(&DataType::from_categories(Categories::global()))
346
.unwrap();
347
348
let options = crate::asserts::SeriesEqualOptions::default().with_categorical_as_str(true);
349
350
assert_series_equal!(&s1, &s2, options);
351
}
352
353
#[test]
354
fn test_series_categorical_str_match() {
355
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
356
.cast(&DataType::from_categories(Categories::global()))
357
.unwrap();
358
let s2 = Series::new("".into(), &["apple", "banana", "cherry"])
359
.cast(&DataType::from_categories(Categories::global()))
360
.unwrap();
361
362
let options = crate::asserts::SeriesEqualOptions::default().with_categorical_as_str(true);
363
364
assert_series_equal!(&s1, &s2, options);
365
}
366
367
// Testing equality of nested values
368
#[test]
369
#[should_panic(expected = "exact value mismatch")]
370
fn test_series_list_values_int_mismatch() {
371
let s1 = Series::new(
372
"".into(),
373
&[
374
[1, 2, 3].iter().collect::<Series>(),
375
[4, 5, 6].iter().collect::<Series>(),
376
[7, 8, 9].iter().collect::<Series>(),
377
],
378
);
379
380
let s2 = Series::new(
381
"".into(),
382
&[
383
[0, 2, 3].iter().collect::<Series>(),
384
[4, 7, 6].iter().collect::<Series>(),
385
[7, 8, 10].iter().collect::<Series>(),
386
],
387
);
388
389
assert_series_equal!(&s1, &s2);
390
}
391
392
#[test]
393
fn test_series_list_values_int_match() {
394
let s1 = Series::new(
395
"".into(),
396
&[
397
[1, 2, 3].iter().collect::<Series>(),
398
[4, 5, 6].iter().collect::<Series>(),
399
[7, 8, 9].iter().collect::<Series>(),
400
],
401
);
402
403
let s2 = Series::new(
404
"".into(),
405
&[
406
[1, 2, 3].iter().collect::<Series>(),
407
[4, 5, 6].iter().collect::<Series>(),
408
[7, 8, 9].iter().collect::<Series>(),
409
],
410
);
411
412
assert_series_equal!(&s1, &s2);
413
}
414
415
#[test]
416
#[should_panic(expected = "nested value mismatch")]
417
fn test_series_list_values_float_mismatch() {
418
let s1 = Series::new(
419
"".into(),
420
&[
421
[1.1, 2.0, 3.0].iter().collect::<Series>(),
422
[4.0, 5.0, 6.0].iter().collect::<Series>(),
423
[7.0, 8.0, 9.0].iter().collect::<Series>(),
424
],
425
);
426
427
let s2 = Series::new(
428
"".into(),
429
&[
430
[0.5, 2.0, 3.0].iter().collect::<Series>(),
431
[4.0, 7.5, 6.0].iter().collect::<Series>(),
432
[7.0, 8.0, 10.2].iter().collect::<Series>(),
433
],
434
);
435
436
assert_series_equal!(&s1, &s2);
437
}
438
439
#[test]
440
fn test_series_list_values_float_match() {
441
let s1 = Series::new(
442
"".into(),
443
&[
444
[1.1, 2.0, 3.0].iter().collect::<Series>(),
445
[4.0, 5.0, 6.0].iter().collect::<Series>(),
446
[7.0, 8.0, 9.0].iter().collect::<Series>(),
447
],
448
);
449
450
let s2 = Series::new(
451
"".into(),
452
&[
453
[1.1, 2.0, 3.0].iter().collect::<Series>(),
454
[4.0, 5.0, 6.0].iter().collect::<Series>(),
455
[7.0, 8.0, 9.0].iter().collect::<Series>(),
456
],
457
);
458
459
assert_series_equal!(&s1, &s2);
460
}
461
462
#[test]
463
#[should_panic(expected = "exact value mismatch")]
464
fn test_series_struct_values_str_mismatch() {
465
let field1 = Series::new("field1".into(), &["a", "d", "g"]);
466
let field2 = Series::new("field2".into(), &["b", "e", "h"]);
467
468
let s1_fields = [field1.clone(), field2];
469
let s1_struct =
470
StructChunked::from_series("".into(), field1.len(), s1_fields.iter()).unwrap();
471
let s1 = s1_struct.into_series();
472
473
let field1_alt = Series::new("field1".into(), &["a", "DIFFERENT", "g"]);
474
let field2_alt = Series::new("field2".into(), &["b", "e", "h"]);
475
476
let s2_fields = [field1_alt.clone(), field2_alt];
477
let s2_struct =
478
StructChunked::from_series("".into(), field1_alt.len(), s2_fields.iter()).unwrap();
479
let s2 = s2_struct.into_series();
480
481
assert_series_equal!(&s1, &s2);
482
}
483
484
#[test]
485
fn test_series_struct_values_str_match() {
486
let field1 = Series::new("field1".into(), &["a", "d", "g"]);
487
let field2 = Series::new("field2".into(), &["b", "e", "h"]);
488
489
let s1_fields = [field1.clone(), field2.clone()];
490
let s1_struct =
491
StructChunked::from_series("".into(), field1.len(), s1_fields.iter()).unwrap();
492
let s1 = s1_struct.into_series();
493
494
let s2_fields = [field1.clone(), field2];
495
let s2_struct =
496
StructChunked::from_series("".into(), field1.len(), s2_fields.iter()).unwrap();
497
let s2 = s2_struct.into_series();
498
499
assert_series_equal!(&s1, &s2);
500
}
501
502
#[test]
503
#[should_panic(expected = "exact value mismatch")]
504
fn test_series_struct_values_mixed_mismatch() {
505
let id = Series::new("id".into(), &[1, 2, 3]);
506
let value = Series::new("value".into(), &["a", "b", "c"]);
507
let active = Series::new("active".into(), &[true, false, true]);
508
509
let s1_fields = [id.clone(), value.clone(), active.clone()];
510
let s1_struct = StructChunked::from_series("".into(), id.len(), s1_fields.iter()).unwrap();
511
let s1 = s1_struct.into_series();
512
513
let id_alt = Series::new("id".into(), &[1, 99, 3]);
514
let s2_fields = [id_alt, value, active];
515
let s2_struct = StructChunked::from_series("".into(), id.len(), s2_fields.iter()).unwrap();
516
let s2 = s2_struct.into_series();
517
518
assert_series_equal!(&s1, &s2);
519
}
520
521
#[test]
522
fn test_series_struct_values_mixed_match() {
523
let id = Series::new("id".into(), &[1, 2, 3]);
524
let value = Series::new("value".into(), &["a", "b", "c"]);
525
let active = Series::new("active".into(), &[true, false, true]);
526
527
let s1_fields = [id.clone(), value.clone(), active.clone()];
528
let s1_struct = StructChunked::from_series("".into(), id.len(), s1_fields.iter()).unwrap();
529
let s1 = s1_struct.into_series();
530
531
let s2_fields = [id.clone(), value, active];
532
let s2_struct = StructChunked::from_series("".into(), id.len(), s2_fields.iter()).unwrap();
533
let s2 = s2_struct.into_series();
534
535
assert_series_equal!(&s1, &s2);
536
}
537
538
// Testing equality of deeply nested values
539
#[test]
540
#[should_panic(expected = "nested value mismatch")]
541
fn test_deeply_nested_list_float_mismatch() {
542
let inner_list_1 = Series::new("inner".into(), &[1.0, 2.0]);
543
let outer_list_1 = Series::new("outer".into(), &[inner_list_1]);
544
let s1 = Series::new("nested".into(), &[outer_list_1]);
545
546
let inner_list_2 = Series::new("inner".into(), &[1.0, 3.0]);
547
let outer_list_2 = Series::new("outer".into(), &[inner_list_2]);
548
let s2 = Series::new("nested".into(), &[outer_list_2]);
549
550
assert_series_equal!(&s1, &s2);
551
}
552
#[test]
553
fn test_deeply_nested_list_float_match() {
554
let inner_list_1 = Series::new("".into(), &[1.0, 2.0]);
555
let outer_list_1 = Series::new("".into(), &[inner_list_1]);
556
557
let s1 = Series::new("".into(), &[outer_list_1]);
558
559
let inner_list_2 = Series::new("".into(), &[1.0, 2.0]);
560
let outer_list_2 = Series::new("".into(), &[inner_list_2]);
561
let s2 = Series::new("".into(), &[outer_list_2]);
562
563
assert_series_equal!(&s1, &s2);
564
}
565
566
// Testing equality of temporal types
567
#[test]
568
#[should_panic(expected = "exact value mismatch")]
569
fn test_series_datetime_values_mismatch() {
570
let dt1: i64 = 1672567200000000000;
571
let dt2: i64 = 1672653600000000000;
572
let dt3: i64 = 1672657200000000000;
573
574
let s1 = Series::new("".into(), &[dt1, dt2])
575
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
576
.unwrap();
577
let s2 = Series::new("".into(), &[dt1, dt3])
578
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
579
.unwrap();
580
581
assert_series_equal!(&s1, &s2);
582
}
583
584
#[test]
585
fn test_series_datetime_values_match() {
586
let dt1: i64 = 1672567200000000000;
587
let dt2: i64 = 1672653600000000000;
588
589
let s1 = Series::new("".into(), &[dt1, dt2])
590
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
591
.unwrap();
592
let s2 = Series::new("".into(), &[dt1, dt2])
593
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
594
.unwrap();
595
596
assert_series_equal!(&s1, &s2);
597
}
598
599
// Testing equality of decimal types
600
#[test]
601
#[should_panic(expected = "exact value mismatch")]
602
fn test_series_decimal_values_mismatch() {
603
let s1 = Series::new("".into(), &[1, 2])
604
.cast(&DataType::Decimal(Some(10), Some(2)))
605
.unwrap();
606
let s2 = Series::new("".into(), &[1, 3])
607
.cast(&DataType::Decimal(Some(10), Some(2)))
608
.unwrap();
609
610
assert_series_equal!(&s1, &s2);
611
}
612
613
#[test]
614
fn test_series_decimal_values_match() {
615
let s1 = Series::new("".into(), &[1, 2])
616
.cast(&DataType::Decimal(Some(10), Some(2)))
617
.unwrap();
618
let s2 = Series::new("".into(), &[1, 2])
619
.cast(&DataType::Decimal(Some(10), Some(2)))
620
.unwrap();
621
622
assert_series_equal!(&s1, &s2);
623
}
624
625
// Testing equality of binary types
626
#[test]
627
#[should_panic(expected = "exact value mismatch")]
628
fn test_series_binary_values_mismatch() {
629
let s1 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
630
.cast(&DataType::Binary)
631
.unwrap();
632
let s2 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 7]])
633
.cast(&DataType::Binary)
634
.unwrap();
635
636
assert_series_equal!(&s1, &s2);
637
}
638
639
#[test]
640
fn test_series_binary_values_match() {
641
let s1 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
642
.cast(&DataType::Binary)
643
.unwrap();
644
let s2 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
645
.cast(&DataType::Binary)
646
.unwrap();
647
648
assert_series_equal!(&s1, &s2);
649
}
650
}
651
652