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 crate::panel::{PanelResult, RandomEffectsResult};
use statrs::distribution::{ChiSquared, ContinuousCDF};
pub struct HausmanTest;
impl HausmanTest {
/// Compara Fixed Effects vs Random Effects.
/// H0: Random Effects é consistente (preferível).
/// H1: Random Effects é inconsistente (use Fixed Effects).
pub fn compare(fe: &PanelResult, re: &RandomEffectsResult) -> String {
let k = fe.params.len();
// 1. Diferença dos Betas (b_fe - b_re)
let diff_beta = &fe.params - &re.params;
// 2. Diferença das Variâncias (Var_fe - Var_re)
// Nota: Simplificação usando apenas a diagonal (assume covariância cruzada nula para o teste simples)
// O teste completo exigiria as matrizes de covariância completas, mas a diagonal é um bom proxy.
let var_fe = fe.std_errors.mapv(|s| s.powi(2));
let var_re = re.std_errors.mapv(|s| s.powi(2));
let diff_var = &var_fe - &var_re;
// 3. Estatística Chi2 (Forma Quadrática)
// H = (b_diff)' * (Var_diff)^-1 * (b_diff)
// Como estamos usando diagonal, simplifica para soma ponderada
let mut chi2_stat = 0.0;
for i in 0..k {
if diff_var[i] > 0.0 {
chi2_stat += (diff_beta[i].powi(2)) / diff_var[i];
}
}
// 4. P-Valor
let dist = ChiSquared::new(k as f64).unwrap();
let p_value = 1.0 - dist.cdf(chi2_stat);
// Formatar Saída
let recommendation = if p_value < 0.05 {
"Reject H0. Use FIXED EFFECTS (RE is inconsistent)."
} else {
"Fail to reject H0. Use RANDOM EFFECTS (it is efficient)."
};
format!(
"\n=== Hausman Test ===\nChi2 Statistic: {:.4}\nP-Value: {:.4}\nResult: {}",
chi2_stat, p_value, recommendation
)
}
}