1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use oaxaca_blinder::OaxacaBuilder;
use polars::prelude::*;
#[test]
fn test_weighted_decomposition() -> Result<(), Box<dyn std::error::Error>> {
// Create data
// Group A:
// 1. Outcome 10, x=1, w=1
// 2. Outcome 10, x=1, w=1
// 3. Outcome 2, x=0, w=10 (Heavy weight on low outcome)
// Unweighted Mean A = (10+10+2)/3 = 7.333
// Weighted Mean A = (10*1 + 10*1 + 2*10)/12 = 40/12 = 3.333
// Group B:
// 1. Outcome 5, x=0, w=1
// 2. Outcome 7, x=1, w=1
// Mean B = 6.0 (Unweighted and Weighted same since weights are 1)
let df = df!(
"outcome" => &[10.0, 10.0, 2.0, 5.0, 7.0],
"group" => &["A", "A", "A", "B", "B"],
"weight" => &[1.0, 1.0, 10.0, 1.0, 1.0],
"x" => &[1.0, 1.0, 0.0, 0.0, 1.0]
)?;
// Unweighted Gap = 7.333 - 6.0 = 1.333
// Weighted Gap = 3.333 - 6.0 = -2.666
// Run unweighted
let res_unweighted = OaxacaBuilder::new(df.clone(), "outcome", "group", "B")
.predictors(&["x"])
.bootstrap_reps(0)
.run()?;
println!("Unweighted Gap: {}", res_unweighted.total_gap());
assert!((res_unweighted.total_gap() - 1.333).abs() < 0.01);
// Run weighted
let res_weighted = OaxacaBuilder::new(df, "outcome", "group", "B")
.predictors(&["x"])
.weights("weight")
.bootstrap_reps(0)
.run()?;
println!("Weighted Gap: {}", res_weighted.total_gap());
assert!((res_weighted.total_gap() - (-2.666)).abs() < 0.01);
Ok(())
}