---
title: "A Decade of Interventions Targeting the Social Determinants of Mental Health"
subtitle: "Analyzing Individual vs. Systems, First vs. Second-Order Change"
authors:
  - name: Edie Gobel
  - name: Connor Sayle
  - name: Kaila Pelton-Flavin
  - name: Alejandro Baez
  - name: Shane McCarty, PhD
  - name: Lisa Cosgrove, PhD
abstract: |
  In 2014, the mental health field began a shift from treatment-focused, individual-level mental health interventions toward promotion/prevention approaches to address social determinants of mental health (SDoMH: see WHO, 2014) that facilitate community mental health (Nelson, Kloos & Ornelas, 2014) and population mental health (Dodge et al., 2024). Recent reviews of mental health interventions targeting social factors demonstrate the effectiveness of housing, cash transfer programs, and psychosocial interventions (e.g., Oswald et al., 2023). However, these reviews do not critically assess the extent to which neoliberal influences may be undermining efforts to realize SDMH goals of structural change. Specifically, Chater and Loewenstein (2022) argue that a historical over-emphasis on 'i-frame' (individual-level) interventions, often driven by corporate interests, diverts from 's-frame' (systems-level) interventions which could more effectively address population mental health. Numerous leaders in the field of community psychology have distinguished between *first-order change* and *second-order change* (Bond et al., 2017) along with *ameliorative* and *transformational* change (Prilleltensky, 2008). Despite this important dynamic, it is unknown whether the new paradigm brought about by the social determinants of mental health movement has resulted in interventions that have shifted away from the dominant individual-level to a systems-level. In a scoping review, we identify interventions targeting the social determinants of mental health/ill health and categorize them using two independent reviewers into i-frame or s-frame interventions. Additionally, coders consider the commercialization of interventions, such as conflicts of interest with authors and industry as well as copyrighted intervention approaches to account for the commercial drivers of health (Maani, Petticrew & Galea, 2022).
date: last-modified
number-sections: true
keywords: 
  - social determinants of mental health
  - scoping review
  - individual-level interventions
  - systems-level interventions
  - i-frame
  - s-frame
  - community psychology
  - population mental health
conflict-of-interest: |
  The authors declare no conflicts of interest.
acknowledgments: |
  We thank the BEAHR lab at UMASS-Boston.
format:
  html:
    code-fold: true
    code-summary: "Show the code"
    toc: true
    number-sections: true
execute:
  echo: false
  warning: false
  message: false
---

# Introduction

A 2014 report by the World Health Organization on the Social Determinants of Mental Health called for the examinination of unfavorable social, economic, and environmental circumstances on population mental health. The mainstream adoption of the social determinants of health (SDOH) framework and its application to mental health (SDOMH: Compton & Shim, 2015) differs markedly from its origin story with a power-centric, social determination theory that emphasizes sociopolitical and structural factors as the primary cause of health and health inequity (Breilh, 2019). Oswald and colleagues (2024) categorized 101 systematic review articles on SDOMH interventions as: demographic, economic, environmental events, neighborhood, or sociocultural. However, this analysis fails to account for other categorical frameworks related to critical mental health and community mental health studies. 

In critical community psychology and across the behavioral sciences, different analytical frameworks have been developed to categorize interventions based on: the target focus as individuals (i-frame) vs. systems (s-frame: Chater and Loewenstein 2022), the type of change as ameliorative/first-order change or transformative/second-order change (Prilleltensky, 2008), and the strategy of the intervention to reform the existing paradigm, offer an alternative, or build toward a new paradigm (Wright, 2015). In this study, we aim to determine the prevalence of SDOMH intervention types and the associations between target, change type, and strategy.

# Method

Review articles (n = 101) from a systematic review by Oswald et al., 2024 were uploaded into a screening tool, Rayyan. Three study team members (E.G., C.S., A.B.) generated the codebook and were assigned two-thirds of the 100 included articles to code. Inter-rater reliability shows fair to moderate agreement (κ = .342 to .517). Coders reviewed their codes with their assigned partner to identify and discuss coding discrepancies, arriving at a single choice (e.g., ALT) or a hybrid option when needed (e.g., REFORM-ALT).

# Results

## Definitions

![](table.png){fig-align="center"}

### Agreement

```{r kappa}
# Inter-Rater Reliability Analysis using Cohen's Kappa in R
# Load required libraries
library(readxl)      # For reading Excel files
library(psych)       # For Cohen's kappa calculation
library(irr)         # Alternative package for inter-rater reliability
library(dplyr)       # For data manipulation
library(knitr)       # For nice table formatting

# Read the Excel file
# Replace 'SDOMHCode.xlsx' with your actual file path
data <- read_excel("SDOMHCode.xlsx")

# Display basic info about the dataset
cat("Dataset dimensions:", nrow(data), "rows,", ncol(data), "columns\n")
cat("Sample size for analysis:", nrow(data), "observations\n\n")

# Define the variable pairs for comparison
variable_pairs <- list(
  "FIRST (Change)" = c("FIRST_rate1", "FIRST_rate2"),
  "SECOND (Change)" = c("SECOND_rate1", "SECOND_rate2"),
  "I (Individual Frame)" = c("I_rate1", "I_rate2"),
  "S (Structural Frame)" = c("S_rate1", "S_rate2"),
  "REFORM (Strategy)" = c("REFORM_rate1", "REFORM_rate2"),
  "ALT (Strategy)" = c("ALT_rate1", "ALT_rate2"),
  "BUILD (Strategy)" = c("BUILD_rate1", "BUILD_rate2")
)

# Function to calculate Cohen's kappa with additional statistics
calculate_kappa_stats <- function(rater1, rater2, var_name) {
  # Remove missing values
  complete_cases <- complete.cases(rater1, rater2)
  r1_clean <- rater1[complete_cases]
  r2_clean <- rater2[complete_cases]
  
  # Calculate basic agreement statistics
  n <- length(r1_clean)
  agreements <- sum(r1_clean == r2_clean)
  observed_agreement <- agreements / n
  
  # Calculate Cohen's kappa using irr package (more reliable)
  kappa_result <- tryCatch({
    kappa2(cbind(r1_clean, r2_clean))
  }, error = function(e) {
    return(list(value = NA, p.value = NA, se = NA))
  })
  
  # Alternative calculation using psych package for comparison
  kappa_psych <- tryCatch({
    cohen.kappa(cbind(r1_clean, r2_clean))
  }, error = function(e) {
    return(list(kappa = NA))
  })
  
  # Create results list - fixed the p.value access issue
  results <- list(
    variable = var_name,
    n = n,
    agreements = agreements,
    observed_agreement = observed_agreement,
    kappa_irr = ifelse(is.list(kappa_result) && !is.null(kappa_result$value), 
                       kappa_result$value, NA),
    kappa_psych = ifelse(is.list(kappa_psych) && !is.null(kappa_psych$kappa), 
                         kappa_psych$kappa, NA),
    p_value = ifelse(is.list(kappa_result) && !is.null(kappa_result$p.value), 
                     kappa_result$p.value, NA),
    se = ifelse(is.list(kappa_result) && !is.null(kappa_result$se), 
                kappa_result$se, NA)
  )
  
  return(results)
}

# Function to interpret kappa values
interpret_kappa <- function(kappa) {
  if (is.na(kappa)) return("Cannot calculate")
  if (kappa < 0.00) return("Poor")
  if (kappa <= 0.20) return("Slight")
  if (kappa <= 0.40) return("Fair")
  if (kappa <= 0.60) return("Moderate")
  if (kappa <= 0.80) return("Substantial")
  return("Almost Perfect")
}

# Calculate kappa for each variable pair
results_list <- list()
cat("=== INTER-RATER RELIABILITY ANALYSIS ===\n\n")

for (i in 1:length(variable_pairs)) {
  var_name <- names(variable_pairs)[i]
  rater1_col <- variable_pairs[[i]][1]
  rater2_col <- variable_pairs[[i]][2]
  
  # Check if columns exist in the dataset
  if (!(rater1_col %in% names(data)) || !(rater2_col %in% names(data))) {
    cat(sprintf("Warning: Columns %s or %s not found in dataset\n", rater1_col, rater2_col))
    next
  }
  
  # Extract the data for both raters
  rater1_data <- data[[rater1_col]]
  rater2_data <- data[[rater2_col]]
  
  # Calculate statistics
  stats <- calculate_kappa_stats(rater1_data, rater2_data, var_name)
  results_list[[i]] <- stats
  
  # Print individual results
  cat(sprintf("%s (%s vs %s):\n", var_name, rater1_col, rater2_col))
  cat(sprintf("  Cohen's κ = %.3f\n", stats$kappa_irr))
  cat(sprintf("  Interpretation: %s\n", interpret_kappa(stats$kappa_irr)))
  cat(sprintf("  Observed Agreement = %.1f%% (%d/%d)\n", 
              stats$observed_agreement * 100, stats$agreements, stats$n))
  if (!is.na(stats$p_value)) {
    cat(sprintf("  p-value = %.4f\n", stats$p_value))
  }
  if (!is.na(stats$se)) {
    cat(sprintf("  Standard Error = %.4f\n", stats$se))
  }
  cat(sprintf("  Sample Size = %d\n\n", stats$n))
}

# Filter out NULL results
results_list <- results_list[!sapply(results_list, is.null)]

# Create summary table
if (length(results_list) > 0) {
  summary_df <- data.frame(
    Variable = sapply(results_list, function(x) x$variable),
    Kappa = sapply(results_list, function(x) x$kappa_irr),
    Interpretation = sapply(results_list, function(x) interpret_kappa(x$kappa_irr)),
    Observed_Agreement = sapply(results_list, function(x) x$observed_agreement * 100),
    Agreements = sapply(results_list, function(x) paste0(x$agreements, "/", x$n)),
    N = sapply(results_list, function(x) x$n),
    P_Value = sapply(results_list, function(x) ifelse(is.na(x$p_value), "—", 
                                                      sprintf("%.4f", x$p_value)))
  )
  
  # Format the summary table
  summary_df$Kappa <- round(summary_df$Kappa, 3)
  summary_df$Observed_Agreement <- paste0(round(summary_df$Observed_Agreement, 1), "%")
  
  # Print formatted summary table
  cat("=== SUMMARY TABLE ===\n")
  print(kable(summary_df, 
              col.names = c("Variable", "Cohen's κ", "Interpretation", 
                           "Observed Agreement", "Agreements", "N", "p-value"),
              align = c("l", "c", "l", "c", "c", "c", "c")))
  
  # Calculate overall statistics
  valid_kappas <- summary_df$Kappa[!is.na(summary_df$Kappa)]
  if (length(valid_kappas) > 0) {
    avg_kappa <- mean(valid_kappas, na.rm = TRUE)
    avg_agreement <- mean(sapply(results_list, function(x) x$observed_agreement), na.rm = TRUE)
    
    cat("\n=== OVERALL STATISTICS ===\n")
    cat(sprintf("Average Cohen's κ: %.3f (%s)\n", avg_kappa, interpret_kappa(avg_kappa)))
    cat(sprintf("Average Observed Agreement: %.1f%%\n", avg_agreement * 100))
    cat(sprintf("Number of variable pairs analyzed: %d\n", length(results_list)))
    cat(sprintf("Total observations per variable: %d\n", nrow(data)))
  } else {
    cat("\nNo valid kappa values calculated. Please check your data.\n")
  }
} else {
  cat("No valid results obtained. Please check column names and data format.\n")
}

# Optional: Save results to CSV
if (exists("summary_df") && nrow(summary_df) > 0) {
  write.csv(summary_df, "kappa_results.csv", row.names = FALSE)
  cat("\nResults saved to 'kappa_results.csv'\n")
}
```

### Plots

#### Setup

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
```

#### Load Required Libraries

```{r libraries}
library(readxl)
library(dplyr)
library(ggplot2)
library(ggmosaic)
library(tidyr)
library(vcd)
library(RColorBrewer)
library(kableExtra)
library(irr)
library(psych)
```

#### Import Data

```{r import-data}
# Import the Excel file
raw_data <- read_excel("SDOMHCode.xlsx")

# Check if data imported correctly
cat("Dataset dimensions:", dim(raw_data), "\n")
cat("Column names:\n")
print(names(raw_data))

# Check data types
cat("\nData structure:\n")
#str(raw_data)

# Display first few rows
cat("\nFirst few rows:\n")
#head(raw_data)

# Check for any issues with the data object
cat("\nClass of data object:", class(raw_data), "\n")
```

#### Data Transformation

```{r transform-data}
# Create the transformed dataset with new categorical variables
data_transformed <- raw_data %>%
  mutate(
    # Create CHANGE variable based on FIRST and SECOND
    CHANGE = case_when(
      FIRST == 1 & SECOND == 1 ~ "BOTH",
      FIRST == 1 & SECOND == 0 ~ "FIRST",
      FIRST == 0 & SECOND == 1 ~ "SECOND",
      TRUE ~ "NEITHER"
    ),
    
    # Create FRAME variable based on IFRAME and SFRAME
    FRAME = case_when(
      IFRAME == 1 & SFRAME == 1 ~ "BOTH",
      IFRAME == 1 & SFRAME == 0 ~ "IFRAME",
      IFRAME == 0 & SFRAME == 1 ~ "SFRAME",
      TRUE ~ "NEITHER"
    ),
    
    # Create STRATEGY variable based on REFORM, ALT, and BUILD
    STRATEGY = case_when(
      REFORM == 1 & ALT == 1 & BUILD == 1 ~ "REFORM-ALT-BUILD",
      REFORM == 1 & ALT == 1 & BUILD == 0 ~ "REFORM-ALT",
      REFORM == 1 & ALT == 0 & BUILD == 1 ~ "REFORM-BUILD",
      REFORM == 0 & ALT == 1 & BUILD == 1 ~ "ALT-BUILD",
      REFORM == 1 & ALT == 0 & BUILD == 0 ~ "REFORM",
      REFORM == 0 & ALT == 1 & BUILD == 0 ~ "ALT",
      REFORM == 0 & ALT == 0 & BUILD == 1 ~ "BUILD",
      TRUE ~ "NEITHER"
    )
  )

# Display summary of new variables
cat("Summary of transformed variables:\n")
cat("\nCHANGE variable distribution:\n")
table(data_transformed$CHANGE)

cat("\nFRAME variable distribution:\n")
table(data_transformed$FRAME)

cat("\nSTRATEGY variable distribution:\n")
table(data_transformed$STRATEGY)

# Display first few rows with new variables
head(data_transformed %>% select(FIRST, SECOND, CHANGE, IFRAME, SFRAME, FRAME, REFORM, ALT, BUILD, STRATEGY))
```

#### Mosaic Plots

#### Data Cleaning and Preparation

```{r data-cleaning}
# First, let's clean and prepare the data properly
data_clean <- data_transformed %>%
  filter(!is.na(CHANGE) & !is.na(FRAME) & !is.na(STRATEGY)) %>%
  filter(CHANGE != "" & FRAME != "" & STRATEGY != "")

# Check data distribution
cat("Sample size after cleaning:", nrow(data_clean), "\n\n")

cat("CHANGE distribution:\n")
change_dist <- table(data_clean$CHANGE)
print(change_dist)
cat("Percentages:", round(prop.table(change_dist) * 100, 1), "\n\n")

cat("FRAME distribution:\n") 
frame_dist <- table(data_clean$FRAME)
print(frame_dist)
cat("Percentages:", round(prop.table(frame_dist) * 100, 1), "\n\n")

cat("STRATEGY distribution:\n")
strategy_dist <- table(data_clean$STRATEGY)
print(strategy_dist)
cat("Percentages:", round(prop.table(strategy_dist) * 100, 1), "\n\n")
```

```{r cross-tabs-basic}
## Cross-Tabulations
### Basic Cross-Tabulations

# Two-way cross-tabulations
cat("CHANGE × FRAME Cross-Tabulation:\n")
change_frame_table <- table(data_transformed$CHANGE, data_transformed$FRAME)
print(change_frame_table)

cat("\nCHANGE × STRATEGY Cross-Tabulation:\n")
change_strategy_table <- table(data_transformed$CHANGE, data_transformed$STRATEGY)
print(change_strategy_table)

cat("\nFRAME × STRATEGY Cross-Tabulation:\n")
frame_strategy_table <- table(data_transformed$FRAME, data_transformed$STRATEGY)
print(frame_strategy_table)
```

```{r create full table}
# Option 2: Flattened Table with All Combinations
all_combinations <- data_transformed %>%
  count(CHANGE, FRAME, STRATEGY) %>%
  arrange(desc(n)) %>%
  mutate(
    Combination = paste(CHANGE, FRAME, STRATEGY, sep = " × "),
    Percentage = round(n / sum(n) * 100, 1),
    Cumulative_Pct = round(cumsum(n) / sum(n) * 100, 1)
  ) %>%
  select(Combination, Count = n, Percentage, Cumulative_Pct)

kable(all_combinations,
      caption = "All CHANGE × FRAME × STRATEGY Combinations (Sorted by Frequency)",
      booktabs = TRUE) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))

print(all_combinations)
```

```{r cross-tabs-three-way}
# Three-way cross-tabulation
cat("Three-Way Cross-Tabulation: CHANGE × FRAME × STRATEGY\n")
three_way_table <- table(data_transformed$CHANGE, 
                        data_transformed$FRAME, 
                        data_transformed$STRATEGY)
print(three_way_table)
```

#### Treemap

```{r plot tree}
# Alternative Visualizations for CHANGE × FRAME × STRATEGY
library(ggplot2)
library(dplyr)
library(tidyr)
library(RColorBrewer)

# Prepare data
plot_data <- data_transformed %>%
  count(CHANGE, FRAME, STRATEGY) %>%
  mutate(
    CHANGE = factor(CHANGE, levels = c("FIRST", "BOTH", "SECOND", "NEITHER")),
    FRAME = factor(FRAME, levels = c("IFRAME", "BOTH", "SFRAME"))
  )

# Reorder the CHANGE variable to your desired order
data_transformed$CHANGE <- factor(data_transformed$CHANGE, 
                                 levels = c("FIRST", "BOTH", "SECOND", "NEITHER"))


# Improved ggplot2 script with custom layout requirements
# 1. STACKED BAR CHARTS with improved layout
p1.1 <- ggplot(plot_data, aes(x = STRATEGY, y = n, fill = CHANGE)) +
  geom_bar(stat = "identity", width = 0.8) +
  
  # Custom faceting with specified order: IFRAME, SFRAME, BOTH
  facet_wrap(~ factor(FRAME, levels = c("IFRAME", "SFRAME", "BOTH")), 
             labeller = labeller(FRAME = c("IFRAME" = "IFRAME", 
                                         "SFRAME" = "SFRAME", 
                                         "BOTH" = "BOTH")),
             strip.position = "bottom") +
  
  # Custom x-axis ordering: ALT, REFORM, REFORM-ALT, NEITHER
  scale_x_discrete(limits = c("ALT", "REFORM", "REFORM-ALT", "NEITHER"),
                   drop = FALSE) +
  
  # Remove grid and set transparent background
  theme_minimal() +
  theme(
    # Remove grid lines
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    
    # Transparent background
    plot.background = element_rect(fill = "transparent", color = NA),
    panel.background = element_rect(fill = "transparent", color = NA),
    
    axis.text.y = element_text(size = 12),
    
    # Rotate x-axis labels for better readability
    axis.text.x = element_text(angle = 45, hjust = 1),
    
    # Center title and subtitle
    plot.title = element_text(hjust = 0.5, size = 14),
    plot.subtitle = element_text(hjust = 0.5, size = 10),
    
    # Optional: make strip background transparent too
    strip.background = element_rect(fill = "transparent", color = NA),
    
    # Move legend position (x, y coordinates where 0,0 is bottom-left and 1,1 is top-right)
    legend.position = c(0.88, 0.75),  # Move right about 1 inch and up 0.5 inch
    legend.background = element_rect(fill = "transparent", color = NA)
  ) +
  
  # Labels and styling
  labs(
    title = "SDOMH INTERVENTION TYPES",
    subtitle = "Associations by Frame, Change, Strategy",
    x = "", 
    y = "Frequency of Systematic Reviews", 
    fill = "CHANGE"
  ) +
  
  # Color palette
  scale_fill_brewer(palette = "Set3")

# Print the plot
print(p1.1)

# Optional: Save with transparent background and dimensions matching screenshot
ggsave("plot.png", p1.1, bg = "transparent", width = 14, height = 8, dpi = 300)
```

```{r heatmap}
# 3. HEAT MAP (Great for seeing all combinations)
p3 <- plot_data %>%
  unite("FRAME_STRATEGY", FRAME, STRATEGY, sep = " × ") %>%
  ggplot(aes(x = FRAME_STRATEGY, y = CHANGE, fill = n)) +
  geom_tile(color = "white", size = 0.5) +
  geom_text(aes(label = n), color = "white", fontweight = "bold", size = 3) +
  scale_fill_gradient(low = "lightblue", high = "darkblue", name = "Count") +
  labs(
    title = "Heat Map: All Three-Way Combinations",
    subtitle = "Numbers show frequency counts",
    x = "FRAME × STRATEGY", y = "CHANGE"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid = element_blank()
  )

print(p3)
```

```{r 6}
# 6. TREEMAP (Shows proportional areas)
# Install treemap if needed: install.packages("treemap")
library(treemap)

treemap_data <- plot_data %>%
  filter(n > 0) %>%
  mutate(label = paste(CHANGE, FRAME, STRATEGY, sep = "\n"))

treemap(treemap_data,
        index = c("CHANGE", "FRAME", "STRATEGY"),
        vSize = "n",
        type = "index",
        title = "Treemap: CHANGE × FRAME × STRATEGY",
        fontsize.labels = c(12, 10, 8),
        fontcolor.labels = "white",
        border.col = "white",
        palette = "Set3")
```

#### 

```{r 3 way}

# Now create the plot with the new ordering
plot.mosiac.three <- ggplot(data = data_transformed) +
  geom_mosaic(aes(x = product(STRATEGY, FRAME, CHANGE), 
                  fill = CHANGE)) +
  labs(
    title = "Three-Way Mosaic Plot: CHANGE × FRAME × STRATEGY",
    subtitle = "Showing associations between all three categorical variables",
    x = "STRATEGY and FRAME",
    y = "CHANGE"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(size = 4, angle = 90, hjust = 1, vjust = 0.5),
    plot.margin = margin(10, 10, 50, 10)
  ) +
  scale_fill_brewer(palette = "Set3")

ggsave(
  filename = "plot.mosiac.three.png",
  plot = plot.mosiac.three,
  device = "png",
  width = 35,
  height = 15,
  units = "cm",
  dpi = 300
)

plot.mosiac.three
```

```{r}

```

#### Alt Plots

```{r p1.1 improved v3}
# Improved ggplot2 script with custom layout requirements

# 1. STACKED BAR CHARTS with improved layout
p1.1 <- ggplot(plot_data, aes(x = STRATEGY, y = n, fill = CHANGE)) +
  geom_bar(stat = "identity", width = 0.8) +
  
  # Custom faceting with specified order: IFRAME, SFRAME, BOTH
  facet_wrap(~ factor(FRAME, levels = c("IFRAME", "SFRAME", "BOTH")), 
             labeller = labeller(FRAME = c("IFRAME" = "IFRAME", 
                                         "SFRAME" = "SFRAME", 
                                         "BOTH" = "BOTH")),
             strip.position = "bottom") +
  
  # Custom x-axis ordering: ALT, REFORM, REFORM-ALT, NEITHER
  scale_x_discrete(limits = c("ALT", "REFORM", "REFORM-ALT", "NEITHER"),
                   drop = FALSE) +
  
  # Remove grid and set transparent background
  theme_minimal() +
  theme(
    # Remove grid lines
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    
    # Transparent background
    plot.background = element_rect(fill = "transparent", color = NA),
    panel.background = element_rect(fill = "transparent", color = NA),
    
    # Rotate x-axis labels for better readability
    axis.text.x = element_text(angle = 45, hjust = 1),
    
    # Optional: make strip background transparent too
    strip.background = element_rect(fill = "transparent", color = NA)
  ) +
  
  # Labels and styling
  labs(
    title = "SDOMH Intervention Categorizations",
    subtitle = "Associations by Frame, Change, Strategy",
    x = "", 
    y = "Frequency of Systematic Reviews", 
    fill = "CHANGE"
  ) +
  
  # Color palette
  scale_fill_brewer(palette = "Set3")

# Print the plot
print(p1.1)

# Optional: Save with transparent background
ggsave("plot.png", p1.1, bg = "transparent", width = 12, height = 6, dpi = 300)

```

```         
```

```         
```

```         
```

```{r 4}
# 4. BALLOON PLOT (Size = frequency)
p4 <- ggplot(plot_data, aes(x = STRATEGY, y = interaction(CHANGE, FRAME))) +
  geom_point(aes(size = n, color = CHANGE), alpha = 0.7) +
  scale_size_continuous(range = c(2, 15), name = "Count") +
  scale_color_brewer(palette = "Set3", name = "CHANGE") +
  labs(
    title = "Balloon Plot: Three-Way Associations",
    subtitle = "Point size = frequency, Color = CHANGE, Y-axis = CHANGE × FRAME",
    x = "STRATEGY", y = "CHANGE × FRAME"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

print(p4)
```

```{r 5}
# 5. FACETED DOT PLOT
p5 <- ggplot(plot_data, aes(x = n, y = STRATEGY, color = CHANGE)) +
  geom_point(size = 4) +
  facet_wrap(~ FRAME, scales = "free") +
  labs(
    title = "Dot Plot: Frequency by STRATEGY and FRAME",
    subtitle = "Faceted by FRAME, colored by CHANGE",
    x = "Count", y = "STRATEGY", color = "CHANGE"
  ) +
  theme_minimal() +
  scale_color_brewer(palette = "Set3")

print(p5)
```

```         
```

```{r 8}
# 8. GROUPED BAR CHART (Alternative grouping)
p8 <- ggplot(plot_data, aes(x = FRAME, y = n, fill = STRATEGY)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~ CHANGE, scales = "free") +
  labs(
    title = "Grouped Bar Chart: Alternative View",
    subtitle = "Faceted by CHANGE, grouped by STRATEGY",
    x = "FRAME", y = "Count", fill = "STRATEGY"
  ) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set2")

print(p8)
```

```{r 9}
# 9. SIMPLE TABLE VISUALIZATION (Clean and clear)
library(gt)

table_viz <- plot_data %>%
  pivot_wider(names_from = STRATEGY, values_from = n, values_fill = 0) %>%
  arrange(CHANGE, FRAME) %>%
  gt() %>%
  tab_header(
    title = "Three-Way Cross-Tabulation",
    subtitle = "CHANGE × FRAME × STRATEGY"
  ) %>%
  data_color(
    columns = c(ALT, NEITHER, REFORM, `REFORM-ALT`),
    colors = scales::col_numeric(
      palette = c("white", "darkblue"),
      domain = NULL
    )
  )

```

```{r 10}

# 10. SUMMARY: Which visualization to choose?

cat("VISUALIZATION RECOMMENDATIONS:\n\n")
cat("For UNBALANCED data (like yours):\n")
cat("✓ Stacked bar charts (p1, p2) - Best overall\n")
cat("✓ Heat map (p3) - Shows all combinations clearly\n")
cat("✓ Table visualization - Most accurate\n\n")

cat("For BALANCED data:\n")
cat("✓ Mosaic plots\n")
cat("✓ Alluvial diagrams\n")
cat("✓ Treemaps\n\n")

cat("For PRESENTATION:\n")
cat("✓ Clean heat map (p3)\n")
cat("✓ Proportional stacked bars (p2)\n")
cat("✓ Simple table with color coding\n")
```

# Discussion

The resulting categorizations of SDOMH interventions using critical community and behavioral science frameworks demonstrate the ubiquity of ameliorative, individual-level interventions rather than the more radical, socio-structural analysis associated with social determination theory (Breilh, 2019).  Iframe, first-order change interventions offering an alternative to or as a reform for the biomedical paradigm were the most common intervention type (85%, n = 85). Interestingly, all sframe interventions were coded as second-order change interventions (n=3). The absence of interventions building toward a new paradigm beyond the biomedical mental health model is notable.

Future research should continue to examine prior SDOMH interventions and call for more alternative and/or building-type strategies to advance an SDOMH paradigm for mental health beyond the biomedical paradigm. 

## Future Directions

While numerous studies examine the social conditions of mental health (Compton & Shim, 2014), recent reviews highlight a critical gap in policy-level interventions within the SDoMH literature (Alegría et al., 2023; Kirkbride et al., 2024). This gap may reflect what Chater and Loewenstein (2022) describe as corporate interests driving researchers to prioritize 'i-frame' over 's-frame' interventions. Notably, the growth of the \$32.7 billion private, for-profit social determinants of health industry (Goldberg et al., 2024) suggests corporate actors are capitalizing on social interventions (see Maani, Petticrew & Galea, 2022).

Using PRISMA guidelines (Tricco, 2018), we plan to conduct a scoping review of 3484 peer-reviewed articles published between 2014-2024 in academic databases (PubMed, PsycINFO, Web of Science). Our search combines "mental health," "mental illness," "mental ill health," and "social determinants of health" to answer: 1) what is the relative prevalence of i-frame versus s-frame social interventions for mental health, and 2) how frequently are these interventions commercialized?

# References

-   Breilh, J. (2019). Critical Epidemiology in Latin America: Roots, Philosophical and Methodological Ruptures. In J. Vallverdú, A. Puyol, & A. Estany (Eds.), Philosophical and Methodological Debates in Public Health (pp. 21–45). Springer International Publishing. <https://doi.org/10.1007/978-3-030-28626-2_3>

-   Chater, N., & Loewenstein, G. (2023). The i-frame and the s-frame: How focusing on individual-level solutions has led behavioral public policy astray. Behavioral and Brain Sciences, 46, e147. <https://doi.org/10.1017/S0140525X22002023>

-   Compton, M. T., & Shim, R. S. (2015). The Social Determinants of Mental Health. FOCUS, 13(4), 419–425. <https://doi.org/10.1176/appi.focus.20150017>

-   Oswald, T. K., Nguyen, M. T., Mirza, L., Lund, C., Jones, H. G., Crowley, G., Aslanyan, D., Dean, K., Schofield, P., Hotopf, M., & Das-Munshi, J. (2024). Interventions targeting social determinants of mental disorders and the Sustainable Development Goals: A systematic review of reviews. Psychological Medicine, 1–25. <https://doi.org/10.1017/S0033291724000333>

-   Prilleltensky, I. (2008). The role of power in wellness, oppression, and liberation: The promise of psychopolitical validity. Journal of Community Psychology, 36(2), 116–136. <https://doi.org/10.1002/jcop.20225>

-   Wright, E. O. (2015, December 2). How to Be an Anticapitalist Today. Jacobin. [https://jacobin.com/2015/12/erik-olin-wright-real-utopias-anticapitalism-democray](https://jacobin.com/2015/12/erik-olin-wright-real-utopias-anticapitalism-democracy)
