use crate::{OaxacaBuilder, OaxacaError};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct JmpDecomposition {
pub total_change: f64,
pub quantity_effect: f64,
pub price_effect: f64,
pub gap_effect: f64,
}
impl JmpDecomposition {
pub fn summary(&self) {
println!("Juhn-Murphy-Pierce (JMP) Decomposition of Changes");
println!("==================================================");
println!("Total Change in Gap: {:.4}", self.total_change);
println!(" Quantity Effect: {:.4}", self.quantity_effect);
println!(" Price Effect: {:.4}", self.price_effect);
println!(" Gap Effect: {:.4}", self.gap_effect);
}
}
pub fn decompose_changes(
builder_t1: &OaxacaBuilder,
builder_t2: &OaxacaBuilder,
) -> Result<JmpDecomposition, OaxacaError> {
let results_t1 = builder_t1.run()?;
let results_t2 = builder_t2.run()?;
let gap_t1 = results_t1.total_gap;
let gap_t2 = results_t2.total_gap;
let total_change = gap_t2 - gap_t1;
let diff_x_t1 = results_t1.xa_mean() - results_t1.xb_mean();
let diff_x_t2 = results_t2.xa_mean() - results_t2.xb_mean();
let beta_star_t1 = results_t1.beta_star();
let quantity_effect = (diff_x_t2 - diff_x_t1).dot(beta_star_t1);
let get_estimate = |res: &crate::TwoFoldResults, name: &str| -> f64 {
*res.aggregate()
.iter()
.find(|c| c.name() == name)
.map(|c| c.estimate())
.unwrap_or(&0.0)
};
let explained_change = get_estimate(results_t2.two_fold(), "explained")
- get_estimate(results_t1.two_fold(), "explained");
let price_effect = explained_change - quantity_effect;
let unexplained_change = get_estimate(results_t2.two_fold(), "unexplained")
- get_estimate(results_t1.two_fold(), "unexplained");
let gap_effect = unexplained_change;
Ok(JmpDecomposition {
total_change,
quantity_effect,
price_effect,
gap_effect,
})
}