DC SL esame.qmd

library(dplyr)
Warning: il pacchetto 'dplyr' è stato creato con R versione 4.5.2

Caricamento pacchetto: 'dplyr'
I seguenti oggetti sono mascherati da 'package:stats':

    filter, lag
I seguenti oggetti sono mascherati da 'package:base':

    intersect, setdiff, setequal, union
library(caret)
Warning: il pacchetto 'caret' è stato creato con R versione 4.5.2
Caricamento del pacchetto richiesto: ggplot2
Caricamento del pacchetto richiesto: lattice
library(ggplot2)
library(gridExtra)
Warning: il pacchetto 'gridExtra' è stato creato con R versione 4.5.2

Caricamento pacchetto: 'gridExtra'
Il seguente oggetto è mascherato da 'package:dplyr':

    combine
library(reshape2)
library(scales)
library(clustrd)
Warning: il pacchetto 'clustrd' è stato creato con R versione 4.5.2
Caricamento del pacchetto richiesto: grid
library(mclust)
Warning: il pacchetto 'mclust' è stato creato con R versione 4.5.2
Package 'mclust' version 6.1.2
Type 'citation("mclust")' for citing this R package in publications.

Caricamento pacchetto: 'mclust'
Il seguente oggetto è mascherato da 'package:dplyr':

    count
library(tidyr)

Caricamento pacchetto: 'tidyr'
Il seguente oggetto è mascherato da 'package:reshape2':

    smiths
library(pROC)
Type 'citation("pROC")' for a citation.

Caricamento pacchetto: 'pROC'
I seguenti oggetti sono mascherati da 'package:stats':

    cov, smooth, var
library(randomForest)
Warning: il pacchetto 'randomForest' è stato creato con R versione 4.5.2
randomForest 4.7-1.2
Type rfNews() to see new features/changes/bug fixes.

Caricamento pacchetto: 'randomForest'
Il seguente oggetto è mascherato da 'package:gridExtra':

    combine
Il seguente oggetto è mascherato da 'package:ggplot2':

    margin
Il seguente oggetto è mascherato da 'package:dplyr':

    combine
library(e1071)

Caricamento pacchetto: 'e1071'
Il seguente oggetto è mascherato da 'package:ggplot2':

    element
library(MASS)

Caricamento pacchetto: 'MASS'
Il seguente oggetto è mascherato da 'package:dplyr':

    select
library(class)
library(factoextra)
Warning: il pacchetto 'factoextra' è stato creato con R versione 4.5.2
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(klaR)    
Warning: il pacchetto 'klaR' è stato creato con R versione 4.5.2
library(kernlab) 
Warning: il pacchetto 'kernlab' è stato creato con R versione 4.5.2

Caricamento pacchetto: 'kernlab'
Il seguente oggetto è mascherato da 'package:scales':

    alpha
Il seguente oggetto è mascherato da 'package:ggplot2':

    alpha
library(scatterplot3d)
Warning: il pacchetto 'scatterplot3d' è stato creato con R versione 4.5.2
library(plotly)
Warning: il pacchetto 'plotly' è stato creato con R versione 4.5.2

Caricamento pacchetto: 'plotly'
Il seguente oggetto è mascherato da 'package:MASS':

    select
Il seguente oggetto è mascherato da 'package:ggplot2':

    last_plot
Il seguente oggetto è mascherato da 'package:stats':

    filter
Il seguente oggetto è mascherato da 'package:graphics':

    layout
library(htmlwidgets)
Warning: il pacchetto 'htmlwidgets' è stato creato con R versione 4.5.2
library(ca)
Warning: il pacchetto 'ca' è stato creato con R versione 4.5.2
# Caricamento del dataset
BS <- read.csv("BS.csv") 
# Rimozione prima colonna 
df <- BS[-1]

# Feature Engineering
# Target
df$Diagnosis <- as.factor(df$Diagnosis)
df$Diagnosis_Num <- ifelse(df$Diagnosis == "Malignant", 1, 0)

# Variabili Categoriche
cat_vars <- c("Gender", "Country", "Ethnicity", "Family_History",
              "Radiation_Exposure", "Iodine_Deficiency", "Smoking",
              "Obesity", "Diabetes")
df[cat_vars] <- lapply(df[cat_vars], as.factor)

# Variabili Numeriche
num_vars <- c("Age", "TSH_Level", "T3_Level", "T4_Level", "Nodule_Size")
df[num_vars] <- lapply(df[num_vars], as.numeric)

# Split Train/Test (70/30 Stratificato)
set.seed(2025)
trainIndex <- createDataPartition(df$Diagnosis, p = 0.7, list = FALSE)

df_train <- df[trainIndex, ]
df_test  <- df[-trainIndex, ]

# Salvataggio environment pulito
save(df, df_train, df_test, cat_vars, num_vars, file = "thyroid_data_clean.RData")
# Setup variabili
my_cat_vars <- c("Gender", "Country", "Ethnicity", "Family_History",
                 "Radiation_Exposure", "Iodine_Deficiency", "Smoking",
                 "Obesity", "Diabetes")
my_num_vars <- c("Age", "TSH_Level", "T3_Level", "T4_Level", "Nodule_Size")

dir.create("plots_selection", showWarnings = FALSE)

# --- 1. Variabili Numeriche (Boxplots) ---
plot_list_num <- list()
for(var in my_num_vars) {
  p <- ggplot(df_train, aes_string(x="Diagnosis", y=var, fill="Diagnosis")) +
    geom_boxplot(alpha=0.7, outlier.colour = "red", outlier.shape = 1) +
    scale_fill_manual(values=c("#66CC99", "#FF6666")) +
    labs(title = paste(var, "vs Diagnosi"), y = var, x = "") +
    theme_minimal() +
    theme(legend.position = "none")
  plot_list_num[[var]] <- p
}
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
ℹ Please use tidy evaluation idioms with `aes()`.
ℹ See also `vignette("ggplot2-in-packages")` for more information.
g_num <- grid.arrange(grobs = plot_list_num, ncol = 3, top = "Distribuzione Variabili Numeriche")
ggsave("plots_selection/01_Numerical_Variables.png", g_num, width = 12, height = 8)
grid::grid.draw(g_num) # Mostra in console

# --- 2. Variabili Categoriche (Barplots 100% Stacked) ---
plot_list_cat <- list()
for(var in my_cat_vars) {
  p <- ggplot(df_train, aes_string(x=var, fill="Diagnosis")) +
    geom_bar(position="fill") +
    scale_y_continuous(labels = scales::percent) +
    scale_fill_manual(values=c("#66CC99", "#FF6666")) +
    labs(title = var, y = "%", x = "") +
    theme_minimal() +
    theme(legend.position = "none",
          axis.text.x = element_text(angle = 45, hjust = 1, size=8))
  plot_list_cat[[var]] <- p
}

# Parte 1
g_cat1 <- grid.arrange(grobs = plot_list_cat[1:5], ncol = 3)
ggsave("plots_selection/02_Categorical_Part1.png", g_cat1, width = 12, height = 8)
grid::grid.draw(g_cat1) # Mostra in console

# Parte 2
g_cat2 <- grid.arrange(grobs = plot_list_cat[6:length(plot_list_cat)], ncol = 3)
ggsave("plots_selection/03_Categorical_Part2.png", g_cat2, width = 12, height = 8)
grid::grid.draw(g_cat2) # Mostra in console

# --- 3. Matrice di Correlazione ---
vars_cor <- c(my_num_vars, "Diagnosis_Num")
cor_matrix <- cor(df_train[, vars_cor], use = "complete.obs")
melted_cor <- melt(cor_matrix)

p_cor <- ggplot(data = melted_cor, aes(x=Var1, y=Var2, fill=value)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(value, 2)), size = 3) +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white", 
                       midpoint = 0, limit = c(-1,1)) +
  labs(title="Matrice di Correlazione") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))

ggsave("plots_selection/04_Correlation_Matrix.png", p_cor, width = 8, height = 7)
print(p_cor) # Mostra in console

#density
plot_list <- list()
df_plot <- df_train

for(var in my_num_vars) {
  mu <- df_plot %>%
    group_by(Diagnosis) %>%
    summarise(grp.mean = mean(.data[[var]], na.rm = TRUE))
  
  p <- ggplot(df_plot, aes_string(x = var, fill = "Diagnosis", color = "Diagnosis")) +
    geom_density(alpha = 0.4) +
    geom_vline(data = mu, aes(xintercept = grp.mean, color = Diagnosis),
               linetype = "dashed", size = 0.8) +
    scale_fill_manual(values = c("Benign" = "#66CC99", "Malignant" = "#FF6666")) +
    scale_color_manual(values = c("Benign" = "#2E8B57", "Malignant" = "#CD5C5C")) +
    labs(title = gsub("_", " ", var), x = var, y = "Densità") +
    theme_minimal() +
    theme(plot.title = element_text(size = 12, face = "bold"),
          legend.position = "none")
  
  plot_list[[var]] <- p
}
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
final_grid <- grid.arrange(grobs = plot_list, ncol = 3, 
                           top = "Distribuzione Variabili Cliniche: Benigni vs Maligni")

ggsave(filename = "Grafico_Densita_Numeriche.png", plot = final_grid, width = 14, height = 8)
grid::grid.draw(final_grid) # Mostra in console

#MCA
options(scipen = 999)
dir.create("Plots_Clustering_3D", showWarnings = FALSE)
dir.create("Plots_Model_Selection", showWarnings = FALSE)

set.seed(2025)
idx_sample <- createDataPartition(df_train$Diagnosis, p = 5000/nrow(df_train), list = FALSE)
df_sample <- df_train[idx_sample, ]

vars_risk <- c("Family_History", "Radiation_Exposure", "Iodine_Deficiency", 
               "Smoking", "Obesity", "Diabetes", "Gender")
df_sample[vars_risk] <- lapply(df_sample[vars_risk], as.factor)

# 1. Calcolo MCA con 4 Cluster (K-means integrato)
out_mca <- clusmca(df_sample[, vars_risk], nclus = 4, ndim = 3, method = "MCAk")

  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |=======                                                               |  10%
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if
`.name_repair` is omitted as of tibble 2.0.0.
ℹ Using compatibility `.name_repair`.
ℹ The deprecated feature was likely used in the clustrd package.
  Please report the issue to the authors.

  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |======================================================================| 100%
df_sample$C1_Kmeans <- factor(out_mca$cluster)

# 2. Plot Interattivo 3D (Aggiornato per 4 colori)
coords <- as.data.frame(out_mca$obscoord)
colnames(coords) <- c("Dim1", "Dim2", "Dim3")
coords$Cluster <- df_sample$C1_Kmeans

# Ho aggiunto il colore VIOLA ("#984EA3") per il 4° cluster
p <- plot_ly(coords, x = ~Dim1, y = ~Dim2, z = ~Dim3, color = ~Cluster, 
             colors = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3")) %>%
  add_markers(marker = list(size = 3)) %>%
  layout(title = "Cluster 3D Interattivo (MCA - 4 Gruppi)",
         scene = list(xaxis = list(title = 'Dim 1'),
                      yaxis = list(title = 'Dim 2'),
                      zaxis = list(title = 'Dim 3')))

saveWidget(p, "Plots_Clustering_3D/Cluster_Interactive.html", selfcontained = TRUE)
print(p) # Mostra nel Viewer

# 3. Calcolo altri cluster (Coerenti a k=4)
mca_coords_matrix <- out_mca$obscoord

# GMM a 4 componenti
mod_gmm <- Mclust(mca_coords_matrix, G = 4) 
df_sample$C3_GMM_MCA <- factor(mod_gmm$classification)

# Gerarchico con taglio a 4
dist_matrix <- dist(mca_coords_matrix)
hc_fit <- hclust(dist_matrix, method = "ward.D2")
df_sample$C2_Hierar <- factor(cutree(hc_fit, k = 4))

# Salvataggio
save(df_sample, out_mca, mod_gmm, file = "thyroid_clusters_ready.RData")
write.csv(df_sample, "thyroid_clusters_ready.csv", row.names = FALSE)
# ==============================================================================
# ANALISI DELLE CORRISPONDENZE MULTIPLE (MCA) - "CLASSICA"
# ==============================================================================

# 1. Caricamento Librerie dedicate
# Se non le hai, installale con: install.packages(c("FactoMineR", "factoextra"))
library(FactoMineR)
Warning: il pacchetto 'FactoMineR' è stato creato con R versione 4.5.2
library(factoextra)
library(dplyr)

# Creazione cartella per i grafici MCA
dir.create("Plots_MCA_Analysis", showWarnings = FALSE)

# 2. Preparazione del Dataset per MCA
# Se df_sample non esiste, usa df_train (o ricarica i dati)
if(!exists("df_sample")) {
  message("df_sample non trovato. Uso df_train o rigenero il campione...")
  # Assicurati di avere df_train caricato o scommenta la riga sotto se necessario:
  # df_sample <- df_train[sample(nrow(df_train), 5000), ] 
}

# Selezioniamo solo le variabili categoriche di interesse + Diagnosi
vars_mca_target <- c("Gender", "Family_History", "Radiation_Exposure", 
                     "Iodine_Deficiency", "Smoking", "Obesity", "Diabetes", 
                     "Diagnosis") # Diagnosis messa per ultima

# Creiamo un sotto-dataset pulito convertendo tutto in fattori
df_mca <- df_sample %>%
  dplyr::select(all_of(vars_mca_target)) %>%
  mutate_all(as.factor)

# 3. Esecuzione MCA (FactoMineR)
# 'quali.sup' indica l'indice della variabile supplementare (Diagnosis è l'ultima)
# graph = FALSE evita di stampare i grafici di default grezzi
idx_diagnosis <- length(vars_mca_target)
res_mca <- MCA(df_mca, quali.sup = idx_diagnosis, graph = FALSE)

# ==============================================================================
# 4. VISUALIZZAZIONE RISULTATI
# ==============================================================================

# A. Scree Plot (Percentuale di varianza spiegata dalle dimensioni)
p_eig <- fviz_eig(res_mca, addlabels = TRUE, ylim = c(0, 15)) +
  labs(title = "Scree Plot - Varianza spiegata dalle Dimensioni MCA")
Warning in geom_bar(stat = "identity", fill = barfill, color = barcolor, :
Ignoring empty aesthetic: `width`.
print(p_eig)

ggsave("Plots_MCA_Analysis/00_Scree_Plot.png", p_eig, width = 8, height = 6)


# B. Mappa delle Variabili (Categorie)
# Mostra le associazioni tra i fattori di rischio.
# Punti vicini = categorie che tendono a presentarsi insieme.
p_vars <- fviz_mca_var(res_mca, 
             choice = "var.cat",     # Mostra le categorie
             repel = TRUE,           # Evita sovrapposizione testo
             col.var = "contrib",    # Colora per contributo
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             shape.var = 15,
             ggtheme = theme_minimal()) +
  labs(title = "MCA - Mappa delle Categorie di Rischio",
       subtitle = "Le categorie vicine sono associate tra loro")

print(p_vars)

ggsave("Plots_MCA_Analysis/01_Risk_Factors_Map.png", p_vars, width = 10, height = 8)


# C. Biplot Individui colorati per Diagnosi
# Proietta i pazienti sullo spazio MCA e colora in base alla diagnosi (variabile supplementare)
# Le ellissi indicano l'area di confidenza del 95% per ciascun gruppo.
p_ind <- fviz_mca_ind(res_mca, 
             label = "none",            # Nascondi etichette punti (troppo affollato)
             habillage = "Diagnosis",   # Colora punti in base alla Diagnosi
             palette = c("#66CC99", "#FF6666"), # Verde (Benign), Rosso (Malignant)
             addEllipses = TRUE,        # Aggiungi ellissi
             ellipse.type = "confidence",
             alpha.ind = 0.4,           # Trasparenza
             ggtheme = theme_minimal()) +
  labs(title = "MCA - Separazione Pazienti Benigni vs Maligni",
       subtitle = "Sovrapposizione delle ellissi indica scarsa separabilità basata solo sui rischi")

print(p_ind)

ggsave("Plots_MCA_Analysis/02_Patients_Separation.png", p_ind, width = 10, height = 8)


# D. Descrizione delle Dimensioni
# Ti dice quali variabili pesano di più sulla Dimensione 1 e 2
desc <- dimdesc(res_mca, axes = c(1,2))
# Puoi ispezionare 'desc$Dim.1' in console per vedere i dettagli numerici
# ==============================================================================
# CONFRONTO: MCA CLASSICA (FactoMineR) vs CLUSTER-MCA (clusmca)
# ==============================================================================


# 1. Estrazione Coordinate MCA Classica (FactoMineR)
# Prendiamo le prime 2 dimensioni
coord_classic <- data.frame(res_mca$ind$coord[, 1:2])
colnames(coord_classic) <- c("Dim1", "Dim2")
coord_classic$Method <- "MCA Classica (Esplorativa)"
# Aggiungiamo le info sui cluster trovati da clusmca per vedere come si dispongono
coord_classic$Cluster <- as.factor(out_mca$cluster) 
coord_classic$Diagnosis <- df_sample$Diagnosis

# 2. Estrazione Coordinate Cluster-MCA (clusmca)
coord_clus <- data.frame(out_mca$obscoord[, 1:2])
colnames(coord_clus) <- c("Dim1", "Dim2")
coord_clus$Method <- "MCA Clustering (Ottimizzata)"
coord_clus$Cluster <- as.factor(out_mca$cluster)
coord_clus$Diagnosis <- df_sample$Diagnosis

# 3. Unione dei dati
df_compare <- rbind(coord_classic, coord_clus)

# --- GRAFICO 1: Come appaiono i Cluster nei due spazi? ---
p1 <- ggplot(df_compare, aes(x = Dim1, y = Dim2, color = Cluster)) +
  geom_point(alpha = 0.5, size = 1.5) +
  facet_wrap(~Method, scales = "free") + # Scale libere perché le unità variano
  theme_minimal() +
  scale_color_brewer(palette = "Set1") +
  labs(title = "Confronto Strutturale: Formazione dei Cluster",
       subtitle = "A SX: Come sono i dati naturalmente. A DX: Come l'algoritmo li forza per separarli.",
       caption = "Se a SX i colori sono mischiati ma a DX separati, i cluster sono 'forzati' dall'algoritmo.") +
  theme(legend.position = "bottom")

ggsave("Plots_MCA_Analysis/03_Compare_Clusters.png", p1, width = 12, height = 6)
print(p1)

# --- GRAFICO 2: Come si separa la DIAGNOSI nei due spazi? ---
# Questo risponde alla domanda: quale metodo separa meglio Maligni e Benigni?
p2 <- ggplot(df_compare, aes(x = Dim1, y = Dim2, color = Diagnosis)) +
  geom_point(alpha = 0.5, size = 1.5) +
  stat_ellipse(level = 0.95, size = 1) + # Ellissi al 95%
  facet_wrap(~Method, scales = "free") +
  scale_color_manual(values = c("Benign" = "#66CC99", "Malignant" = "#FF6666")) +
  theme_minimal() +
  labs(title = "Confronto Diagnostico: Separazione Benigni vs Maligni",
       subtitle = "Verifica se la manipolazione dello spazio aiuta a distinguere la diagnosi.",
       caption = "Ellissi sovrapposte = Scarsa capacità discriminante delle variabili categoriali.") +
  theme(legend.position = "bottom")

ggsave("Plots_MCA_Analysis/04_Compare_Diagnosis.png", p2, width = 12, height = 6)
print(p2)

#Inerzia
options(scipen = 999)
dir.create("Plots_Model_Selection", showWarnings = FALSE)

coords_mca <- out_mca$obscoord

p_elbow <- fviz_nbclust(coords_mca, kmeans, method = "wss", k.max = 10) +
  geom_vline(xintercept = 3, linetype = "dashed", color = "red") +
  labs(title = "Metodo del Gomito (Inerzia)",
       x = "Numero di Cluster (k)",
       y = "Total Within Sum of Square") +
  theme_minimal()

ggsave("Plots_Model_Selection/Inertia_Elbow_Method.png", p_elbow, width = 8, height = 6)
print(p_elbow) 

options(scipen = 999)
dir.create("Plots_Data_Quality", showWarnings = FALSE)

vars_check <- c("C1_Kmeans", "C2_Hierar", "C3_GMM_MCA") 
df_miss <- df_sample[, vars_check]

miss_stats <- data.frame(
  Var = names(df_miss),
  Pct = colMeans(is.na(df_miss)) * 100
)
write.csv(miss_stats, "Plots_Data_Quality/Missing_Stats.csv", row.names = FALSE)

# Heatmap Missing
df_long <- df_miss %>%
  mutate(ID = row_number()) %>%
  pivot_longer(cols = -ID, names_to = "Var", values_to = "Val") %>%
  mutate(Is_Missing = is.na(Val))

p1 <- ggplot(df_long, aes(x = Var, y = ID, fill = Is_Missing)) +
  geom_tile() +
  scale_fill_manual(values = c("FALSE" = "grey95", "TRUE" = "red"), labels = c("Presente", "Mancante")) +
  theme_minimal() +
  labs(title = "Mappa Valori Mancanti", x = "", y = "Indice Paziente", fill = "Stato") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggsave("Plots_Data_Quality/01_Missing_Map.png", p1, width = 10, height = 8)
print(p1) # Mostra in console

# Barplot Missing
p2 <- ggplot(miss_stats, aes(x = reorder(Var, Pct), y = Pct)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  theme_minimal() +
  labs(title = "% Valori Mancanti per Variabile", x = "", y = "%") +
  geom_text(aes(label = round(Pct, 1)), hjust = -0.1, size = 3)

ggsave("Plots_Data_Quality/02_Missing_Bar.png", p2, width = 8, height = 6)
print(p2) # Mostra in console

#analisi cluster
options(scipen = 999)
dir.create("Plots_Target_Analysis", showWarnings = FALSE)

# 1. Distribuzione Target
df_targ <- df_sample %>% 
  dplyr::count(Diagnosis) %>% 
  mutate(Pct = n / sum(n))

p1 <- ggplot(df_targ, aes(x = Diagnosis, y = n, fill = Diagnosis)) +
  geom_bar(stat = "identity", width = 0.6, show.legend = FALSE) +
  geom_text(aes(label = paste0(n, "\n(", scales::percent(Pct), ")")), vjust = -0.2) +
  scale_fill_manual(values = c("Benign" = "#66CC99", "Malignant" = "#FF6666")) +
  theme_minimal() +
  labs(title = "Distribuzione Variabile Target (Diagnosi)", x = "", y = "Conteggio")

ggsave("Plots_Target_Analysis/01_Target_Distribution.png", p1, width = 6, height = 6)
print(p1) # Mostra in console

# 2. Relazione Target vs Cluster
df_clust_targ <- df_sample %>%
  dplyr::select(C1_Kmeans, C2_Hierar, C3_GMM_MCA, Diagnosis) %>%
  pivot_longer(cols = c("C1_Kmeans", "C2_Hierar", "C3_GMM_MCA"), names_to = "Cluster_Type", values_to = "Cluster_ID")

p2 <- ggplot(df_clust_targ, aes(x = Cluster_ID, fill = Diagnosis)) +
  geom_bar(position = "fill") +
  facet_wrap(~Cluster_Type, scales = "free_x") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("Benign" = "#66CC99", "Malignant" = "#FF6666")) +
  theme_minimal() +
  labs(title = "Capacità dei Cluster di separare la Diagnosi", x = "Cluster ID", y = "% Malignità")

ggsave("Plots_Target_Analysis/02_Target_vs_Clusters.png", p2, width = 12, height = 6)
print(p2) # Mostra in console

# 3. Heatmap Fattori di Rischio
df_risk_targ <- df_sample %>%
  dplyr::select(all_of(c("Diagnosis", vars_risk))) %>%
  pivot_longer(cols = vars_risk, names_to = "Risk_Factor", values_to = "Value") %>%
  group_by(Risk_Factor, Value, Diagnosis) %>%
  dplyr::count() %>%
  group_by(Risk_Factor, Value) %>%
  mutate(Pct = n / sum(n)) %>%
  filter(Diagnosis == "Malignant")
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
  # Was:
  data %>% select(vars_risk)

  # Now:
  data %>% select(all_of(vars_risk))

See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
p3 <- ggplot(df_risk_targ, aes(x = Value, y = Risk_Factor, fill = Pct)) +
  geom_tile(color = "white") +
  scale_fill_gradient(low = "white", high = "red", labels = scales::percent) +
  geom_text(aes(label = scales::percent(Pct, accuracy = 1)), size = 3.5) +
  theme_minimal() +
  labs(title = "Probabilità di Malignità per Fattore di Rischio", fill = "Risk %")

ggsave("Plots_Target_Analysis/03_Target_Risk_Heatmap.png", p3, width = 8, height = 8)
print(p3) # Mostra in console

# 4. Feature Importance Random Forest (Target)
set.seed(2025)
rf_target <- randomForest(Diagnosis ~ ., 
                          data = df_sample[, c("Diagnosis", vars_risk, "C1_Kmeans", "C2_Hierar", "C3_GMM_MCA")], 
                          ntree = 100, importance = TRUE)

imp_df <- as.data.frame(importance(rf_target))
imp_df$Variable <- rownames(imp_df)

p4 <- ggplot(imp_df, aes(x = reorder(Variable, MeanDecreaseGini), y = MeanDecreaseGini)) +
  geom_bar(stat = "identity", fill = "#FF6666") +
  coord_flip() +
  theme_minimal() +
  labs(title = "Quali variabili predicono meglio la Diagnosi?", x = "", y = "Importanza")

ggsave("Plots_Target_Analysis/04_Target_Feature_Importance.png", p4, width = 8, height = 6)
print(p4) # Mostra in console

 df_sample %>%
 filter(C1_Kmeans == 1)%>% 
 boxplot(data= ., df_sample$TSH_Level~df_sample$Diagnosis)

 table(df_sample$C1_Kmeans, df_sample$Thyroid_Cancer_Risk)
   
    High Low Medium
  1  121 962    681
  2  178 911    576
  3  270 404    286
  4  189 257    166
 t <- table(df_sample$C1_Kmeans, df_sample$Thyroid_Cancer_Risk)
 plot(ca(t))

# analisi cluster
options(scipen = 999)
dir.create("Plots_Cluster_Analysis", showWarnings = FALSE)

vars_cat <- c("Gender", "Smoking", "Obesity", "Family_History", 
              "Radiation_Exposure", "Iodine_Deficiency", "Diabetes")
clusters  <- c("C1_Kmeans", "C2_Hierar", "C3_GMM_MCA")

for(k in clusters) {
  
  # Heatmap Rischi
  df_risk <- df_sample %>%
    dplyr::select(all_of(c(k, vars_cat))) %>%
    pivot_longer(cols = vars_cat, names_to = "Var", values_to = "Val") %>%
    dplyr::count(.data[[k]], Var, Val) %>%
    group_by(.data[[k]], Var) %>%
    mutate(Pct = n / sum(n)) %>%
    filter(Val == "Yes" | Val == "Male")
  
  p4 <- ggplot(df_risk, aes_string(x = k, y = "Var", fill = "Pct")) +
    geom_tile(color = "white") +
    scale_fill_gradient(low = "white", high = "red", labels = scales::percent) +
    geom_text(aes(label = scales::percent(Pct, accuracy = 1)), size = 3.5) +
    theme_minimal() +
    labs(title = paste("Profilo Rischi -", k), x = "", y = "")
  
  ggsave(paste0("Plots_Cluster_Analysis/02_Heatmap_", k, ".png"), p4, width = 8, height = 6)
  print(p4) # Mostra in console (funziona anche nel loop)
  
  # Barplot Diagnosi
  p5 <- ggplot(df_sample, aes_string(x = k, fill = "Diagnosis")) +
    geom_bar(position = "fill") +
    scale_y_continuous(labels = scales::percent) +
    scale_fill_manual(values = c("Benign" = "#66CC99", "Malignant" = "#FF6666")) +
    theme_minimal() +
    labs(title = paste("Composizione Diagnosi -", k), x = "Cluster", y = "%")
  
  ggsave(paste0("Plots_Cluster_Analysis/03_Diagnosis_", k, ".png"), p5, width = 8, height = 6)
  print(p5) # Mostra in console
}
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
  # Was:
  data %>% select(vars_cat)

  # Now:
  data %>% select(all_of(vars_cat))

See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.

#matrice cramer's
options(scipen = 999)
dir.create("Plots_Data_Quality", showWarnings = FALSE)


# 1. Selezione Variabili (Cluster + Diagnosi)
vars_assoc <- c("C1_Kmeans", "C2_Hierar", "C3_GMM_MCA", "Diagnosis")

df_assoc <- as.data.frame(df_sample[, vars_assoc]) 

# 2. Funzione V di Cramer (Robusta)
calc_cramer <- function(x, y) {
  # Assicuriamoci che siano fattori o vettori
  tbl <- table(as.factor(x), as.factor(y))
  chi2 <- chisq.test(tbl, correct = FALSE)$statistic
  n <- sum(tbl)
  k <- min(dim(tbl)) - 1
  if(k == 0) return(0) # Gestione errori per variabili costanti
  return(as.numeric(sqrt(chi2 / (n * k))))
}

# 3. Calcolo Matrice
n <- ncol(df_assoc)
mat_cramer <- matrix(0, nrow = n, ncol = n)
rownames(mat_cramer) <- colnames(df_assoc)
colnames(mat_cramer) <- colnames(df_assoc)

for (i in 1:n) {
  for (j in 1:n) {
    if (i == j) {
      mat_cramer[i, j] <- 1
    } else {
      # Usiamo [[ ]] che è più sicuro per estrarre vettori
      mat_cramer[i, j] <- calc_cramer(df_assoc[[i]], df_assoc[[j]])
    }
  }
}

# 4. Plot
df_plot <- as.data.frame(as.table(mat_cramer))
colnames(df_plot) <- c("Var1", "Var2", "Value")

p <- ggplot(df_plot, aes(x = Var1, y = Var2, fill = Value)) +
  geom_tile(color = "white") +
  geom_text(aes(label = sprintf("%.2f", Value)), size = 4, fontface = "bold") +
  scale_fill_gradient(low = "white", high = "#FF3333", limits = c(0, 1)) +
  theme_minimal() +
  labs(title = "Matrice di Associazione (Cramer's V)", 
       subtitle = "1.00 = Ridondanza Totale (stessa informazione)",
       x = "", y = "", fill = "Cramer's V") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(face = "bold"))

ggsave("Plots_Data_Quality/04_Association_Matrix.png", p, width = 8, height = 7)
print(p)

options(scipen = 999)
dir.create("Plots SL Balanced", showWarnings = FALSE)


# 1. Preparazione Dati 
vars_use <- c(cat_vars, "C1_Kmeans", "Diagnosis") 
df_work <- df_sample[, vars_use]
df_work$Diagnosis <- as.factor(df_work$Diagnosis)

# Creazione Matrice 
x_mat <- model.matrix(Diagnosis ~ . - 1, data = df_work)


nzv_cols <- nearZeroVar(x_mat)
if(length(nzv_cols) > 0) x_mat <- x_mat[, -nzv_cols]

comboInfo <- findLinearCombos(x_mat)
if(length(comboInfo$remove) > 0) x_mat <- x_mat[, -comboInfo$remove]

colnames(x_mat) <- make.names(colnames(x_mat))
y_vec <- df_work$Diagnosis

# 2. Training (con Upsampling)
set.seed(2025)
ctrl <- caret::trainControl(method = "cv", number = 5, 
                            savePredictions = "final", 
                            classProbs = TRUE, 
                            summaryFunction = twoClassSummary, 
                            sampling = "up") 

models <- list()
print("Training modelli in corso... (Uso caret::train per evitare errori)")
[1] "Training modelli in corso... (Uso caret::train per evitare errori)"
#modelli
models$Logit <- caret::train(x = x_mat, y = y_vec, method = "glm", metric = "ROC", trControl = ctrl)
models$LDA   <- caret::train(x = x_mat, y = y_vec, method = "lda", metric = "ROC", trControl = ctrl)
Warning in lda.default(x, grouping, ...): le variabili sono collineari
Warning in lda.default(x, grouping, ...): le variabili sono collineari
Warning in lda.default(x, grouping, ...): le variabili sono collineari
Warning in lda.default(x, grouping, ...): le variabili sono collineari
Warning in lda.default(x, grouping, ...): le variabili sono collineari
Warning in lda.default(x, grouping, ...): le variabili sono collineari
models$NB    <- caret::train(x = x_mat, y = y_vec, method = "nb", metric = "ROC", trControl = ctrl, 
                             tuneGrid = data.frame(fL=1, usekernel=TRUE, adjust=1))
models$SVM   <- caret::train(x = x_mat, y = y_vec, method = "svmRadial", metric = "ROC", trControl = ctrl, 
                             preProcess = c("center", "scale"), tuneLength = 3)
line search fails -1.308176 -0.4019754 0.00001030248 0.000009466971 -0.00000002275996 -0.00000001696896 -0.0000000000003951288
Warning in method$predict(modelFit = modelFit, newdata = newdata, submodels =
param): kernlab class prediction calculations failed; returning NAs
Warning in method$prob(modelFit = modelFit, newdata = newdata, submodels =
param): kernlab class probability calculations failed; returning NAs
Warning in nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo,
: There were missing values in resampled performance measures.
models$RF    <- caret::train(x = x_mat, y = y_vec, method = "rf", metric = "ROC", trControl = ctrl, 
                             ntree = 100, tuneLength = 3)

# 3. Estrazione Risultati
res_metrics <- data.frame()
res_roc <- data.frame()
auc_vals <- list()

for(nom in names(models)) {
  mod <- models[[nom]]
  preds <- mod$pred
  
  if(length(mod$bestTune) > 0) {
    idx <- rep(TRUE, nrow(preds))
    for(p in names(mod$bestTune)) idx <- idx & (preds[[p]] == mod$bestTune[[p]])
    preds <- preds[idx, ]
  }
  
  cm <- confusionMatrix(preds$pred, preds$obs, mode="everything", positive="Malignant")
  tbl <- cm$table
  
  # Calcolo Falsi Positivi e Negativi
  fp_rate <- tbl["Malignant", "Benign"] / sum(tbl)
  fn_rate <- tbl["Benign", "Malignant"] / sum(tbl)
  
  res_metrics <- rbind(res_metrics, data.frame(
    Model = nom,
    Accuracy  = as.numeric(cm$overall["Accuracy"]),
    F1        = as.numeric(cm$byClass["F1"]),
    Precision = as.numeric(cm$byClass["Precision"]),
    Recall    = as.numeric(cm$byClass["Recall"]),
    FP_Rate   = fp_rate,
    FN_Rate   = fn_rate
  ))
  
  roc_obj <- roc(preds$obs, preds$Malignant, levels=c("Benign", "Malignant"), direction="<", quiet=TRUE)
  auc_vals[[nom]] <- as.numeric(auc(roc_obj))
  res_roc <- rbind(res_roc, data.frame(Model=nom, Sens=roc_obj$sensitivities, Spec=roc_obj$specificities))
}

# 4. Grafici

# A. Performance
df_m <- pivot_longer(res_metrics, cols = c("Accuracy", "F1", "Precision", "Recall"), names_to = "Metrica", values_to = "Val")

p1 <- ggplot(df_m, aes(x = Model, y = Val, fill = Metrica)) +
  geom_bar(stat = "identity", position = "dodge", width = 0.7) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 1.05)) +
  theme_minimal() + theme(legend.position = "bottom") +
  labs(title = "Performance Modelli (Con Bilanciamento Classi)", x = "", y = "")

print(p1)

ggsave("Plots SL Balanced/01_Performance.png", p1, width = 12, height = 7)

# B. Distribuzione Errori (Il grafico che volevi)
df_e <- pivot_longer(res_metrics, cols = c("FP_Rate", "FN_Rate"), names_to = "ErrType", values_to = "Val")

p2 <- ggplot(df_e, aes(x = Model, y = Val, fill = ErrType)) +
  geom_bar(stat = "identity", position = "stack", width = 0.6) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.1)) +
  scale_fill_manual(values = c("FP_Rate"="#FFCC00", "FN_Rate"="#FF3333"), 
                    labels=c("Falsi Negativi (Gravi)", "Falsi Positivi")) +
  theme_minimal() + theme(legend.position = "bottom") +
  labs(title = "Distribuzione Errori (Bilanciato)", x = "", y = "% Errori") +
  geom_text(aes(label = scales::percent(Val, accuracy=0.1)), position = position_stack(vjust = 0.5), size = 3)

print(p2)

ggsave("Plots SL Balanced/02_Errors.png", p2, width = 10, height = 6)

# C. Curve ROC
lbls <- sapply(names(auc_vals), function(x) paste0(x, " (AUC: ", round(auc_vals[[x]]*100, 1), "%)"))

p3 <- ggplot(res_roc, aes(x = 1 - Spec, y = Sens, color = Model)) +
  geom_path(size = 1.1) +
  geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "grey50") +
  scale_color_discrete(labels = lbls) +
  scale_x_continuous(limits = c(0,1), expand = c(0,0)) +
  scale_y_continuous(limits = c(0,1), expand = c(0,0)) +
  theme_minimal() + 
  theme(legend.position = c(0.75, 0.25), legend.background = element_rect(fill="white", color="grey80")) +
  labs(title = "Curve ROC Comparate (Bilanciato)", x = "1 - Specificity", y = "Sensitivity")

print(p3)

ggsave("Plots SL Balanced/03_ROC.png", p3, width = 8, height = 8)

# Tabella finale
print(res_metrics)
  Model  Accuracy        F1 Precision    Recall   FP_Rate    FN_Rate
1 Logit 0.6478704 0.4404194 0.3494705 0.5953608 0.2579484 0.09418116
2   LDA 0.6492701 0.4399745 0.3501016 0.5919244 0.2557489 0.09498100
3    NB 0.6862627 0.4370291 0.3752311 0.5231959 0.2027594 0.11097780
4   SVM 0.7810438 0.5241199 0.5303430 0.5180412 0.1067786 0.11217756
5    RF 0.7632474 0.4991540 0.4916667 0.5068729 0.1219756 0.11477704
#feature RF SVM
options(scipen = 999)
dir.create("Plots_Feature_Importance", showWarnings = FALSE)

# Random Forest
imp_rf <- varImp(models$RF, scale = TRUE)
df_rf <- imp_rf$importance
colnames(df_rf)[1] <- "Importance"
df_rf$Variable <- rownames(df_rf)
top_rf <- df_rf %>% arrange(desc(Importance)) %>% head(20)

p1 <- ggplot(top_rf, aes(x = reorder(Variable, Importance), y = Importance)) +
  geom_bar(stat = "identity", fill = "#FF6666", width = 0.7) +
  coord_flip() + theme_minimal() +
  labs(title = "Feature Importance - Random Forest", x = "", y = "Importanza Scalata")

ggsave("Plots_Feature_Importance/01_Importance_RF.png", p1, width = 10, height = 8)
print(p1) 

# SVM
imp_svm <- varImp(models$SVM, scale = TRUE)
df_svm <- imp_svm$importance
colnames(df_svm)[1] <- "Importance"
df_svm$Variable <- rownames(df_svm)
top_svm <- df_svm %>% arrange(desc(Importance)) %>% head(20)

p2 <- ggplot(top_svm, aes(x = reorder(Variable, Importance), y = Importance)) +
  geom_bar(stat = "identity", fill = "#3399FF", width = 0.7) +
  coord_flip() + theme_minimal() +
  labs(title = "Feature Importance - SVM (Radial)", x = "", y = "Importanza Scalata")

ggsave("Plots_Feature_Importance/02_Importance_SVM.png", p2, width = 10, height = 8)
print(p2) # Mostra in console