library(tidyverse)
library(rpart)
library(rpart.plot)
library(caret)
library(knitr)
library(kableExtra)Το Mushroom Dataset (UCI Machine Learning Repository) περιέχει 8.124 μανιτάρια με 22 κατηγορικά χαρακτηριστικά. Στόχος είναι να προβλέψουμε αν ένα μανιτάρι είναι βρώσιμο (e) ή τοξικό (p).
Η ανάλυση ακολουθεί τα 4 βήματα του μαθήματος και συγκρίνει το CART με τη Λογιστική Παλινδρόμηση.
mush <- read.csv("mushrooms.csv", stringsAsFactors = TRUE)
# Καθαρά ονόματα στηλών
colnames(mush) <- c(
"class", "cap_shape", "cap_surface", "cap_color", "bruises", "odor",
"gill_attachment", "gill_spacing", "gill_size", "gill_color",
"stalk_shape", "stalk_root", "stalk_surface_above_ring",
"stalk_surface_below_ring", "stalk_color_above_ring",
"stalk_color_below_ring", "veil_type", "veil_color",
"ring_number", "ring_type", "spore_print_color", "population", "habitat"
)
# Μετατροπή target σε περιγραφικές τιμές
mush$class <- fct_recode(mush$class, edible = "e", poisonous = "p")
cat("Διαστάσεις:", nrow(mush), "x", ncol(mush), "\n")## Διαστάσεις: 8124 x 23
##
## edible poisonous
## 4208 3916
# Ποσοστό τοξικών ανά μυρωδιά — η πιο διαχωριστική μεταβλητή
mush %>%
count(odor, class) %>%
group_by(odor) %>%
mutate(pct = n / sum(n)) %>%
filter(class == "poisonous") %>%
ggplot(aes(x = reorder(odor, pct), y = pct, fill = pct)) +
geom_col(alpha = 0.85) +
scale_fill_gradient(low = "#A5D6A7", high = "#B71C1C") +
scale_y_continuous(labels = scales::percent) +
coord_flip() +
labs(title = "Ποσοστό Τοξικών ανά Μυρωδιά (odor)",
x = "Κωδικός Μυρωδιάς", y = "% Τοξικά") +
theme_minimal() +
theme(legend.position = "none")Μανιτάρια με μυρωδιά
f(foul) ήp(pungent) είναι σχεδόν πάντα τοξικά. Η μεταβλητήodorαναμένεται να είναι η πιο σημαντική στο δέντρο.
# Αφαίρεση μεταβλητής με μία μόνο τιμή (veil_type — χωρίς πληροφορία)
mush <- mush %>% select(-veil_type)
# Διαχωρισμός Train / Test (75% / 25%, stratified)
set.seed(42)
train_idx <- createDataPartition(mush$class, p = 0.75, list = FALSE)
train_set <- mush[ train_idx, ]
test_set <- mush[-train_idx, ]
cat("Train:", nrow(train_set), "| Test:", nrow(test_set), "\n")## Train: 6093 | Test: 2031
##
## edible poisonous
## 0.518 0.482
tree_model <- rpart(class ~ ., data = train_set, method = "class",
control = rpart.control(cp = 0.001, minbucket = 7))
# Κλάδεμα με βέλτιστο cp
best_cp <- tree_model$cptable[which.min(tree_model$cptable[,"xerror"]), "CP"]
tree_pruned <- prune(tree_model, cp = best_cp)
cat("Βέλτιστο cp:", round(best_cp, 6), "\n")## Βέλτιστο cp: 0.001
## Φύλλα δέντρου: 6
rpart.plot(tree_pruned, type = 4, extra = 104,
box.palette = c("#A5D6A7", "#EF9A9A"),
fallen.leaves = TRUE,
main = "CART Decision Tree — Mushroom Dataset")Ο πρώτος διαχωρισμός γίνεται βάσει της μυρωδιάς (odor) — επιβεβαιώνει το EDA. Με λίγους μόνο κόμβους το δέντρο φτάνει σχεδόν τέλεια ακρίβεια.
Χρησιμοποιώ υποσύνολο μεταβλητών για να αποφύγω προβλήματα perfect separation που προκύπτουν από τις πολλές κατηγορικές μεταβλητές.
logit_model <- glm(
class ~ odor + spore_print_color + gill_color + ring_type,
data = train_set,
family = binomial(link = "logit")
)
summary(logit_model)##
## Call:
## glm(formula = class ~ odor + spore_print_color + gill_color +
## ring_type, family = binomial(link = "logit"), data = train_set)
##
## Coefficients: (1 not defined because of singularities)
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.090e+11 3.334e+13 0.003 0.99739
## odorc 8.656e+01 3.919e+04 0.002 0.99824
## odorf -1.090e+11 3.334e+13 -0.003 0.99739
## odorl 3.888e-02 2.452e+04 0.000 1.00000
## odorm 2.459e+01 6.873e+04 0.000 0.99971
## odorn -2.445e+00 2.544e+04 0.000 0.99992
## odorp 8.566e+01 3.695e+04 0.002 0.99815
## odors -1.090e+11 3.334e+13 -0.003 0.99739
## odory -1.090e+11 3.334e+13 -0.003 0.99739
## spore_print_colorh 1.090e+11 3.334e+13 0.003 0.99739
## spore_print_colork 1.770e+01 7.026e+04 0.000 0.99980
## spore_print_colorn 3.884e+00 5.772e+04 0.000 0.99995
## spore_print_coloro 1.173e-01 8.261e+04 0.000 1.00000
## spore_print_colorr 9.398e+01 7.791e+04 0.001 0.99904
## spore_print_coloru 1.687e+01 8.819e+04 0.000 0.99985
## spore_print_colorw 4.381e+01 5.773e+04 0.001 0.99939
## spore_print_colory -9.752e-02 7.713e+04 0.000 1.00000
## gill_colore -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorg -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorh -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colork -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorn -1.090e+11 3.334e+13 -0.003 0.99739
## gill_coloro -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorp -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorr -1.090e+11 3.334e+13 -0.003 0.99739
## gill_coloru -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colorw -1.090e+11 3.334e+13 -0.003 0.99739
## gill_colory -1.090e+11 3.334e+13 -0.003 0.99739
## ring_typef -1.090e+11 3.334e+13 -0.003 0.99739
## ring_typel -5.782e-01 2.868e+04 0.000 0.99998
## ring_typen NA NA NA NA
## ring_typep -1.469e+00 4.671e-01 -3.145 0.00166 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 8438.82 on 6092 degrees of freedom
## Residual deviance: 188.88 on 6062 degrees of freedom
## AIC: 250.88
##
## Number of Fisher Scoring iterations: 25
# Προβλέψεις CART
pred_tree <- predict(tree_pruned, test_set, type = "class")
# Προβλέψεις Logistic
pred_logit_prob <- predict(logit_model, test_set, type = "response")
pred_logit <- factor(ifelse(pred_logit_prob >= 0.5, "poisonous", "edible"),
levels = levels(test_set$class))## === Confusion Matrix: CART ===
## Confusion Matrix and Statistics
##
## Reference
## Prediction edible poisonous
## edible 1052 2
## poisonous 0 977
##
## Accuracy : 0.999
## 95% CI : (0.9964, 0.9999)
## No Information Rate : 0.518
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.998
##
## Mcnemar's Test P-Value : 0.4795
##
## Sensitivity : 0.9980
## Specificity : 1.0000
## Pos Pred Value : 1.0000
## Neg Pred Value : 0.9981
## Prevalence : 0.4820
## Detection Rate : 0.4810
## Detection Prevalence : 0.4810
## Balanced Accuracy : 0.9990
##
## 'Positive' Class : poisonous
##
## === Confusion Matrix: Logistic Regression ===
## Confusion Matrix and Statistics
##
## Reference
## Prediction edible poisonous
## edible 1052 10
## poisonous 0 969
##
## Accuracy : 0.9951
## 95% CI : (0.991, 0.9976)
## No Information Rate : 0.518
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.9901
##
## Mcnemar's Test P-Value : 0.004427
##
## Sensitivity : 0.9898
## Specificity : 1.0000
## Pos Pred Value : 1.0000
## Neg Pred Value : 0.9906
## Prevalence : 0.4820
## Detection Rate : 0.4771
## Detection Prevalence : 0.4771
## Balanced Accuracy : 0.9949
##
## 'Positive' Class : poisonous
##
tibble(
Μετρική = c("Accuracy", "Sensitivity", "Specificity", "Kappa"),
CART = round(c(cm_tree$overall["Accuracy"],
cm_tree$byClass["Sensitivity"],
cm_tree$byClass["Specificity"],
cm_tree$overall["Kappa"]), 4),
Logistic = round(c(cm_logit$overall["Accuracy"],
cm_logit$byClass["Sensitivity"],
cm_logit$byClass["Specificity"],
cm_logit$overall["Kappa"]), 4)
) %>%
kable(caption = "Σύγκριση: CART vs Λογιστική Παλινδρόμηση") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| Μετρική | CART | Logistic |
|---|---|---|
| Accuracy | 0.999 | 0.9951 |
| Sensitivity | 0.998 | 0.9898 |
| Specificity | 1.000 | 1.0000 |
| Kappa | 0.998 | 0.9901 |
odor που διαχωρίζει σχεδόν τέλεια τις δύο
κλάσεις.EA-008 | Δέντρα Απόφασης & CART | Mushroom Dataset