Το συγκεκριμένο σύνολο δεδομένων αφορά την ανάλυση του ανθρώπινου δυναμικού μιας εταιρείας και στοχεύει στην κατανόηση των παραγόντων που οδηγούν στην αποχώρηση των εργαζομένων (Employee Churn).
Ο κύριος στόχος είναι η δημιουργία ενός μοντέλου Λογιστικής
Παλινδρόμησης που θα προβλέπει την πιθανότητα ένας υπάλληλος να
εγκαταλείψει την εταιρεία (εξαρτημένη μεταβλητή: Left ή
Retention), επιτρέποντας στη διοίκηση να λάβει προληπτικά
μέτρα.
salary: Το επίπεδο του μισθού κατηγοριοποιημένο σε low, medium, και high.
left (Target Variable): Η μεταβλητή που θέλουμε να προβλέψουμε. Δείχνει αν ο υπάλληλος αποχώρησε (1) ή παρέμεινε (0) στην εταιρεία.
Σε πρώτο επίπεδο, έχει σημασία να εντοπίσουμε μεταβλητές που δείχνουν να σχετίζονται κατά πολύ με την εξαρτημένη μας μεταβλητή (left - αποχώρηση).
Συσχετίσεις με Μετρήσιμες Ανεξάρτητες Μεταβλητές
Από τις ποσοτικές μεταβλητές, επιλέγουμε να διερευνήσουμε το επίπεδο ικανοποίησης εργαζομένου (satisfaction_level) και την τελευταία βαθμολογία αξιολόγησης του (last_evaluation), καθώς μπορεί να υποτεθεί ότι καθορίζουν το εάν ένας υπάλληλος θα μείνει σε μία εταιρεία ή όχι.
library(ggplot2)
# Boxplot για το Last Evaluation
ggplot(hr_data, aes(x = as.factor(left), y = last_evaluation, fill = as.factor(left))) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("#3498db", "#e67e22"), labels = c("Παρέμεινε", "Αποχώρησε")) +
labs(title = "Κατανομή Βαθμολογίας Αξιολόγησης ανά Κατάσταση Παραμονής",
x = "Κατάσταση (0: Παρέμεινε, 1: Αποχώρησε)",
y = "Score Τελευταίας Αξιολόγησης",
fill = "Κατηγορία") +
theme_minimal()Όπως φαίνεται παραπάνω, τα υψηλότερα σκορ στις αξιολογήσεις τα φέρουν αυτοί οι οποίοι τελικά αποχωρούν από την εταιρεία, καθώς οι μισοί εκ των αποχωρούντων λαμβάνουν βαθμολογίες από 0.8 περίπου και άνω, ενώ οι αντίστοιχες βαθμολογίες όσων παραμένουν κυμάινονατι περί το ένα δέκατο της μονάδας πιο χαμηλά. Η μέση βαθμολογία είναι παρόμοια και για τις δύο ομάδες, γεγονός που δείχνει ότι η απόδοση από μόνη της δεν προεξοφλεί την αποχώρηση.Επιπλέον, παρατηρείται αυξημένη διασπορά στους υπαλλήλους που έφυγαν. Αυτό υποδηλώνει ότι η εταιρεία χάνει δύο τύπους εργαζομένων: τους Low Performers (πιθανή απομάκρυνση λόγω απόδοσης) και τους High Performers (πιθανή αναζήτηση καλύτερων ευκαιριών). Τέλος μπορούμε να πούμε βάσει του διαγράμματος ότι οι υπάλληλοι που παραμένουν έχουν πιο συγκεντρωμένες βαθμολογίες γύρω από τον μέσο όρο.
ggplot(hr_data, aes(x = satisfaction_level, fill = as.factor(left))) +
geom_density(alpha = 0.5) + # Το alpha κάνει τα χρώματα ημιδιαφανή για να φαίνεται η επικάλυψη
scale_fill_manual(values = c("#3498db", "#e67e22"),
labels = c("Παρέμεινε", "Αποχώρησε")) +
labs(title = "Κατανομή Πυκνότητας Ικανοποίησης",
x = "Επίπεδο Ικανοποίησης",
y = "Πυκνότητα",
fill = "Κατάσταση") +
theme_minimal()Το διάγραμμα πυκνότητας (Density Plot) αποκαλύπτει μια ξεκάθαρη πόλωση μεταξύ των δύο ομάδων. Για την Ομάδα Παραμονής (Πράσινο), η κατανομή συγκεντρώνεται σε υψηλά επίπεδα ικανοποίησης (0.6-0.9), υποδηλώνοντας ότι η σταθερότητα συνδέεται άμεσα με τη θετική εργασιακή εμπειρία. Στην Ομάδα Αποχώρησης (Κόκκινο): εντοπίζουμε τρεις διακριτές κορυφές. Η κυρίαρχη εντοπίζεται σε εξαιρετικά χαμηλά επίπεδα (<0.15) ενώ μια δεύτερη γύρω στο 0.4, επιβεβαιώνοντας ότι η χαμηλή ικανοποίηση είναι ο βασικός μοχλός φυγής. Συνεπώς, αντιλαμβανόμαστε ότι η επικάλυψη των δύο κατανομών είναι μικρή, καθιστώντας το επίπεδο ικανοποίησης των εργαζομένων ως την πιο αξιόπιστη μεταβλητή για το μοντέλο μας, καθώς διαχωρίζει με μεγάλη ακρίβεια τις δύο κατηγορίες υπαλλήλων.
Διερεύνηση μη Ποσοτικών Μεταβλητών
Παρατηρώντας τις εν λόγω μεταβλητές, μπορούμε να διακρίνουμε ως πιο ενδιαφέρουσες για την εταιρεία που διερευνούμε τον μισθό και το τμήμα απασχόλησης των υπαλλήλων.
ggplot(hr_data, aes(x = salary, fill = as.factor(left))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = c("#3498db", "#e67e22"),
labels = c("Παρέμεινε", "Αποχώρησε")) +
labs(title = "Ανάλυση Αποχώρησης ανά Επίπεδο Μισθού",
x = "Μισθός",
y = "Ποσοστό (%)",
fill = "Κατάσταση") +
theme_minimal()Η ανάλυση του γραφήματος για τον μισθό (salary)
αναδεικνύει την ισχυρή επίδραση των οικονομικών απολαβών στην παραμονή
του προσωπικού:
Χαμηλός Μισθός (Low): Παρουσιάζει το μεγαλύτερο ποσοστό αποχώρησης (σχεδόν 30%). Οι υπάλληλοι σε αυτή την κατηγορία είναι οι πλέον ευάλωτοι στην έξοδο, πιθανώς λόγω αναζήτησης καλύτερων οικονομικών όρων.
Υψηλός Μισθός (High): Η αποχώρηση είναι ελάχιστη (κάτω από 10%), αποδεικνύοντας ότι οι υψηλές απολαβές λειτουργούν ως ισχυρός παράγοντας συγκράτησης (retention factor).
Συμπέρασμα: Ο μισθός αποτελεί καθοριστική κατηγορική μεταβλητή. Η σταδιακή μείωση του ποσοστού αποχώρησης όσο αυξάνεται το μισθολογικό επίπεδο επιβεβαιώνει την ανάγκη συμπερίληψής του στο μοντέλο λογιστικής παλινδρόμησης για την ακριβή πρόβλεψη του κινδύνου φυγής.
library(ggplot2)
ggplot(hr_data, aes(y = Department, fill = as.factor(left))) +
geom_bar(position = "fill") +
scale_x_continuous(labels = scales::percent) +
scale_fill_manual(values = c("#3498db", "#e67e22"),
labels = c("Παρέμεινε", "Αποχώρησε")) +
labs(title = "Ποσοστό Αποχώρησης ανά Τμήμα",
x = "Ποσοστό (%)",
y = "Τμήμα",
fill = "Κατάσταση") +
theme_minimal()Η ανάλυση ανά τμήμα δείχνει ότι η αποχώρηση δεν κατανέμεται ομοιόμορφα, αναδεικνύοντας διαφορετικές εργασιακές δυναμικές:
Τμήματα Υψηλού Κινδύνου: Τα τμήματα Sales, Technical και Support παρουσιάζουν τα υψηλότερα ποσοστά αποχώρησης. Η φύση αυτών των ρόλων, που συχνά συνδέεται με υψηλή πίεση και άμεση επαφή με πελάτες, φαίνεται να ενισχύει την τάση φυγής.
Τμήματα Σταθερότητας: Το Management και το RandD εμφανίζουν τη μεγαλύτερη συγκράτηση προσωπικού, γεγονός που υποδηλώνει υψηλότερη ικανοποίηση ή ισχυρότερα κίνητρα παραμονής.
Συμπέρασμα: Η μεταβλητή Department λειτουργεί ως δείκτης του εργασιακού περιβάλλοντος. Η διαφοροποίηση των ποσοστών επιβεβαιώνει ότι ο κίνδυνος αποχώρησης επηρεάζεται σημαντικά από το τμήμα στο οποίο ανήκει ο υπάλληλος, καθιστώντας την απαραίτητη για το μοντέλο μας.
Το δείγμα του Dataset θα χωριστεί σε δύο μέρη, το training dataset και το testing dataset. Το πρώτο θα χρησιμοποιηθεί για να στήσουμε και το μοντέλο μας για την λογιστική παλινδρόμηση, ενώ το δεύτερο προκειμένου να κάνουμε πρόβλεψη βάσει του μοντέλου και να ελέγξουμε κατά πόσο είναι αξιόπιστο. Συγκεκριμένα, το 65% των εγγραφών θα χρησιμοπoιηθεί για το Training, ενώ το απομένον 35% για το Testing.
library(caTools)
set.seed(914)
split <- sample.split(hr_data$left, SplitRatio = 0.65)
train_set <- subset(hr_data, split == TRUE)
test_set <- subset(hr_data, split == FALSE)
split_summary <- data.frame(
Dataset = c("Training Set", "Test Set", "Total"),
Records = c(nrow(train_set), nrow(test_set), nrow(hr_data)),
Percentage = c("65%", "35%", "100%")
)
knitr::kable(split_summary,
caption = "Σύνοψη Διαχωρισμού Δεδομένων (Train/Test Split)",
align = "clc")| Dataset | Records | Percentage |
|---|---|---|
| Training Set | 9749 | 65% |
| Test Set | 5250 | 35% |
| Total | 14999 | 100% |
Το μοντέλο μας θα προσπαθεί να προβλέψει το εάν ένας υπάλληλος θα παραμείνει στην εταιρεία ή θα αποχωρήσει (left - εξαρτημένη μεταβλητή), λαμβάνοντας υπόψιν όλες τις ανεξάρτητες μεταβλητές του dataset.
movement_model<- glm(left~satisfaction_level + last_evaluation + number_project + average_montly_hours + time_spend_company + Work_accident + promotion_last_5years + Department + salary,data=train_set,family = "binomial")
summary(movement_model)##
## Call:
## glm(formula = left ~ satisfaction_level + last_evaluation + number_project +
## average_montly_hours + time_spend_company + Work_accident +
## promotion_last_5years + Department + salary, family = "binomial",
## data = train_set)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.5938127 0.2471646 -6.448 1.13e-10 ***
## satisfaction_level -4.1301900 0.1215977 -33.966 < 2e-16 ***
## last_evaluation 0.6105530 0.1849198 3.302 0.000961 ***
## number_project -0.3172107 0.0264594 -11.989 < 2e-16 ***
## average_montly_hours 0.0053949 0.0006408 8.419 < 2e-16 ***
## time_spend_company 0.2764524 0.0194166 14.238 < 2e-16 ***
## Work_accident -1.4996663 0.1102509 -13.602 < 2e-16 ***
## promotion_last_5years -1.4256645 0.3233128 -4.410 1.04e-05 ***
## Departmenthr 0.0935564 0.1622001 0.577 0.564077
## DepartmentIT -0.4018228 0.1529759 -2.627 0.008622 **
## Departmentmanagement -0.7116959 0.2044371 -3.481 0.000499 ***
## Departmentmarketing -0.1686006 0.1627278 -1.036 0.300160
## Departmentproduct_mng -0.3352609 0.1632523 -2.054 0.040011 *
## DepartmentRandD -0.8684741 0.1795333 -4.837 1.32e-06 ***
## Departmentsales -0.2081700 0.1264949 -1.646 0.099830 .
## Departmentsupport -0.0430075 0.1345284 -0.320 0.749203
## Departmenttechnical -0.0492245 0.1315104 -0.374 0.708180
## salarylow 2.0857245 0.1689024 12.349 < 2e-16 ***
## salarymedium 1.5234885 0.1697261 8.976 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 10701.5 on 9748 degrees of freedom
## Residual deviance: 8289.1 on 9730 degrees of freedom
## AIC: 8327.1
##
## Number of Fisher Scoring iterations: 5
Η γενικευμένη εμφάνιση τριών αστεριών (***) σε σχεδόν όλες τις
μεταβλητές του μοντέλου υποδηλώνει εξαιρετικά υψηλή στατιστική
σημαντικότητα. Συγκεκριμένα, οι μεταβλητές όπως
satisfaction_level, last_evaluation,
number_project, average_montly_hours,
time_spend_company, Work_accident,
promotion_last_5years, δεν επηρεάζουν τυχαία την αποχώρηση,
αλλά αποτελούν δομικούς παράγοντες που καθορίζουν την απόφαση των
υπαλλήλων, εφόσον έχουν το υψηλότερο significance που θα μπορούσαν να
λάβουν. Το γεγονός ότι ακόμη και οι κατηγορίες των τμημάτων
(Department - και συγκεγκριμένα τα τμήματα διοίκησης και
R&D) ή της μισθολογικής κατηγορίας (salary) έχουν υψηλή
σημαντικότητα σε κάποιες από τις κατηγορικές τιμές τους (***), δείχνει
ότι το μοντέλο ερμηνεύει με μεγάλη βεβαιότητα τις τάσεις του dataset.
Κάθε μία από αυτές τις μεταβλητές προσθέτει μοναδική και στατιστικά
επιβεβαιωμένη πληροφορία στο μοντέλο.
Διεξαγωγή προβλέψεων με την
predict()
Εφόσον έχουμε δημιουργήσει και εκπαιδεύσει το μοντέλο μας βάσει του Train dataset, είμαστε σε θέση να δοκιμάσουμε την αξιοπιστία του κάνοντας προβλέψεις στο Test dataset:
# Πρόβλεψη πιθανοτήτων στο test set
predictTest <- predict(movement_model, newdata = test_set, type = "response")
# Δες τις πρώτες προβλέψεις για να καταλάβεις τι εννοώ
summary(predictTest)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0007467 0.0672617 0.1646763 0.2347553 0.3525760 0.9224910
Οι προβλέψεις, όπως μπορεί να φανεί και στον κώδικα, γίνονται με την εντολή predict. Σημασία έχει να αναφέρουμε ότι η πρόβλεψή μας και ως εκ τούτου η Predict, δεν μας δίνει μία απόφαση θετική η αρνητική, αλλά προσπαθεί να εκτιμήσει τη πιθανότητα να έχουμε επιτυχία (εν προκειμένω επιτυχία είναι η αποχώρηση αφού Left==1 σημαίνει ότι ο υπάλληλος έφυγε).
Confusion Matrix & Metrics
Σε επόμενο επίπεδο, θα επιχειρήσουμε να δημιουργήσουμε ένα confusion matrix, προκειμένου να αποτυπώσουμε την επίδοση του μοντέλου μας ανά πιθανή έκβαση (αποχώρηση-1 ή παραμονή-0)
# 1. Μετατροπή πιθανοτήτων σε κλάσεις (Threshold = 0.5)
predictClass <- ifelse(predictTest > 0.5, 1, 0)
# 2. Δημιουργία και εμφάνιση Confusion Matrix σε πίνακα
conf_matrix <- table(Actual = test_set$left, Predicted = predictClass)
conf_matrix_df <- as.data.frame.matrix(conf_matrix)
# Ονοματοδοσία για σαφήνεια
rownames(conf_matrix_df) <- c("Πραγματικά Έμειναν (0)", "Πραγματικά Έφυγαν (1)")
colnames(conf_matrix_df) <- c("Πρόβλεψη: Έμειναν (0)", "Πρόβλεψη: Έφυγαν (1)")
knitr::kable(conf_matrix_df,
caption = "Confusion Matrix (Κατώφλι 0.5)",
align = "c")| Πρόβλεψη: Έμειναν (0) | Πρόβλεψη: Έφυγαν (1) | |
|---|---|---|
| Πραγματικά Έμειναν (0) | 3703 | 297 |
| Πραγματικά Έφυγαν (1) | 842 | 408 |
Στη συνέχεια θα εξάγουμε και τις τιμές των βασικών μετρικών που μπορούμε να χρησιμοπιήσουμε για να αξιολογήσουμε το μοντέλο μας πριν εξάγουμε τα συμπεράσματά μας.
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
sensitivity <- conf_matrix[2,2] / sum(conf_matrix[2,])
specificity <- conf_matrix[1,1] / sum(conf_matrix[1,])
baseline_accuracy <- max(table(test_set$left)) / nrow(test_set)
metrics_summary <- data.frame(
Μετρική = c("Accuracy (Ακρίβεια)", "Sensitivity (Ευαισθησία)",
"Specificity (Εξειδίκευση)", "Baseline Accuracy"),
Τιμή = c(accuracy, sensitivity, specificity, baseline_accuracy)
)
knitr::kable(metrics_summary, digits = 4,
caption = "Αξιολόγηση Επιδόσεων Μοντέλου",
align = "lc")| Μετρική | Τιμή |
|---|---|
| Accuracy (Ακρίβεια) | 0.7830 |
| Sensitivity (Ευαισθησία) | 0.3264 |
| Specificity (Εξειδίκευση) | 0.9258 |
| Baseline Accuracy | 0.7619 |
Το μοντέλο λογιστικής παλινδρόμησης κρίνεται μερικώς αξιόπιστο καθώς υπερβαίνει ελάχιστα την ακρίβεια του baseline μοντέλου. Ωστόσο, παρουσιάζει υψηλή συντηρητικότητα. Ενώ είναι εξαιρετικά ακριβές στο να επιβεβαιώνει ποιοι υπάλληλοι θα παραμείνουν (High Specificity), δυσκολεύεται να εντοπίσει το σύνολο των υπαλλήλων που βρίσκονται σε κίνδυνο αποχώρησης (Low Sensitivity). Για μια επιχείρηση που θέλει να προλάβει τις παραιτήσεις, το μοντέλο αυτό λειτουργεί ως ένας δείκτης σίγουρων περιπτώσεων, αλλά πιθανώς χρειάζεται μείωση του κατωφλιού (threshold) για να γίνει πιο αποτελεσματικό στον εντοπισμό περισσότερων επίφοβων υπαλλήλων.
Στη συνέχεια, προκειμένου να διασφαλιστεί ότι δουλεύουμε σε έγκυρα δεδομένα χωρίς εγγραφές με κενά κελιά, θα κάνουμε έναν εκ νέου διαχωρισμό των δεδομένων σε train και test, ώστε να διαπιστώσουμε εάν προηγουμένως είχαμε συμπεριλάβει ελλιπή δεδομένα στην παλινδρόμηση.
hr_data2 <- na.omit(hr_data)
set.seed(914)
split2 <- sample.split(hr_data2$left, SplitRatio = 0.65)
train2 <- subset(hr_data2, split2 == TRUE)
test2 <- subset(hr_data2, split2 == FALSE)
split_summary <- data.frame(
Dataset = c("Training Set", "Test Set", "Total"),
Records = c(nrow(train2), nrow(test2), nrow(hr_data2)),
Percentage = c("65%", "35%", "100%")
)
knitr::kable(split_summary,
caption = "Σύνοψη Διαχωρισμού Δεδομένων (Train/Test Split)",
align = "clc")| Dataset | Records | Percentage |
|---|---|---|
| Training Set | 9749 | 65% |
| Test Set | 5250 | 35% |
| Total | 14999 | 100% |
Παρατηρούμε ότι η εικόνα των δεδομέων είναι πανομοιότυπη με προηγουμένως. Επομένως είμαστε έτοιμοι να προχωρήσουμε εκ νέου στη δημιουργία ενός μοντέλου “ROCpred” το οποίο θα εκπαιδεύσουμε και ελέγξουμε όπως προηγουμένως. Αυτή τη φορά, θα προχωρήσουμε και στην απεικόνιηση της καμπύλης ROC του μοντέλου, ενώ θα διερευνήσουμε και την τιμή της μετρικής AUC ώστε να αξιολογήσουμε το παραχθέν μοντέλο.
library(ROCR)
logistic_model2 <- glm(left ~ satisfaction_level + last_evaluation + number_project + average_montly_hours + time_spend_company + Work_accident + promotion_last_5years + Department + salary,data = train2, family = "binomial")
predictTest2 <- predict(logistic_model2, newdata = test2, type = "response")
ROCRpred <- prediction(predictTest2, test2$left)
ROCRperf <- performance(ROCRpred, "tpr", "fpr")
plot(ROCRperf, colorize = TRUE,
print.cutoffs.at = seq(0, 1, by = 0.1),
text.adj = c(-0.2, 1.7),
main = "ROC Curve for Employee Churn")
abline(a = 0, b = 1, lty = 2) auc_val <- performance(ROCRpred, measure = "auc")
auc_value <- auc_val@y.values[[1]]
# Δημιουργία πίνακα για την AUC
auc_display <- data.frame(
Δείκτης = "AUC (Area Under the Curve)",
Τιμή = round(auc_value, 4),
Ερμηνεία = ifelse(auc_value > 0.8, "Πολύ Καλή Προβλεπτική Ικανότητα", "Ικανοποιητική Προβλεπτική Ικανότητα")
)
# Εμφάνιση όμορφου πίνακα
knitr::kable(auc_display, align = "clc", caption = "Συνολική Αξιολόγηση Μοντέλου")| Δείκτης | Τιμή | Ερμηνεία |
|---|---|---|
| AUC (Area Under the Curve) | 0.813 | Πολύ Καλή Προβλεπτική Ικανότητα |
Η καμπύλη ROC και η τιμή AUC (0.822) επιβεβαιώνουν την υψηλή προβλεπτική ισχύ του μοντέλου. Με την AUC να υπερβαίνει σημαντικά το 0.5 (τυχαία πρόβλεψη), αποδεικνύεται ότι η λογιστική παλινδρόμηση διαχωρίζει αποτελεσματικά τους υπαλλήλους που προτίθενται να αποχωρήσουν από εκείνους που θα παραμείνουν. Η ανοδική πορεία της καμπύλης προς την πάνω αριστερή γωνία υποδηλώνει υψηλή αναλογία True Positives έναντι False Positives.