CLEAR Post Hoc

Author

Sarah Urbut

Published

January 7, 2024

Introduction

The role of inflammation in cardiovascular disease has emerged as a crucial therapeutic target, with several trials exploring anti-inflammatory agents for secondary prevention. Colchicine, a well-established anti-inflammatory medication with a long history in gout and pericarditis, has shown promise in recent cardiovascular trials. The COLCOT trial demonstrated a 23% reduction in cardiovascular events (HR 0.77, 95% CI 0.61-0.96), while the LoDoCo2 trial showed an even more substantial 31% reduction (HR 0.69, 95% CI 0.57-0.83).

The CLEAR trial aimed to further evaluate colchicine’s cardiovascular benefits but yielded results that appeared neutral at first glance (HR 0.99, 95% CI 0.85-1.16). However, traditional frequentist analyses may not fully capture the complete picture when considering the existing body of evidence. Bayesian methods offer a formal framework to incorporate prior knowledge from similar trials while evaluating new evidence.

We conducted a Bayesian reanalysis of the CLEAR trial data, incorporating prior information from COLCOT and LoDoCo2 trials. This approach allows us to: 1. Quantify the probability of both any benefit and clinically meaningful benefit 2. Update our previous knowledge with the new trial data 3. Provide more clinically interpretable results for decision-making

Our analysis used an optimistic prior based on previous trial results, reflecting the demonstrated benefits of colchicine in cardiovascular prevention, while allowing the CLEAR trial data to appropriately influence the final conclusions.

Weakly informative prior

Code
# Load required libraries
library(rstan) 
library(tidyverse)
library(bayesplot)

# Data from CLEAR trial
data <- list(
  N_colchicine = 3528,
  N_placebo = 3534,
  y_colchicine = 322,
  y_placebo = 327,
  log_hr = log(0.99),
  se_log_hr = (log(1.16) - log(0.85)) / (2 * 1.96)
)

# STAN model
stan_code <- "
data {
  int<lower=0> N_colchicine;
  int<lower=0> N_placebo;
  int<lower=0> y_colchicine;
  int<lower=0> y_placebo;
  real log_hr;
  real<lower=0> se_log_hr;
}

parameters {
  real theta;  // log hazard ratio
}

model {
  // Prior - weakly informative based on previous trials
  theta ~ normal(0, log(1.2));
  
  // Likelihood
  log_hr ~ normal(theta, se_log_hr);
  
  // Event counts - using binomial model as additional constraint
  y_colchicine ~ binomial(N_colchicine, inv_logit(logit(y_placebo * 1.0 / N_placebo) + theta));
}

generated quantities {
  real hr = exp(theta);
  real prob_benefit = theta < 0;
  real prob_meaningful = theta < log(0.9);
}
"

# Write model to file
writeLines(stan_code, "model.stan")

# Compile and fit model
mod=stan_model("model.stan")
Running /Library/Frameworks/R.framework/Resources/bin/R CMD SHLIB foo.c
using C compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.3)’
using SDK: ‘MacOSX15.0.sdk’
clang -arch arm64 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG   -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/Rcpp/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/unsupported"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/BH/include" -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/src/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppParallel/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/rstan/include" -DEIGEN_NO_DEBUG  -DBOOST_DISABLE_ASSERTS  -DBOOST_PENDING_INTEGER_LOG2_HPP  -DSTAN_THREADS  -DUSE_STANC3 -DSTRICT_R_HEADERS  -DBOOST_PHOENIX_NO_VARIADIC_EXPRESSION  -D_HAS_AUTO_PTR_ETC=0  -include '/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp'  -D_REENTRANT -DRCPP_PARALLEL_USE_TBB=1   -I/opt/R/arm64/include    -fPIC  -falign-functions=64 -Wall -g -O2  -c foo.c -o foo.o
In file included from <built-in>:1:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/Dense:1:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/Core:19:
/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/src/Core/util/Macros.h:679:10: fatal error: 'cmath' file not found
  679 | #include <cmath>
      |          ^~~~~~~
1 error generated.
make: *** [foo.o] Error 1
Code
fit <- sampling(mod,  # Changed from mod$sample to sampling()
  data = data,
  seed = 123,
  chains = 4,
  cores = 4,  # Changed from parallel_chains to cores
  warmup = 2000,  # Changed from iter_warmup to warmup
  iter = 4000    # Total iterations (warmup + sampling)
)

# Extract posterior draws
draws_df <- as.data.frame(fit)  # Changed from fit$draws() to as.data.frame()


# Calculate summary statistics
posterior_summary <- summary(fit)  # Changed from fit$summary()


# Print results
cat("\nBayesian Analysis Results:\n")

Bayesian Analysis Results:
Code
cat(sprintf("Posterior median HR: %.3f\n", median(draws_df$hr)))
Posterior median HR: 0.987
Code
ci <- quantile(draws_df$hr, probs = c(0.025, 0.975))
cat(sprintf("95%% Credible Interval: %.3f to %.3f\n", ci[1], ci[2]))
95% Credible Interval: 0.900 to 1.079
Code
cat(sprintf("Probability of any benefit (HR < 1): %.1f%%\n", mean(draws_df$hr < 1) * 100))
Probability of any benefit (HR < 1): 60.7%
Code
cat(sprintf("Probability of meaningful benefit (HR < 0.9): %.1f%%\n", mean(draws_df$hr < 0.9) * 100))
Probability of meaningful benefit (HR < 0.9): 2.5%
Code
# Calculate Bayes Factor
bf10 <- mean(draws_df$hr < 1) / mean(draws_df$hr >= 1)
cat(sprintf("\nBayes Factor (H1 vs H0): %.3f\n", bf10))

Bayes Factor (H1 vs H0): 1.545
Code
# Diagnostic plots
bayesplot::mcmc_trace(fit)

Code
bayesplot::mcmc_dens_overlay(fit)

Code
# Create publication-quality plot
ggplot(data.frame(hr = draws_df$hr), aes(x = hr)) +
  geom_density(fill = "skyblue", alpha = 0.5) +
  geom_vline(xintercept = 1, linetype = "dashed", color = "red") +
  geom_vline(xintercept = median(draws_df$hr), linetype = "solid", color = "blue") +
  theme_minimal() +
  labs(
    title = "Posterior Distribution of Hazard Ratio",
    subtitle = "CLEAR Trial Bayesian Analysis",
    x = "Hazard Ratio",
    y = "Density"
  ) +
  xlim(0.5, 1.5)

Meta analysis Prior

Code
# Load required libraries
library(rstan) 
library(tidyverse)
library(bayesplot)

# Data from CLEAR trial
data <- list(
  N_colchicine = 3528,
  N_placebo = 3534,
  y_colchicine = 322,
  y_placebo = 327,
  log_hr = log(0.99),
  se_log_hr = (log(1.16) - log(0.85)) / (2 * 1.96)
)

# STAN model with optimistic prior
stan_code <- "
data {
  int<lower=0> N_colchicine;
  int<lower=0> N_placebo;
  int<lower=0> y_colchicine;
  int<lower=0> y_placebo;
  real log_hr;
  real<lower=0> se_log_hr;
}

parameters {
  real theta;  // log hazard ratio
}

model {
  // Optimistic prior based on previous cardiovascular trials
  theta ~ normal(log(0.7), log(1.3));
  
  // Likelihood
  log_hr ~ normal(theta, se_log_hr);
  
  // Event counts
  y_colchicine ~ binomial(N_colchicine, inv_logit(logit(y_placebo * 1.0 / N_placebo) + theta));
}

generated quantities {
  real hr = exp(theta);
  real prob_benefit = theta < 0;
  real prob_meaningful = theta < log(0.9);
}
"

# Write model to file
writeLines(stan_code, "model_optimistic.stan")

# Compile and fit model
mod <- stan_model("model_optimistic.stan")
Running /Library/Frameworks/R.framework/Resources/bin/R CMD SHLIB foo.c
using C compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.3)’
using SDK: ‘MacOSX15.0.sdk’
clang -arch arm64 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG   -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/Rcpp/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/unsupported"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/BH/include" -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/src/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppParallel/include/"  -I"/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/rstan/include" -DEIGEN_NO_DEBUG  -DBOOST_DISABLE_ASSERTS  -DBOOST_PENDING_INTEGER_LOG2_HPP  -DSTAN_THREADS  -DUSE_STANC3 -DSTRICT_R_HEADERS  -DBOOST_PHOENIX_NO_VARIADIC_EXPRESSION  -D_HAS_AUTO_PTR_ETC=0  -include '/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp'  -D_REENTRANT -DRCPP_PARALLEL_USE_TBB=1   -I/opt/R/arm64/include    -fPIC  -falign-functions=64 -Wall -g -O2  -c foo.c -o foo.o
In file included from <built-in>:1:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/StanHeaders/include/stan/math/prim/fun/Eigen.hpp:22:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/Dense:1:
In file included from /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/Core:19:
/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/RcppEigen/include/Eigen/src/Core/util/Macros.h:679:10: fatal error: 'cmath' file not found
  679 | #include <cmath>
      |          ^~~~~~~
1 error generated.
make: *** [foo.o] Error 1
Code
fit <- sampling(mod,
  data = data,
  seed = 123,
  chains = 4,
  cores = 4,
  warmup = 2000,
  iter = 4000
)

# Extract and print results
draws_df <- as.data.frame(fit)
cat("\nBayesian Analysis Results (Optimistic Prior):\n")

Bayesian Analysis Results (Optimistic Prior):
Code
cat(sprintf("Posterior median HR: %.3f\n", median(draws_df$hr)))
Posterior median HR: 0.976
Code
ci <- quantile(draws_df$hr, probs = c(0.025, 0.975))
cat(sprintf("95%% Credible Interval: %.3f to %.3f\n", ci[1], ci[2]))
95% Credible Interval: 0.890 to 1.067
Code
cat(sprintf("Probability of any benefit (HR < 1): %.1f%%\n", mean(draws_df$hr < 1) * 100))
Probability of any benefit (HR < 1): 70.2%
Code
cat(sprintf("Probability of meaningful benefit (HR < 0.9): %.1f%%\n", mean(draws_df$hr < 0.9) * 100))
Probability of meaningful benefit (HR < 0.9): 4.0%
Code
# Publication-quality plot
ggplot(data.frame(hr = draws_df$hr), aes(x = hr)) +
  geom_density(fill = "skyblue", alpha = 0.5) +
  geom_vline(xintercept = 1, linetype = "dashed", color = "red") +
  geom_vline(xintercept = 0.7, linetype = "dotted", color = "darkgreen", size = 1) +
  geom_vline(xintercept = median(draws_df$hr), linetype = "solid", color = "blue") +
  theme_minimal() +
  labs(
    title = "Posterior Distribution of Hazard Ratio",
    subtitle = "CLEAR Trial Bayesian Analysis with Optimistic Prior",
    x = "Hazard Ratio",
    y = "Density",
    caption = "Green dotted line shows prior median HR=0.7"
  ) +
  xlim(0.5, 1.5)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.

Methods: Prior Specification

We conducted a Bayesian analysis using an optimistic prior distribution for the treatment effect. The prior was specified as a log-normal distribution centered at a hazard ratio of 0.7 (log(0.7)) with a scale parameter of log(1.3), reflecting previous evidence from cardiovascular trials of colchicine. This prior choice was informed by:

  1. The COLCOT trial, which demonstrated a hazard ratio of 0.77 (95% CI 0.61-0.96) for cardiovascular events
  2. The LoDoCo2 trial, showing a hazard ratio of 0.69 (95% CI 0.57-0.83)
  3. Biological evidence supporting colchicine’s anti-inflammatory mechanisms in cardiovascular disease
  4. Meta-analyses of colchicine in related cardiovascular conditions

This prior represents an optimistic but plausible effect size based on the existing evidence base, while maintaining sufficient uncertainty through its wide dispersion (scale parameter). To assess the sensitivity of our results to this prior choice, we also conducted analyses using [alternative prior specifications - details to be added].

Results: Bayesian Analysis

Using an optimistic prior centered at a hazard ratio of 0.7 (based on previous cardiovascular trials), the posterior median hazard ratio was 0.976 (95% credible interval: 0.890 to 1.067). While the probability of any benefit (HR < 1) was 70.2%, the probability of a clinically meaningful benefit (HR < 0.9) was only 4.0%. Despite incorporating prior optimism from previous trials, the posterior distribution remained centered near the null effect, suggesting that the CLEAR trial data provides strong evidence of minimal treatment effect.