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.