In Bayesian statistics, a credible interval is an interval within which an unobserved parameter falls with a certain probability. While there are many ways to construct credible intervals, the Highest Posterior Density (HPD) interval is particularly important because it represents the shortest possible interval for a given probability level.
The Highest Posterior Density (HPD) region of credibility \(100(1-\alpha)\%\) is the subset \(C\) of the parameter space \(\Theta\) such that:
\[P(\theta \in C) = 1-\alpha\]
For any \(\theta_1 \in C\) and \(\theta_2 \notin C\), \(p(\theta_1) \geq p(\theta_2)\)
In other words, the HPD region contains all points with the highest posterior density that collectively have probability \(1-\alpha\).
For a unimodal posterior distribution, the HPD interval satisfies two conditions:
The HPD interval is optimal in the sense that:
Let’s demonstrate the concept using a Gamma posterior distribution with shape parameter \(\alpha=3\) and rate parameter \(\beta=0.7\), which might arise from a conjugate prior for a Poisson likelihood.
First, let’s define our posterior distribution:
# Define the Gamma posterior distribution
shape <- 3
rate <- 0.7
prob <- 0.95
# Create a grid of values
x <- seq(0, qgamma(0.9999, shape, rate), length.out = 5000)
# Calculate density
density <- dgamma(x, shape, rate)
posterior <- data.frame(
theta = x,
density = density
)
# Plot the posterior
ggplot(posterior, aes(theta, density)) +
geom_line(linewidth = 1.2) +
labs(
title = "Gamma Posterior Distribution",
x = expression(theta),
y = "Posterior Density"
) +
theme_bw(base_size = 14)
# Function to calculate probability of an interval
interval_prob <- function(lower, upper) {
pgamma(upper, shape, rate) -
pgamma(lower, shape, rate)
}
We can create different 95% credible intervals by choosing different lower tail probabilities:
# Function to create 95% intervals
make_95_interval <- function(alpha) {
lower <- qgamma(alpha, shape, rate)
upper <- qgamma(alpha + prob, shape, rate)
data.frame(
lower = lower,
upper = upper,
length = upper - lower,
probability = interval_prob(lower, upper)
)
}
For a unimodal posterior, the HPD interval can be found by solving:
\[P(L<\theta<U)=0.95\] \[p(L)=p(U)\]
# Function to find HPD interval
hpd_function <- function(lower) {
upper <- qgamma(
pgamma(lower, shape, rate) + prob,
shape,
rate
)
dgamma(lower, shape, rate) -
dgamma(upper, shape, rate)
}
# Find the HPD lower bound
HPD.lower <- uniroot(
hpd_function,
lower = 0,
upper = qgamma(0.05, shape, rate)
)$root
# Calculate the HPD upper bound
HPD.upper <- qgamma(
pgamma(HPD.lower, shape, rate) + prob,
shape,
rate
)
# Create HPD interval data frame
HPD <- data.frame(
Type = "HPD (shortest)",
lower = HPD.lower,
upper = HPD.upper
)
HPD$length <- HPD$upper - HPD$lower
HPD$probability <- interval_prob(HPD$lower, HPD$upper)
Let’s create and compare several 95% credible intervals:
# Create different intervals
Equal <- make_95_interval(0.025)
Left_shift <- make_95_interval(0.005)
Right_shift <- make_95_interval(0.045)
# Add labels
Equal$Type <- "Equal tail"
Left_shift$Type <- "Shifted left"
Right_shift$Type <- "Shifted right"
# Combine all results
results <- bind_rows(
HPD,
Equal,
Left_shift,
Right_shift
)
# Format results
results <- results %>%
select(Type, lower, upper, length, probability) %>%
mutate(
length = round(length, 3),
probability = round(probability * 100, 2),
lower = round(lower, 3),
upper = round(upper, 3)
)
# Display results
knitr::kable(results,
caption = "Comparison of Different 95% Credible Intervals",
digits = 3,
align = "c"
)
| Type | lower | upper | length | probability |
|---|---|---|---|---|
| HPD (shortest) | 0.434 | 9.145 | 8.711 | 95 |
| Equal tail | 0.884 | 10.321 | 9.437 | 95 |
| Shifted left | 0.483 | 9.199 | 8.717 | 95 |
| Shifted right | 1.118 | 13.248 | 12.130 | 95 |
Let’s visualize how these different intervals cover the posterior distribution:
# Prepare data for plotting intervals
interval_data <- list(
HPD = HPD,
Equal = Equal,
Left = Left_shift,
Right = Right_shift
)
plot_data <- data.frame()
for(i in names(interval_data)) {
tmp <- interval_data[[i]]
tmp_density <- posterior %>%
filter(theta >= tmp$lower &
theta <= tmp$upper)
tmp_density$Type <- tmp$Type
plot_data <- bind_rows(
plot_data,
tmp_density
)
}
# Create the plot
ggplot(posterior, aes(theta, density)) +
geom_line(linewidth = 1.2) +
geom_area(
data = plot_data,
aes(theta, density),
fill = "steelblue",
alpha = 0.35
) +
geom_vline(
data = results,
aes(xintercept = lower, colour = Type),
linewidth = 1
) +
geom_vline(
data = results,
aes(xintercept = upper, colour = Type),
linewidth = 1
) +
facet_wrap(~Type, ncol = 2) +
labs(
title = "Same 95% Posterior Probability, Different Interval Lengths",
subtitle = "HPD interval is the shortest credible interval",
x = expression(theta),
y = "Posterior density"
) +
theme_bw(base_size = 12) +
theme(
legend.position = "none",
strip.background = element_rect(fill = "lightgray")
)
The HPD interval should be the shortest. Let’s verify this:
ggplot(results, aes(x = reorder(Type, length), y = length)) +
geom_col(fill = c("steelblue", "gray70", "gray70", "gray70")) +
geom_text(
aes(label = length),
vjust = -0.5,
size = 4
) +
labs(
title = "Length of 95% Credible Intervals",
subtitle = "HPD provides the shortest credible interval",
x = "Interval Type",
y = "Interval Length"
) +
theme_bw(base_size = 14)
HPD intervals are the shortest credible intervals for a given probability level
For symmetric unimodal posteriors (like normal), HPD equals the equal-tail interval
For skewed posteriors (like gamma), the equal-tail interval is not HPD
The HPD interval focuses on the most likely regions of the parameter space
HPD intervals are particularly useful when the posterior is skewed or multimodal
For unimodal posteriors, the HPD interval can be found by solving the density equality condition
For multimodal posteriors, the HPD region might consist of multiple disjoint intervals
In practice, HPD intervals are often computed using MCMC samples and density estimation
The HPD interval is invariant under monotone transformations of the parameter