def bca_interval(boot_stats, data, statistic, theta_hat, confidence_level=0.95):
alpha = 1.0 - confidence_level
# Bias correction z0
prop_less = np.mean(boot_stats < theta_hat)
# Clip to avoid infinity
prop_less = np.clip(prop_less, 1e-10, 1 - 1e-10)
z0 = float(sp_stats.norm.ppf(prop_less))
# Acceleration a
jack_stats = _jackknife(data, statistic)
mean_jack = jack_stats.mean()
diffs = mean_jack - jack_stats
num = (diffs**3).sum()
den = ((diffs**2).sum()) ** 1.5
a_hat = num / (6 * den) if den != 0 else 0.0
# Adjusted percentiles
def adjust_percentile(z_alpha):
num_adj = z0 + z_alpha
denom_adj = 1 - a_hat * num_adj
return float(sp_stats.norm.cdf(z0 + num_adj / denom_adj))
p_low = adjust_percentile(sp_stats.norm.ppf(alpha / 2))
p_high = adjust_percentile(sp_stats.norm.ppf(1 - alpha / 2))
return ConfidenceInterval(
low=float(np.percentile(boot_stats, 100 * p_low)),
high=float(np.percentile(boot_stats, 100 * p_high)),
method="bca",
)