Skip to main content

iman_conover

Function iman_conover 

Source
pub fn iman_conover(
    pd: &PredictiveDistribution,
    correlation: &[f64],
    seed: u64,
) -> Result<PredictiveDistribution>
Expand description

Reorders each component’s draws so the components have (close to) the target correlation of normal scores, by Iman and Conover (1982). Every component keeps exactly its own draws; only their pairing across simulations changes.

Builds an n × m matrix whose columns are the normal scores Φ⁻¹(i / (n + 1)), shuffled independently (column j by StreamRng::new(seed, j); column 0 is not shuffled), transforms it to have exactly the correlation correlation, and gives each component’s draws the ranks of the matching column. The correlation of the result’s normal scores (van der Waerden) is then close to correlation, not exact, and Spearman’s rho is close to (6 / π) asin(correlation / 2), as for a Gaussian copula.

Use it to join results simulated separately, for example a reserve and a premium-risk distribution, without resimulating either.

use prospicio_prob::copula::iman_conover;
use prospicio_prob::{Empirical, KeyValue, PredictiveDistribution, Provenance};

// Two components, both 1..=1000, simulated independently.
let n = 1000;
let draws: Vec<f64> = (0..n).flat_map(|i| [i as f64, ((i * 7919) % n) as f64]).collect();
let pd = PredictiveDistribution::from_draws(
    vec!["lob".into()],
    vec![vec![KeyValue::Int(0)], vec![KeyValue::Int(1)]],
    draws,
    Provenance::new("example"),
)
.unwrap();
let joined = iman_conover(&pd, &[1.0, 0.7, 0.7, 1.0], 3).unwrap();
assert_eq!(joined.marginal(&vec![KeyValue::Int(1)]).unwrap().sorted(),
           pd.marginal(&vec![KeyValue::Int(1)]).unwrap().sorted());