library(dplyr)
library(ggplot2)
library(gt)
library(ggiraph)
library(tidyr)
library(tidyverse)
library(corrplot)
library(bestNormalize)
library(patchwork)
library(gridExtra)
library(zoo)
library(stringr)
library(grid)
library(pracma)
library(infotheo)

Missing Variables

From the table below, it is clear that every variable has missing rows. This is going to require a lot of work to best fill in these missing values, to ensure that optimal results are achieved.

train <- read.csv("train.csv")
test <- read.csv("test.csv")
missing_train <- train |>
  dplyr::mutate(across(where(is.character), ~ dplyr::na_if(.x, ""))) |>
  dplyr::summarise(across(everything(), ~ sum(is.na(.x)))) |>
  tidyr::pivot_longer(cols = everything(), names_to = "Variables", values_to = "Total_train")

missing_test <- test |>
  dplyr::mutate(across(where(is.character), ~ dplyr::na_if(.x, ""))) |>
  dplyr::summarise(across(everything(), ~ sum(is.na(.x)))) |>
  tidyr::pivot_longer(cols = everything(), names_to = "Variables", values_to = "Total_test")

missing <- dplyr::left_join(missing_train, missing_test, by = "Variables") |> 
  dplyr::mutate(
    Pct_train = round((Total_train / nrow(train)) * 100, 2),
    Pct_test  = round((Total_test / nrow(test)) * 100, 2)
  )

missing |> gt()
Variables Total_train Total_test Pct_train Pct_test
id 0 0 0.00 0.00
age 28929 17138 4.18 5.78
daily_screen_time_hours 95854 32788 13.86 11.07
social_media_hours 133995 47397 19.38 16.00
gaming_hours 126821 59420 18.34 20.05
work_study_hours 51518 27777 7.45 9.37
sleep_hours 44480 22455 6.43 7.58
notifications_per_day 67584 34221 9.78 11.55
app_opens_per_day 80710 25705 11.67 8.68
weekend_screen_time 112063 50697 16.21 17.11
gender 29034 14212 4.20 4.80
stress_level 55148 19626 7.98 6.62
academic_work_impact 44224 25721 6.40 8.68
addicted_label 0 NA 0.00 NA

Exploratory Data

sum_raw <- summary(train)

stat_names <- sapply(strsplit(sum_raw[, 1], ":"), function(x) trimws(x[1]))

sum_clean <- apply(sum_raw, 2, function(col) {
  sapply(strsplit(col, ":"), function(x) trimws(tail(x, 1)))
})

sum_df <- as.data.frame(sum_clean, stringsAsFactors = FALSE)
sum_df <- cbind(Statistic = stat_names, sum_df)

sum_df |>
  gt(rowname_col = "Statistic") |>
  tab_header(
    title = "Data Summary of Training Set",
    subtitle = "Summary statistics for all columns"
  ) |>
  tab_options(
    table.font.size = px(14),
    heading.align = "left",
    stub.font.weight = "bold"
  )
Data Summary of Training Set
Summary statistics for all columns
id age daily_screen_time_hours social_media_hours gaming_hours work_study_hours sleep_hours notifications_per_day app_opens_per_day weekend_screen_time gender stress_level academic_work_impact addicted_label
Min. 0 18.00 0.50 0.00 0.00 0.00 4.50 20.0 15.0 0.51 691369 691369 691369 0.0000
1st Qu. 172842 22.00 5.48 1.45 0.70 1.36 5.78 93.0 64.0 7.28 character character character 0.0000
Median 345684 27.00 7.77 2.31 1.33 2.20 6.80 150.0 104.0 9.58 character character character 1.0000
Mean 345684 26.61 7.64 2.47 1.46 2.37 6.80 145.9 102.6 9.48 NA NA NA 0.7094
3rd Qu. 518526 31.00 9.84 3.37 2.09 3.20 7.87 204.0 145.0 11.75 NA NA NA 1.0000
Max. 691368 35.00 15.00 8.00 4.00 6.00 9.00 250.0 180.0 17.56 NA NA NA 1.0000
NA NA 28929 95854 133995 126821 51518 44480 67584 80710 112063 NA NA NA NA

Class Imbalance Check

target_imbalance <- as.data.frame(table(train$addicted_label)) 
colnames(target_imbalance) <- c("Target", "Frequency")
target_imbalance |> 
  dplyr::mutate(Per_Freq = round(Frequency / sum(Frequency) *100, 2)) |>
  gt()
Target Frequency Per_Freq
0 200895 29.06
1 490474 70.94

Univariate Exploration

Correlation

train_numeric <- train |>
  mutate(
    stress_level = as.numeric(factor(stress_level, levels = c("low", "medium", "high"))),
    academic_work_impact = as.numeric(factor(academic_work_impact, levels = c("No", "Yes"))),
    gender = NULL
  )

pit_corr <- stats::cor(train_numeric, use = "pairwise.complete.obs", method = "spearman")

corrplot(pit_corr, method = 'color', addCoef.col = 'black', col = COL2('RdYlBu'))

Mutual Information

train_cat <- train |>
  mutate(across(where(is.numeric), ~ discretize(.x, disc = "equalfreq", nbins = 5)[[1]]))

train_cat <- as.data.frame(lapply(train_cat, function(x) as.integer(as.factor(x))))

vars <- names(train_cat)

mi_mat <- matrix(0, nrow = length(vars), ncol = length(vars),
                  dimnames = list(vars, vars))
for (i in seq_along(vars)) {
  for (j in seq_along(vars)) {
    mi_mat[i, j] <- mutinformation(train_cat[[i]], train_cat[[j]])
  }
}

nmi_mat <- mi_mat
for (i in seq_along(vars)) {
  for (j in seq_along(vars)) {
    nmi_mat[i, j] <- mi_mat[i, j] / sqrt(mi_mat[i, i] * mi_mat[j, j])
  }
}
nmi_long <- as.data.frame(nmi_mat) |>
  mutate(var1 = rownames(nmi_mat)) |>
  pivot_longer(-var1, names_to = "var2", values_to = "nmi")

ggplot(nmi_long, aes(x = var1, y = var2, fill = nmi)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(nmi, 2)), size = 3) +
  scale_fill_distiller(palette = "RdYlBu", limits = c(0, 1)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(x = NULL, y = NULL, fill = "NMI")

target <- "addicted_label"

mi_with_target <- data.frame(
  variable = setdiff(vars, target),
  mi = sapply(setdiff(vars, target), function(v) {
    mutinformation(train_cat[[v]], train_cat[[target]])
  })
)

ggplot(mi_with_target, aes(x = reorder(variable, mi), y = mi)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(x = NULL, y = "Mutual Information with screen addiction", title = "Predictor MI with addicted_label") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

Parameter Distributions

ggplot(train, aes(x = addicted_label, y = age, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Age by Addiction", y = "Age", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = daily_screen_time_hours, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Screen Time by Addiction", y = "Screen Time (Hours)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = social_media_hours, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Social Media Hours", y = "Social Media (Hours)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = gaming_hours, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Gaming Hours", y = "Gaming Hours", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = work_study_hours, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Work vs. Study Hours", y = "Work / Study (Hours)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = sleep_hours, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Sleep", y = "Sleep (Hours)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = notifications_per_day, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Notifications per Day", y = "Notifications (Total)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = app_opens_per_day, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Number of Apps Opened per Day", y = "App Opens per Day (Total)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = addicted_label, y = weekend_screen_time, fill = factor(addicted_label))) +
  geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", 
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(title = "Weekend Screen Time", y = "Weekend Screen Time (Hours)", x = "") +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = factor(gender, levels = c("Male", "Female")),
                   fill = factor(addicted_label))) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(
    title = "Addition by Gender",
    y = "Count",
    x = "Gender"
  ) +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = factor(stress_level, levels = c("Low", "Medium", "High")),
                   fill = factor(addicted_label))) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(
    title = "Stress Level",
    y = "Count",
    x = "Stress Level"
  ) +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )

ggplot(train, aes(x = factor(academic_work_impact, levels = c("Yes", "No")),
                   fill = factor(addicted_label))) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9", "#009E73", "#F0E442",
                                "#0072B2", "#D55E00", "#CC79A7")) +
  labs(
    title = "Academic Work Impact",
    y = "Count",
    x = "Academic Work Impact"
  ) +
  theme_minimal(base_size = 12, base_family = "sans") +
  theme(
    plot.title = element_text(face = "bold", size = 14, margin = margin(b = 4)),
    plot.subtitle = element_text(color = "grey40", size = 11, margin = margin(b = 12)),
    plot.caption = element_text(color = "grey60", size = 9, hjust = 0),
    plot.margin = margin(16, 16, 16, 16),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.title = element_text(color = "grey30", size = 10),
    axis.text = element_text(color = "grey30"),
    axis.ticks.x = element_line(color = "grey80"),
    legend.position = "bottom",
    legend.title = element_blank()
  )