///|
/// Delta-method uncertainty for a scalar transform.
pub fn delta_method(
mean_value : Double,
standard_error : Double,
transform : (Double) -> Double,
) -> MetricEstimate {
if standard_error < 0.0 {
abort("standard error must be non-negative")
}
let estimate = transform(mean_value)
let step = standard_error.max(1.0e-6)
let derivative = (transform(mean_value + step) - transform(mean_value - step)) /
(2.0 * step)
let output_error = derivative.abs() * standard_error
metric_estimate(
estimate~,
lower=estimate - 1.96 * output_error,
upper=estimate + 1.96 * output_error,
confidence_level=0.95,
)
}
///|
pub fn lognormal_mean_uncertainty(
mu : Double,
sigma : Double,
mu_error : Double,
sigma_error : Double,
) -> MetricEstimate {
propagate_independent_uncertainty([mu, sigma], [mu_error, sigma_error], values => {
@math.exp(values[0] + values[1] * values[1] / 2.0)
})
}
///|
pub fn confidence_to_standard_error(
lower : Double,
upper : Double,
confidence : Double,
) -> Double {
let z = standard_normal_inv(0.5 + confidence / 2.0)
(upper - lower) / (2.0 * z)
}
///|
pub fn combine_estimates(
estimates : Array[Double],
standard_errors : Array[Double],
) -> MetricEstimate {
if estimates.length() != standard_errors.length() || estimates.is_empty() {
abort("estimate arrays mismatch")
}
let mut precision = 0.0
let mut weighted = 0.0
for i in 0.. MetricEstimate {
let estimate = count.to_double() / exposure
let error = standard_normal_inv(0.5 + confidence / 2.0) *
estimate.max(1.0 / exposure).sqrt() /
exposure.sqrt()
metric_estimate(
estimate~,
lower=(estimate - error).max(0.0),
upper=estimate + error,
confidence_level=confidence,
)
}
///|
pub fn transform_interval(
metric : MetricEstimate,
transform : (Double) -> Double,
) -> MetricEstimate {
let transformed = transform(metric.estimate)
let lower = transform(metric.lower)
let upper = transform(metric.upper)
metric_estimate(
estimate=transformed,
lower=lower.min(upper),
upper=lower.max(upper),
confidence_level=metric.confidence_level,
)
}