# Check and install required packages automatically if missing
library(epitools)
library(tidyverse)
library(broom)

Introduction

This publication provides a step-by-step guide to calculating the Relative Risk (RR) in R using three distinct methodologies.

Relative Risk compares the probability of an outcome occurring in an exposed group versus an unexposed group:

\[RR = \frac{\text{Risk in Exposed Group}}{\text{Risk in Unexposed Group}} = \frac{A / (A + B)}{C / (C + D)}\]


Step 1: Define the Data Structure

We will create a standard \(2 \times 2\) contingency table.

For epidemiological packages like epitools to work perfectly without flipping results, we structure the matrix according to standard package conventions: * Row 1: Unexposed (Control) * Row 2: Exposed (Treatment) * Column 1: Outcome Negative (Healthy) * Column 2: Outcome Positive (Disease)

# Define the counts for our 2x2 table
# Row 1 (Unexposed): 80 Healthy, 20 Diseased
# Row 2 (Exposed):   60 Healthy, 40 Diseased
study_matrix <- matrix(c(80, 20, 
                         60, 40), 
                       nrow = 2, 
                       byrow = TRUE)

# Assign names for visual clarity
rownames(study_matrix) <- c("Unexposed", "Exposed")
colnames(study_matrix) <- c("No Disease", "Disease")

# Print the final table
study_matrix
##           No Disease Disease
## Unexposed         80      20
## Exposed           60      40

Step 2: Method 1 - Manual Calculation (By Hand)

To understand how the arithmetic works behind the scenes, we extract the values directly from our matrix and apply the formula.

# Extract table cells
unexposed_healthy  <- study_matrix[1, 1] # 80
unexposed_diseased <- study_matrix[1, 2] # 20
exposed_healthy    <- study_matrix[2, 1] # 60
exposed_diseased   <- study_matrix[2, 2] # 40

# Calculate risk per group
risk_unexposed <- unexposed_diseased / (unexposed_healthy + unexposed_diseased)
risk_exposed   <- exposed_diseased / (exposed_healthy + exposed_diseased)

# Calculate Relative Risk
manual_rr <- risk_exposed / risk_unexposed

# Output results
cat("Risk in Unexposed Group:", risk_unexposed, "\n")
## Risk in Unexposed Group: 0.2
cat("Risk in Exposed Group:  ", risk_exposed, "\n")
## Risk in Exposed Group:   0.4
cat("Calculated Relative Risk:", manual_rr, "\n")
## Calculated Relative Risk: 2

Step 3: Method 2 - Using the epitools Package

While manual calculation gives you the exact estimate point, it does not provide confidence intervals or \(p\)-values. The riskratio() function from the epitools package automates this.

We choose method = "wald" to get standard Wald confidence intervals.

# Run the risk ratio estimation
rr_analysis <- riskratio(study_matrix, method = "wald")

# View the full output structure
print(rr_analysis)
## $data
##           No Disease Disease Total
## Unexposed         80      20   100
## Exposed           60      40   100
## Total            140      60   200
## 
## $measure
##                         NA
## risk ratio with 95% C.I. estimate    lower    upper
##                Unexposed        1       NA       NA
##                Exposed          2 1.263006 3.167047
## 
## $p.value
##            NA
## two-sided    midp.exact fisher.exact  chi.square
##   Unexposed          NA           NA          NA
##   Exposed   0.002145336  0.003191817 0.002028231
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
# Extract only the Relative Risk point estimate and its 95% Confidence Intervals
rr_summary <- rr_analysis$measure
print(rr_summary)
##                         NA
## risk ratio with 95% C.I. estimate    lower    upper
##                Unexposed        1       NA       NA
##                Exposed          2 1.263006 3.167047

Interpretation: The output shows that the exposed group has an RR of 2 (95% CI: 1.26 - 3.17). This means individuals exposed to the factor are exactly 2 times more likely to develop the disease compared to the unexposed group.


Step 4: Method 3 - Advanced Adjusted RR via Regression

When you have real-world data, you usually need to control for confounding variables like age or sex. Standard logistic regression (glm with family = binomial) yields an Odds Ratio. To get an adjusted Relative Risk, you must fit a Log-Binomial Model using a log link function.

First, let’s transform our summary data into an individual-level data frame:

# Create individual patient data (100 unexposed, 100 exposed)
set.seed(42)
patient_data <- data.frame(
  exposure = c(rep("Unexposed", 100), rep("Exposed", 100)),
  disease  = c(rep(0, 80), rep(1, 20), rep(0, 60), rep(1, 40)),
  age      = round(runif(200, min = 18, max = 65)) # Simulating a confounder
)

# Convert exposure to a factor using clean string indexing
patient_data[["exposure"]] <- factor(patient_data[["exposure"]], levels = c("Unexposed", "Exposed"))

# Fit the log-binomial regression model
log_binomial_model <- glm(disease ~ exposure + age, 
                          data = patient_data, 
                          family = binomial(link = "log"))

# Exponentiate the regression coefficients to convert them to Relative Risks
relative_risks     <- exp(coef(log_binomial_model))
confidence_intervals <- exp(confint(log_binomial_model))

# Combine results into a neat summary table
regression_results <- cbind(RR = relative_risks, confidence_intervals)
print(regression_results)
##                        RR     2.5 %    97.5 %
## (Intercept)     0.3200208 0.1520058 0.6189747
## exposureExposed 1.9962886 1.2866566 3.2379459
## age             0.9887331 0.9744352 1.0035810

The estimate for exposureExposed represents your Relative Risk adjusted for the participant’s age.

Conclusion

Calculate Relative Risk

The Relative Risk measures the risk of the outcome in the exposed group divided by the risk in the unexposed group.

# Calculate risks using your actual matrix variable name
risk_exposed   <- study_matrix[2, 2] / (study_matrix[2, 1] + study_matrix[2, 2])
risk_unexposed <- study_matrix[1, 2] / (study_matrix[1, 1] + study_matrix[1, 2])

# Calculate Relative Risk (RR)
relative_risk <- risk_exposed / risk_unexposed
relative_risk
## [1] 2

Based on our fictional data, the Relative Risk is 2.