epilepsy <- read_csv("/Users/drlakeshialegettejones/Downloads/dataset.csv") %>%
  clean_names()

glimpse(epilepsy)
## Rows: 1,000
## Columns: 30
## $ age                              <dbl> 15, 4, 36, 32, 29, 18, 14, 70, 12, 76…
## $ gender                           <chr> "Male", "Female", "Female", "Female",…
## $ weight                           <dbl> 48.3, 56.0, 44.8, 70.4, 54.5, 62.1, 5…
## $ height                           <dbl> 168.2, 174.5, 156.2, 180.4, 161.7, 16…
## $ medication_status                <chr> "On Medication", "On Medication", "On…
## $ alcohol_or_drug_use              <chr> "Yes", "No", "No", "No", "No", "No", …
## $ eeg_abnormality_detected         <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Y…
## $ mri_ct_scan_result               <chr> "Normal", "Abnormal", "Abnormal", "No…
## $ seizure_frequency                <dbl> 1, 1, 3, 0, 1, 1, 2, 1, 1, 5, 3, 1, 0…
## $ seizure_duration                 <dbl> 60, 30, 30, 60, 60, 30, 30, 30, 90, 3…
## $ seizure_type                     <chr> "Tonic-Clonic", "Generalized", "Tonic…
## $ aura_before_seizure              <chr> "No", "Yes", "Yes", "Yes", "No", "Yes…
## $ loss_of_consciousness            <chr> "Yes", "Yes", "No", "Yes", "Yes", "Ye…
## $ muscle_stiffness                 <chr> "No", "Yes", "Yes", "No", "Yes", "No"…
## $ jerky_movements                  <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Y…
## $ postictal_confusion              <chr> "Yes", "Yes", "No", "Yes", "Yes", "Ye…
## $ blank_stare_episodes             <chr> "No", "No", "Yes", "Yes", "No", "Yes"…
## $ eye_rolling                      <chr> "Yes", "No", "Yes", "No", "No", "No",…
## $ stress_or_anxiety_before_episode <chr> "Yes", "No", "Yes", "Yes", "Yes", "Ye…
## $ lack_of_sleep_before_episode     <chr> "Yes", "No", "No", "Yes", "No", "Yes"…
## $ flashing_lights_sensitivity      <chr> "Yes", "No", "No", "Yes", "No", "Yes"…
## $ loud_sound_sensitivity           <chr> "Yes", "No", "Yes", "Yes", "Yes", "No…
## $ missed_medication                <chr> "No", "No", "Yes", "No", "No", "No", …
## $ family_history_of_epilepsy       <chr> "No", "Yes", "Yes", "No", "Yes", "Yes…
## $ head_injury_history              <chr> "No", "Yes", "No", "No", "No", "No", …
## $ brain_tumor                      <chr> "No", "Yes", "No", "No", "Yes", "No",…
## $ history_of_stroke                <chr> "No", "No", "No", "No", "No", "No", "…
## $ genetic_disorder                 <chr> "No", "No", "No", "No", "No", "No", "…
## $ developmental_delay_in_children  <chr> "Yes", "No", "No", "No", "No", "No", …
## $ target_epilepsy_type             <chr> "Generalized", "Generalized", "Genera…
epilepsy <- epilepsy %>%
  mutate(
    age_group = case_when(
      age >= 12 & age <= 19 ~ "Adolescent",
      age >= 20 ~ "Adult",
      age < 12 ~ "Child"
    ),
    age_group = factor(age_group, levels = c("Adolescent", "Adult", "Child"))
  )

analysis_data <- epilepsy %>%
  filter(age_group %in% c("Adolescent", "Adult")) %>%
  mutate(age_group = droplevels(age_group))

analysis_data %>%
  count(age_group) %>%
  mutate(percent = round(100 * n / sum(n), 1))
## # A tibble: 2 × 3
##   age_group      n percent
##   <fct>      <int>   <dbl>
## 1 Adolescent    92    10.6
## 2 Adult        773    89.4

4. Exploratory Data Analysis

analysis_data %>%
  group_by(age_group) %>%
  summarise(
    n = n(),
    mean_age = mean(age, na.rm = TRUE),
    sd_age = sd(age, na.rm = TRUE),
    mean_weight = mean(weight, na.rm = TRUE),
    sd_weight = sd(weight, na.rm = TRUE),
    mean_height = mean(height, na.rm = TRUE),
    sd_height = sd(height, na.rm = TRUE)
  )
## # A tibble: 2 × 8
##   age_group      n mean_age sd_age mean_weight sd_weight mean_height sd_height
##   <fct>      <int>    <dbl>  <dbl>       <dbl>     <dbl>       <dbl>     <dbl>
## 1 Adolescent    92     15.2   2.11        64.1      15.3        165.      9.27
## 2 Adult        773     49.7  17.6         66.1      15.3        165.     10.3
analysis_data %>%
  count(age_group, gender) %>%
  group_by(age_group) %>%
  mutate(percent = round(100 * n / sum(n), 1))
## # A tibble: 6 × 4
## # Groups:   age_group [2]
##   age_group  gender     n percent
##   <fct>      <chr>  <int>   <dbl>
## 1 Adolescent Female    30    32.6
## 2 Adolescent Male      60    65.2
## 3 Adolescent Other      2     2.2
## 4 Adult      Female   332    42.9
## 5 Adult      Male     424    54.9
## 6 Adult      Other     17     2.2
ggplot(analysis_data, aes(x = age, fill = age_group)) +
  geom_histogram(bins = 30, alpha = 0.7, position = "identity") +
  labs(
    title = "Age Distribution by Group",
    x = "Age",
    y = "Count"
  )

ggplot(analysis_data, aes(x = gender, fill = age_group)) +
  geom_bar(position = "dodge") +
  labs(
    title = "Gender Distribution by Age Group",
    x = "Gender",
    y = "Count"
  )

symptom_vars <- c(
  "aura_before_seizure",
  "loss_of_consciousness",
  "muscle_stiffness",
  "jerky_movements",
  "postictal_confusion",
  "blank_stare_episodes",
  "eye_rolling"
)

risk_vars <- c(
  "alcohol_or_drug_use",
  "stress_or_anxiety_before_episode",
  "lack_of_sleep_before_episode",
  "flashing_lights_sensitivity",
  "loud_sound_sensitivity",
  "missed_medication",
  "family_history_of_epilepsy",
  "head_injury_history",
  "brain_tumor",
  "history_of_stroke",
  "genetic_disorder"
)

clinical_vars <- c(
  "medication_status",
  "eeg_abnormality_detected",
  "mri_ct_scan_result",
  "seizure_frequency",
  "seizure_duration",
  "seizure_type",
  "target_epilepsy_type"
)
analysis_data %>%
  select(age_group, all_of(symptom_vars)) %>%
  pivot_longer(
    cols = all_of(symptom_vars),
    names_to = "symptom",
    values_to = "response"
  ) %>%
  count(age_group, symptom, response) %>%
  group_by(age_group, symptom) %>%
  mutate(percent = 100 * n / sum(n)) %>%
  filter(response == "Yes") %>%
  ggplot(aes(x = symptom, y = percent, fill = age_group)) +
  geom_col(position = "dodge") +
  coord_flip() +
  labs(
    title = "Symptom Profiles by Age Group",
    x = "Symptom",
    y = "Percent Reporting Symptom"
  )

analysis_data %>%
  select(age_group, all_of(risk_vars)) %>%
  pivot_longer(
    cols = all_of(risk_vars),
    names_to = "risk_factor",
    values_to = "response"
  ) %>%
  count(age_group, risk_factor, response) %>%
  group_by(age_group, risk_factor) %>%
  mutate(percent = 100 * n / sum(n)) %>%
  filter(response == "Yes") %>%
  ggplot(aes(x = risk_factor, y = percent, fill = age_group)) +
  geom_col(position = "dodge") +
  coord_flip() +
  labs(
    title = "Risk Factors by Age Group",
    x = "Risk Factor",
    y = "Percent Reporting Risk Factor"
  )

5. Statistical Analysis

vars_to_test <- c(clinical_vars, symptom_vars, risk_vars)

chi_square_results <- map_df(vars_to_test, function(var) {
  
  test_data <- analysis_data %>%
    select(age_group, all_of(var)) %>%
    drop_na()
  
  tbl <- table(test_data$age_group, test_data[[var]])
  
  if (nrow(tbl) < 2 || ncol(tbl) < 2) {
    return(tibble(
      variable = var,
      chi_square = NA,
      df = NA,
      p_value = NA
    ))
  }
  
  test <- suppressWarnings(chisq.test(tbl))
  
  tibble(
    variable = var,
    chi_square = round(as.numeric(test$statistic), 3),
    df = as.numeric(test$parameter),
    p_value = round(test$p.value, 4)
  )
})

chi_square_results %>%
  arrange(p_value)
## # A tibble: 25 × 4
##    variable                   chi_square    df p_value
##    <chr>                           <dbl> <dbl>   <dbl>
##  1 seizure_type                   41.1       3  0     
##  2 target_epilepsy_type           24.0       5  0.0002
##  3 aura_before_seizure             2.03      1  0.154 
##  4 seizure_duration                4.45      3  0.217 
##  5 jerky_movements                 1.48      1  0.224 
##  6 loss_of_consciousness           1.39      1  0.239 
##  7 postictal_confusion             1.30      1  0.255 
##  8 history_of_stroke               0.863     1  0.353 
##  9 mri_ct_scan_result              1.54      2  0.463 
## 10 family_history_of_epilepsy      0.438     1  0.508 
## # ℹ 15 more rows
model_data <- analysis_data %>%
  mutate(
    adolescent = ifelse(age_group == "Adolescent", 1, 0)
  ) %>%
  select(
    adolescent,
    gender,
    weight,
    height,
    medication_status,
    alcohol_or_drug_use,
    eeg_abnormality_detected,
    mri_ct_scan_result,
    seizure_frequency,
    seizure_duration,
    seizure_type,
    aura_before_seizure,
    loss_of_consciousness,
    muscle_stiffness,
    jerky_movements,
    postictal_confusion,
    blank_stare_episodes,
    eye_rolling,
    stress_or_anxiety_before_episode,
    lack_of_sleep_before_episode,
    missed_medication,
    family_history_of_epilepsy,
    head_injury_history,
    genetic_disorder,
    target_epilepsy_type
  ) %>%
  mutate(across(where(is.character), as.factor)) %>%
  drop_na()
logit_model <- glm(
  adolescent ~ .,
  data = model_data,
  family = binomial
)

logit_results <- tidy(
  logit_model,
  exponentiate = TRUE,
  conf.int = TRUE
) %>%
  arrange(p.value)

logit_results
## # A tibble: 33 × 7
##    term                  estimate std.error statistic p.value conf.low conf.high
##    <chr>                    <dbl>     <dbl>     <dbl>   <dbl>    <dbl>     <dbl>
##  1 seizure_typeFocal        0.121   0.524       -4.03 5.64e-5   0.0425     0.334
##  2 seizure_typeGenerali…    0.230   0.500       -2.94 3.30e-3   0.0854     0.610
##  3 seizure_typeTonic-Cl…    0.359   0.511       -2.00 4.52e-2   0.130      0.973
##  4 genderMale               1.61    0.250        1.91 5.67e-2   0.994      2.65 
##  5 seizure_duration         0.992   0.00432     -1.77 7.64e-2   0.984      1.00 
##  6 jerky_movementsYes       0.611   0.298       -1.66 9.77e-2   0.340      1.10 
##  7 postictal_confusionY…    0.655   0.261       -1.62 1.05e-1   0.394      1.10 
##  8 weight                   0.989   0.00781     -1.40 1.63e-1   0.974      1.00 
##  9 loss_of_consciousnes…    1.41    0.294        1.16 2.44e-1   0.797      2.53 
## 10 mri_ct_scan_resultNo…    0.745   0.257       -1.14 2.52e-1   0.449      1.24 
## # ℹ 23 more rows
rf_data <- model_data %>%
  mutate(
    adolescent = factor(
      adolescent,
      levels = c(0, 1),
      labels = c("Adult", "Adolescent")
    )
  )

set.seed(123)

rf_model <- randomForest(
  adolescent ~ .,
  data = rf_data,
  importance = TRUE,
  ntree = 500
)

importance_df <- importance(rf_model) %>%
  as.data.frame() %>%
  rownames_to_column("variable") %>%
  arrange(desc(MeanDecreaseGini))

importance_df
##                            variable       Adult  Adolescent
## 1                            weight  1.21225533  0.41573211
## 2                            height -0.29711248  1.09277451
## 3                      seizure_type  3.39799091  8.69402249
## 4                 seizure_frequency -0.76198080 -2.32756338
## 5              target_epilepsy_type  1.24128761  1.44187408
## 6                  seizure_duration -1.20245006 -0.80013987
## 7                mri_ct_scan_result -0.68716582 -0.34488143
## 8                       eye_rolling  1.18580219  4.09113751
## 9                            gender -1.41888108 -0.25370992
## 10                 muscle_stiffness  2.50258063 -1.03090560
## 11                  jerky_movements  1.82391510  3.01553318
## 12     lack_of_sleep_before_episode -0.38653867 -0.77670047
## 13              aura_before_seizure  0.08913831 -0.62671879
## 14              postictal_confusion  1.92697174 -0.05460891
## 15       family_history_of_epilepsy -1.99128271  1.50908312
## 16 stress_or_anxiety_before_episode -2.98509348  1.45899237
## 17                medication_status -1.69400793 -0.13358916
## 18              head_injury_history  0.06250515  0.82008398
## 19             blank_stare_episodes -3.25933420  0.22925981
## 20            loss_of_consciousness  0.51963175  0.20733471
## 21                missed_medication  0.78761861 -1.31314838
## 22         eeg_abnormality_detected -0.90039778 -1.10666739
## 23                 genetic_disorder -2.89393562  2.55601013
## 24              alcohol_or_drug_use -1.37169823 -2.12010005
##    MeanDecreaseAccuracy MeanDecreaseGini
## 1            1.22935484        24.996569
## 2            0.04969975        23.477850
## 3            6.30307802        11.374333
## 4           -1.50160457        10.863905
## 5            1.69840313         9.637369
## 6           -1.41328297         8.079422
## 7           -0.76666042         7.247477
## 8            2.55016127         4.766428
## 9           -1.50951374         4.584075
## 10           1.88172618         4.310033
## 11           2.67638799         4.174650
## 12          -0.59280314         4.133665
## 13          -0.08336847         4.095214
## 14           1.77196560         4.013726
## 15          -1.29984603         3.994467
## 16          -2.19681833         3.921945
## 17          -1.63109118         3.540770
## 18           0.36480064         3.534973
## 19          -3.03923438         3.493657
## 20           0.59903114         3.453896
## 21           0.34830511         3.416778
## 22          -1.24630948         2.762019
## 23          -1.88584648         2.569827
## 24          -2.11521592         2.108855
varImpPlot(rf_model)

6. Topological Characterization of Clinical Risk Profiles

tda_data <- analysis_data %>%
  select(
    age_group,
    gender,
    medication_status,
    alcohol_or_drug_use,
    eeg_abnormality_detected,
    mri_ct_scan_result,
    seizure_frequency,
    seizure_duration,
    seizure_type,
    aura_before_seizure,
    loss_of_consciousness,
    muscle_stiffness,
    jerky_movements,
    postictal_confusion,
    blank_stare_episodes,
    eye_rolling,
    stress_or_anxiety_before_episode,
    lack_of_sleep_before_episode,
    missed_medication,
    family_history_of_epilepsy,
    head_injury_history,
    genetic_disorder,
    target_epilepsy_type
  ) %>%
  drop_na()

age_labels <- tda_data$age_group

risk_profiles <- tda_data %>%
  select(-age_group) %>%
  mutate(across(where(is.character), as.factor))

str(risk_profiles)
## tibble [865 × 22] (S3: tbl_df/tbl/data.frame)
##  $ gender                          : Factor w/ 3 levels "Female","Male",..: 2 1 1 2 2 2 1 2 1 2 ...
##  $ medication_status               : Factor w/ 2 levels "Not on Medication",..: 2 2 1 2 2 1 2 1 2 2 ...
##  $ alcohol_or_drug_use             : Factor w/ 2 levels "No","Yes": 2 1 1 1 1 1 1 1 1 1 ...
##  $ eeg_abnormality_detected        : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 2 1 2 2 ...
##  $ mri_ct_scan_result              : Factor w/ 3 levels "Abnormal","Normal",..: 2 1 3 3 1 3 2 2 1 2 ...
##  $ seizure_frequency               : num [1:865] 1 3 0 1 1 2 1 1 5 3 ...
##  $ seizure_duration                : num [1:865] 60 30 60 60 30 30 30 90 30 30 ...
##  $ seizure_type                    : Factor w/ 4 levels "Absence","Focal",..: 4 4 2 4 4 1 1 4 4 3 ...
##  $ aura_before_seizure             : Factor w/ 2 levels "No","Yes": 1 2 2 1 2 1 2 2 1 2 ...
##  $ loss_of_consciousness           : Factor w/ 2 levels "No","Yes": 2 1 2 2 2 1 2 2 1 2 ...
##  $ muscle_stiffness                : Factor w/ 2 levels "No","Yes": 1 2 1 2 1 2 1 1 1 1 ...
##  $ jerky_movements                 : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 2 2 ...
##  $ postictal_confusion             : Factor w/ 2 levels "No","Yes": 2 1 2 2 2 2 1 1 2 1 ...
##  $ blank_stare_episodes            : Factor w/ 2 levels "No","Yes": 1 2 2 1 2 2 2 1 1 1 ...
##  $ eye_rolling                     : Factor w/ 2 levels "No","Yes": 2 2 1 1 1 2 2 2 2 2 ...
##  $ stress_or_anxiety_before_episode: Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 2 2 2 ...
##  $ lack_of_sleep_before_episode    : Factor w/ 2 levels "No","Yes": 2 1 2 1 2 1 2 2 2 2 ...
##  $ missed_medication               : Factor w/ 2 levels "No","Yes": 1 2 1 1 1 1 1 1 1 1 ...
##  $ family_history_of_epilepsy      : Factor w/ 2 levels "No","Yes": 1 2 1 2 2 2 1 2 2 1 ...
##  $ head_injury_history             : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 2 1 1 1 2 ...
##  $ genetic_disorder                : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 2 1 1 1 1 ...
##  $ target_epilepsy_type            : Factor w/ 6 levels "Absence","Complicated",..: 4 4 3 4 4 1 1 2 2 4 ...
gower_dist <- daisy(risk_profiles, metric = "gower")
gower_matrix <- as.matrix(gower_dist)
mds <- cmdscale(gower_dist, k = 2)

mds_df <- data.frame(
  Dim1 = mds[, 1],
  Dim2 = mds[, 2],
  age_group = age_labels
)

ggplot(mds_df, aes(x = Dim1, y = Dim2, color = age_group)) +
  geom_point(alpha = 0.7) +
  labs(
    title = "Risk Profile Space Using Gower Distance",
    x = "Dimension 1",
    y = "Dimension 2"
  )

filter_values <- mds[, 1]

mapper_result <- mapper1D(
  distance_matrix = gower_matrix,
  filter_values = filter_values,
  num_intervals = 10,
  percent_overlap = 50,
  num_bins_when_clustering = 10
)

mapper_vertices <- mapper_result$points_in_vertex
mapper_edges <- mapper_result$adjacency

g <- graph_from_adjacency_matrix(
  mapper_edges,
  mode = "undirected"
)

node_adolescent_prop <- sapply(mapper_vertices, function(indices) {
  mean(age_labels[indices] == "Adolescent")
})

V(g)$adolescent_prop <- node_adolescent_prop
plot(
  g,
  vertex.size = 8 + 25 * V(g)$adolescent_prop,
  vertex.label = NA,
  main = "Mapper Graph of Epilepsy Risk Profiles"
)

mapper_summary <- tibble(
  node = seq_along(mapper_vertices),
  node_size = sapply(mapper_vertices, length),
  adolescent_proportion = node_adolescent_prop
) %>%
  arrange(desc(adolescent_proportion))

mapper_summary
## # A tibble: 10 × 3
##     node node_size adolescent_proportion
##    <int>     <int>                 <dbl>
##  1     4       176                0.148 
##  2     9       250                0.132 
##  3    10       166                0.127 
##  4     8       201                0.119 
##  5     5       203                0.0985
##  6     3       164                0.0976
##  7     7       160                0.0938
##  8     6       192                0.0677
##  9     1        45                0.0667
## 10     2       109                0.0642

Interpretation Notes

The exploratory analysis describes differences between adolescents and adults in the dataset.

The statistical analysis identifies individual clinical, demographic, and behavioral variables that distinguish adolescent epilepsy patients from adult epilepsy patients.

The TDA analysis treats each patient as a multivariate clinical risk profile. Rather than studying variables one at a time, Mapper studies the shape of the risk-profile space and identifies clusters of patients with similar combinations of symptoms and risk factors.

library(tidyr)

seizure_summary <- analysis_data %>%
  count(age_group, seizure_type) %>%
  group_by(age_group) %>%
  mutate(
    Percent = round(100 * n / sum(n), 1),
    Summary = paste0(n, " (", Percent, "%)")
  ) %>%
  select(age_group, seizure_type, Summary) %>%
  pivot_wider(
    names_from = age_group,
    values_from = Summary
  )

seizure_summary
## # A tibble: 4 × 3
##   seizure_type Adolescent Adult      
##   <chr>        <chr>      <chr>      
## 1 Absence      28 (30.4%) 82 (10.6%) 
## 2 Focal        12 (13%)   280 (36.2%)
## 3 Generalized  29 (31.5%) 273 (35.3%)
## 4 Tonic-Clonic 23 (25%)   138 (17.9%)
target_summary <- analysis_data %>%
  count(age_group, target_epilepsy_type) %>%
  group_by(age_group) %>%
  mutate(
    Percent = round(100 * n / sum(n), 1),
    Summary = paste0(n, " (", Percent, "%)")
  ) %>%
  select(
    target_epilepsy_type,
    age_group,
    Summary
  ) %>%
  pivot_wider(
    names_from = age_group,
    values_from = Summary
  )

knitr::kable(
  target_summary,
  col.names = c(
    "Target Epilepsy Type",
    "Adolescent",
    "Adult"
  ),
  caption = "Distribution of Target Epilepsy Type by Age Group",
  align = c("l", "c", "c")
)
Distribution of Target Epilepsy Type by Age Group
Target Epilepsy Type Adolescent Adult
Absence 19 (20.7%) 69 (8.9%)
Complicated 8 (8.7%) 72 (9.3%)
Focal 12 (13%) 252 (32.6%)
Generalized 46 (50%) 327 (42.3%)
Normal 6 (6.5%) 34 (4.4%)
Not Confirmed 1 (1.1%) 19 (2.5%)
library(tidyverse)

# Variables to summarize within each Mapper node
tda_symptom_vars <- c(
  "aura_before_seizure",
  "loss_of_consciousness",
  "muscle_stiffness",
  "jerky_movements",
  "postictal_confusion",
  "blank_stare_episodes",
  "eye_rolling"
)

tda_risk_vars <- c(
  "alcohol_or_drug_use",
  "stress_or_anxiety_before_episode",
  "lack_of_sleep_before_episode",
  "missed_medication",
  "family_history_of_epilepsy",
  "head_injury_history",
  "genetic_disorder"
)

# Function to identify the most common category
get_mode <- function(x) {
  x <- x[!is.na(x)]
  
  if (length(x) == 0) {
    return(NA_character_)
  }
  
  counts <- sort(table(x), decreasing = TRUE)
  names(counts)[1]
}

# Function to calculate the percentage in the most common category
get_mode_percent <- function(x) {
  x <- x[!is.na(x)]
  
  if (length(x) == 0) {
    return(NA_real_)
  }
  
  counts <- sort(table(x), decreasing = TRUE)
  round(100 * counts[1] / sum(counts), 1)
}

# Function to identify the most prevalent Yes/No variables
get_top_yes_variables <- function(data, variables, number_to_return = 3) {
  
  available_variables <- variables[variables %in% names(data)]
  
  if (length(available_variables) == 0) {
    return(NA_character_)
  }
  
  prevalence <- map_dfr(available_variables, function(var) {
    
    responses <- trimws(tolower(as.character(data[[var]])))
    
    tibble(
      variable = var,
      percent_yes = round(
        100 * mean(responses == "yes", na.rm = TRUE),
        1
      )
    )
  }) %>%
    arrange(desc(percent_yes)) %>%
    slice_head(n = number_to_return) %>%
    mutate(
      variable_label = str_replace_all(variable, "_", " "),
      summary = paste0(variable_label, " (", percent_yes, "%)")
    )
  
  paste(prevalence$summary, collapse = "; ")
}

# Create a clinical summary for each Mapper node
mapper_node_profiles <- map_dfr(
  seq_along(mapper_vertices),
  function(node_number) {
    
    # Row numbers of patients assigned to this node
    node_indices <- mapper_vertices[[node_number]]
    
    # Clinical records for patients in this node
    node_data <- tda_data[node_indices, , drop = FALSE]
    
    adolescent_n <- sum(
      node_data$age_group == "Adolescent",
      na.rm = TRUE
    )
    
    adult_n <- sum(
      node_data$age_group == "Adult",
      na.rm = TRUE
    )
    
    dominant_seizure <- get_mode(node_data$seizure_type)
    dominant_seizure_percent <- get_mode_percent(node_data$seizure_type)
    
    dominant_epilepsy <- get_mode(node_data$target_epilepsy_type)
    dominant_epilepsy_percent <- get_mode_percent(
      node_data$target_epilepsy_type
    )
    
    tibble(
      node = node_number,
      node_size = nrow(node_data),
      adolescent_n = adolescent_n,
      adult_n = adult_n,
      adolescent_percent = round(
        100 * adolescent_n / nrow(node_data),
        1
      ),
      most_common_seizure_type = paste0(
        dominant_seizure,
        " (",
        dominant_seizure_percent,
        "%)"
      ),
      most_common_epilepsy_type = paste0(
        dominant_epilepsy,
        " (",
        dominant_epilepsy_percent,
        "%)"
      ),
      top_symptoms = get_top_yes_variables(
        node_data,
        tda_symptom_vars,
        number_to_return = 3
      ),
      top_risk_factors = get_top_yes_variables(
        node_data,
        tda_risk_vars,
        number_to_return = 3
      )
    )
  }
) %>%
  arrange(desc(adolescent_percent))

mapper_node_profiles
## # A tibble: 10 × 9
##     node node_size adolescent_n adult_n adolescent_percent
##    <int>     <int>        <int>   <int>              <dbl>
##  1     4       176           26     150               14.8
##  2     9       250           33     217               13.2
##  3    10       166           21     145               12.7
##  4     8       201           24     177               11.9
##  5     5       203           20     183                9.9
##  6     3       164           16     148                9.8
##  7     7       160           15     145                9.4
##  8     6       192           13     179                6.8
##  9     1        45            3      42                6.7
## 10     2       109            7     102                6.4
## # ℹ 4 more variables: most_common_seizure_type <chr>,
## #   most_common_epilepsy_type <chr>, top_symptoms <chr>, top_risk_factors <chr>
print(
  mapper_node_profiles,
  n = Inf,
  width = Inf
)
## # A tibble: 10 × 9
##     node node_size adolescent_n adult_n adolescent_percent
##    <int>     <int>        <int>   <int>              <dbl>
##  1     4       176           26     150               14.8
##  2     9       250           33     217               13.2
##  3    10       166           21     145               12.7
##  4     8       201           24     177               11.9
##  5     5       203           20     183                9.9
##  6     3       164           16     148                9.8
##  7     7       160           15     145                9.4
##  8     6       192           13     179                6.8
##  9     1        45            3      42                6.7
## 10     2       109            7     102                6.4
##    most_common_seizure_type most_common_epilepsy_type
##    <chr>                    <chr>                    
##  1 Focal (60.2%)            Focal (56.2%)            
##  2 Generalized (64.4%)      Generalized (99.2%)      
##  3 Generalized (69.9%)      Generalized (100%)       
##  4 Generalized (61.7%)      Generalized (85.1%)      
##  5 Focal (54.7%)            Focal (49.3%)            
##  6 Focal (75.6%)            Focal (67.1%)            
##  7 Generalized (47.5%)      Generalized (38.1%)      
##  8 Focal (34.4%)            Focal (29.2%)            
##  9 Focal (86.7%)            Focal (86.7%)            
## 10 Focal (87.2%)            Focal (81.7%)            
##    top_symptoms                                                                 
##    <chr>                                                                        
##  1 aura before seizure (69.9%); muscle stiffness (61.9%); eye rolling (58.5%)   
##  2 loss of consciousness (98%); jerky movements (97.6%); postictal confusion (9…
##  3 loss of consciousness (100%); jerky movements (98.8%); postictal confusion (…
##  4 loss of consciousness (88.6%); jerky movements (88.1%); postictal confusion …
##  5 postictal confusion (66%); eye rolling (60.1%); muscle stiffness (59.6%)     
##  6 aura before seizure (78.7%); muscle stiffness (61%); eye rolling (54.9%)     
##  7 postictal confusion (71.9%); jerky movements (70%); eye rolling (66.2%)      
##  8 postictal confusion (81.8%); eye rolling (67.2%); jerky movements (65.1%)    
##  9 aura before seizure (91.1%); muscle stiffness (62.2%); blank stare episodes …
## 10 aura before seizure (80.7%); muscle stiffness (62.4%); eye rolling (49.5%)   
##    top_risk_factors                                                             
##    <chr>                                                                        
##  1 lack of sleep before episode (69.9%); stress or anxiety before episode (68.2…
##  2 stress or anxiety before episode (67.2%); lack of sleep before episode (66.8…
##  3 lack of sleep before episode (63.3%); stress or anxiety before episode (60.8…
##  4 lack of sleep before episode (72.6%); stress or anxiety before episode (68.2…
##  5 stress or anxiety before episode (72.9%); lack of sleep before episode (71.4…
##  6 lack of sleep before episode (73.8%); stress or anxiety before episode (62.2…
##  7 lack of sleep before episode (70.6%); stress or anxiety before episode (66.9…
##  8 lack of sleep before episode (76%); stress or anxiety before episode (70.8%)…
##  9 lack of sleep before episode (75.6%); stress or anxiety before episode (53.3…
## 10 lack of sleep before episode (72.5%); stress or anxiety before episode (57.8…