Email          : naufal3433@gmail.com
RPubs         : https://www.rpubs.com/muhammad_naufal/
Address  Â
  : Jalan Gunung Galunggung 5 Blok E9, No.21
Employee Turn-over atau diketahui sebagai Employee Churn adalah masalah yang cukup berisiko bagi perusahaan, dikarenakan biaya untuk mengganti karyawan seringkali bisa sangat besar. Berdasarkan studi di Center for American Progress, ditemukan bahwa perusahaan biasanya membayar 1/5 dari gaji karyawan untuk menggantikan karyawan itu, dan biayakan dapat meningkat jika eksekutif atau karyawan dengan bayaran tertinggi harus diganti. Dengan kata lain, biaya mengganti karyawan untuk sebagian besar pemberi kerja tetap signifikan. Hal ini disebabkan banyaknya waktu yang dihabiskan untuk wawancara dan mencari pengganti, bonus, dan hilangnya produktivitas selama beberapa bulan dimana karyawan baru harus membiasakan diri dengan pekerjaannya.
Mengetahui hal itu, perlu tindakan untuk meningkatkan retensi karyawan serta merencanakan perekrutan karyawan baru telebih dahulu. Salah satunya dapat dengan menggunakan Survival Analysis. Project ini biasanya disebut dengan HR Analytics atau People Analytics.
Variabel-variabel yang terdapat pada data ini adalah: Attrition, BusinessTravel, Department, Education, EducationField, EnvironmentSatisfaction, Gender, JobInvolvement, JobLevel, JobRole, JobSatisfaction, MaritalStatus, OverTime, PerformanceRating, RelationshipSatisfaction, WorkLifeBalance, Age, DistanceFromHome, MonthlyIncome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsInCurrentRole, YearsSinceLastPromotion, YearsWithCurrManager. Dengan beberapa variabel kategorikal yang dituliskan dalam bentuk numerik, yaitu:
Dapat kita ketahui, ada berbagai faktor yang menyebabkan terjadinya employee turnover. Salah satunya adalah Overtime. Pada projek ini, ada 2 hal yang akan dicari, yaitu: * Apakah Overtime memengaruhi secara signifikan terjadinya employee turnover? * Berapa peluang karyawan yang overtime dan tidak overtime dapat bertahan hingga 10 tahun kedepan?
Data yang digunakan adalah data dari IBM HR Analytics Employee Attrition & Performance yang diambil dari kaggle yang berisikan data 1470 karyawan dengan berbagai informasi didalamnya dengan total 35 kolom dengan 237 karyawan turnover dan 1233 yang masih bertahan.
library(DT)
<- read.csv("data_HR.csv")
data datatable(data)
str(data)
## 'data.frame': 1470 obs. of 35 variables:
## $ Age : int 41 49 37 33 27 32 59 30 38 36 ...
## $ Attrition : chr "Yes" "No" "Yes" "No" ...
## $ BusinessTravel : chr "Travel_Rarely" "Travel_Frequently" "Travel_Rarely" "Travel_Frequently" ...
## $ DailyRate : int 1102 279 1373 1392 591 1005 1324 1358 216 1299 ...
## $ Department : chr "Sales" "Research & Development" "Research & Development" "Research & Development" ...
## $ DistanceFromHome : int 1 8 2 3 2 2 3 24 23 27 ...
## $ Education : int 2 1 2 4 1 2 3 1 3 3 ...
## $ EducationField : chr "Life Sciences" "Life Sciences" "Other" "Life Sciences" ...
## $ EmployeeCount : int 1 1 1 1 1 1 1 1 1 1 ...
## $ EmployeeNumber : int 1 2 4 5 7 8 10 11 12 13 ...
## $ EnvironmentSatisfaction : int 2 3 4 4 1 4 3 4 4 3 ...
## $ Gender : chr "Female" "Male" "Male" "Female" ...
## $ HourlyRate : int 94 61 92 56 40 79 81 67 44 94 ...
## $ JobInvolvement : int 3 2 2 3 3 3 4 3 2 3 ...
## $ JobLevel : int 2 2 1 1 1 1 1 1 3 2 ...
## $ JobRole : chr "Sales Executive" "Research Scientist" "Laboratory Technician" "Research Scientist" ...
## $ JobSatisfaction : int 4 2 3 3 2 4 1 3 3 3 ...
## $ MaritalStatus : chr "Single" "Married" "Single" "Married" ...
## $ MonthlyIncome : int 5993 5130 2090 2909 3468 3068 2670 2693 9526 5237 ...
## $ MonthlyRate : int 19479 24907 2396 23159 16632 11864 9964 13335 8787 16577 ...
## $ NumCompaniesWorked : int 8 1 6 1 9 0 4 1 0 6 ...
## $ Over18 : chr "Y" "Y" "Y" "Y" ...
## $ OverTime : chr "Yes" "No" "Yes" "Yes" ...
## $ PercentSalaryHike : int 11 23 15 11 12 13 20 22 21 13 ...
## $ PerformanceRating : int 3 4 3 3 3 3 4 4 4 3 ...
## $ RelationshipSatisfaction: int 1 4 2 3 4 3 1 2 2 2 ...
## $ StandardHours : int 80 80 80 80 80 80 80 80 80 80 ...
## $ StockOptionLevel : int 0 1 0 0 1 0 3 1 0 2 ...
## $ TotalWorkingYears : int 8 10 7 8 6 8 12 1 10 17 ...
## $ TrainingTimesLastYear : int 0 3 3 3 3 2 3 2 2 3 ...
## $ WorkLifeBalance : int 1 3 3 3 3 2 2 3 3 2 ...
## $ YearsAtCompany : int 6 10 0 8 2 7 1 1 9 7 ...
## $ YearsInCurrentRole : int 4 7 0 7 2 7 0 0 7 7 ...
## $ YearsSinceLastPromotion : int 0 1 0 3 2 3 0 0 1 7 ...
## $ YearsWithCurrManager : int 5 7 0 0 2 6 0 0 8 7 ...
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
<- count(subset(data, Attrition == "Yes" ))
Yes <- count(subset(data, Attrition == "No"))
No <- data.frame(Yes, No)
attr names(attr) <- c('yes', 'no')
attr
## yes no
## 1 237 1233
Sebagai permulaan, akan dilakukan eksplorasi data untuk membersihkan data dan melihat visualisasi setiap variable yang ada sehingga hasil prediksi dapat lebih baik.
Sebelum dilakukan pembersihan, tipe data Character akan diubah terlebih dahulu menjadi Character.
<- data %>%
data mutate_if(is.character, as.factor) %>%
select(Attrition, everything())
data.frame(colSums(is.na(data)))
## colSums.is.na.data..
## Attrition 0
## Age 0
## BusinessTravel 0
## DailyRate 0
## Department 0
## DistanceFromHome 0
## Education 0
## EducationField 0
## EmployeeCount 0
## EmployeeNumber 0
## EnvironmentSatisfaction 0
## Gender 0
## HourlyRate 0
## JobInvolvement 0
## JobLevel 0
## JobRole 0
## JobSatisfaction 0
## MaritalStatus 0
## MonthlyIncome 0
## MonthlyRate 0
## NumCompaniesWorked 0
## Over18 0
## OverTime 0
## PercentSalaryHike 0
## PerformanceRating 0
## RelationshipSatisfaction 0
## StandardHours 0
## StockOptionLevel 0
## TotalWorkingYears 0
## TrainingTimesLastYear 0
## WorkLifeBalance 0
## YearsAtCompany 0
## YearsInCurrentRole 0
## YearsSinceLastPromotion 0
## YearsWithCurrManager 0
Dari hasil di atas, dapat dilihat tidak ada data yang hilang. Maka selanjutkan akan melakukan pengecekan data duplikat.
<- data.frame(
check.duplicate row_of_data = data %>% nrow(),
row_of_unique.data = data %>% distinct() %>% nrow())
check.duplicate
## row_of_data row_of_unique.data
## 1 1470 1470
Dapat dilihat jumlah row of data dan row of unique data sama-sama 1470. Oleh karena itu, dapat dibilang tidak ada data yang duplikat.
Dalam hal ini ada beberapa kolom yang tidak digunakan seperti employeecount, standardhour, dan over18. Untuk mengurasi jumlah kolom, maka dapat dihapus.
<- data [, c(-9, -22, -27)] data
Outlier dapat mengganggu hasil dari penelitian, oleh karena itu harus diatasi.
library(DT)
<- boxplot.stats(data$YearsWithCurrManager)$out
outlier <- which(data$YearsWithCurrManager %in% c(outlier))
out_idx <- data [-out_idx,]
data datatable(data)
Pada variabel YearsWithCurrManager terdapat 14 data yang outlier, oleh karena itu untuk membantu penelitian maka dihapuskan. Sehingga data akhir menjadi 1456 dengan 32 kolom.
library(ggplot2)
<- ggplot(data, aes(x = MonthlyIncome, fill = Attrition)) +
plot1 geom_density(alpha = 0.6) +
labs(x = "Monthly Income", y = "") +
ggtitle("Attrition by Income Level") +
theme_classic()
print(plot1)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan Department
<- data %>%
dept group_by(Department, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'Department'. You can override using the
## `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap departemen
<- data %>%
dept_totals group_by(Department) %>%
summarise(totals = n())
# Gabungkan 'totals' ke dalam data 'dept'
<- left_join(dept, dept_totals, by = "Department")
dept
# Membuat variabel 'attr_perc' untuk menyimpan persentase attrition di setiap departemen
<- dept %>%
dept mutate(attr_perc = n / totals * 100)
# Filter hanya data dengan Attrition == "Yes"
<- dept %>%
plot2 filter(Attrition == "Yes") %>%
ggplot(aes(x = reorder(Department, attr_perc), y = attr_perc, fill = Department)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "", y = "% of employees") +
ggtitle("Attrition % by Department") +
theme_classic()
# Tampilkan plot
print(plot2)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan BusinessTravel
<- data %>%
trav_attr group_by(BusinessTravel, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'BusinessTravel'. You can override using
## the `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap BusinessTravel
<- trav_attr %>%
trav_attr mutate(totals = ifelse(BusinessTravel == "Non-Travel",
sum(n),
ifelse(BusinessTravel == "Travel_Frequently",
sum(n),
ifelse(BusinessTravel == "Travel_Rarely",
sum(n), n))))
# Membuat variabel 'attr_perc' untuk menyimpan persentase attrition di setiap BusinessTravel
<- trav_attr %>%
trav_attr mutate(attr_perc = n / totals * 100)
# Filter hanya data dengan Attrition == "Yes"
<- trav_attr %>%
plot3 filter(Attrition == "Yes") %>%
ggplot(aes(x = reorder(BusinessTravel, attr_perc), y = attr_perc, fill = BusinessTravel)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "", y = "% of employees") +
ggtitle("Attrition % by Amount of Business Travel") +
theme_classic()
# Tampilkan plot
print(plot3)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan BusinessTravel
<- data %>%
edu_attr group_by(Education, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'Education'. You can override using the
## `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap BusinessTravel
<- data %>%
edu_tots group_by(Education) %>%
summarise(num = n())
<- left_join(edu_attr, edu_tots, by = "Education")
edu_attr
<- edu_attr %>% mutate(attr_perc = n / num * 100)
edu_attr
# Filter hanya data dengan Attrition == "Yes"
<- edu_attr %>%
plot4 filter(Attrition == "Yes") %>%
ggplot(aes(x = reorder(Education, attr_perc), y = attr_perc, fill = Education)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "Education Level", y = "% of employees") +
ggtitle("Attrition % by Education Level") +
theme_classic()
# Tampilkan plot
print(plot4)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan BusinessTravel
<- data %>%
mar_attr group_by(MaritalStatus, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'MaritalStatus'. You can override using the
## `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap BusinessTravel
<- data %>%
mar_tots group_by(MaritalStatus) %>%
summarise(num = n())
<- left_join(mar_attr, mar_tots, by = "MaritalStatus")
mar_attr
<- mar_attr %>%
mar_attr mutate(attr_perc = n / num * 100)
# Filter hanya data dengan Attrition == "Yes"
<- mar_attr %>%
plot5 filter(Attrition == "Yes") %>%
ggplot(aes(x = reorder(MaritalStatus, attr_perc), y = attr_perc, fill = MaritalStatus)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "", y = "% of employees") +
ggtitle("Attrition % by Marital Status") +
theme_classic()
# Tampilkan plot
print(plot5)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan Gender dan MaritalStatus
<- data %>%
gend_attr group_by(Gender, MaritalStatus, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'Gender', 'MaritalStatus'. You can override
## using the `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap Gender dan MaritalStatus
<- data %>%
gend_tots group_by(Gender, MaritalStatus) %>%
summarise(num = n())
## `summarise()` has grouped output by 'Gender'. You can override using the
## `.groups` argument.
# Menggabungkan 'gend_attr' dengan 'gend_tots' berdasarkan Gender dan MaritalStatus
<- left_join(gend_attr, gend_tots, by = c("Gender", "MaritalStatus"))
gend_attr
# Menghitung persentase attrition untuk setiap kelompok Gender dan MaritalStatus
<- gend_attr %>%
gend_attr mutate(attr_perc = n / num * 100)
# Membuat variabel 'type' yang menyatukan Gender dan MaritalStatus
$type <- paste(gend_attr$MaritalStatus, gend_attr$Gender, sep = " ")
gend_attr
# Filter hanya data dengan Attrition == "Yes"
<- gend_attr %>%
plot6 filter(Attrition == "Yes") %>%
ggplot(aes(x = reorder(type, attr_perc), y = attr_perc, fill = Gender)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "", y = "% of employees") +
ggtitle("Attrition % by Gender and Marital Status") +
theme_classic()
# Tampilkan plot
print(plot6)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan Gender dan MaritalStatus
<- data %>%
jobinv group_by(JobInvolvement, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'JobInvolvement'. You can override using
## the `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap Gender dan MaritalStatus
<- data %>%
jobtots group_by(JobInvolvement) %>%
summarise(num = n())
# Menggabungkan 'gend_attr' dengan 'gend_tots' berdasarkan Gender dan MaritalStatus
<- left_join(jobinv, jobtots, by = "JobInvolvement")
jobinv
# Menghitung persentase attrition untuk setiap kelompok Gender dan MaritalStatus
<- jobinv %>%
jobinv mutate(attr_perc = n / num * 100)
# Filter hanya data dengan Attrition == "Yes"
<- jobinv %>%
plot7 filter(Attrition == "Yes") %>%
ggplot(aes(x = JobInvolvement, y = attr_perc, fill = JobInvolvement)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "Job Involvement Level", y = "% of employee attrition") +
ggtitle("Attrition % by Job Involvement") +
theme_classic()
# Tampilkan plot
print(plot7)
library(ggplot2)
library(dplyr)
# Attrition berdasarkan Gender dan MaritalStatus
<- data %>%
ovt group_by(OverTime, Attrition) %>%
summarise(n = n())
## `summarise()` has grouped output by 'OverTime'. You can override using the
## `.groups` argument.
# Membuat variabel 'totals' untuk menyimpan jumlah total karyawan di setiap Gender dan MaritalStatus
<- data %>%
ovttos group_by(OverTime) %>%
summarise(num = n())
# Menggabungkan 'gend_attr' dengan 'gend_tots' berdasarkan Gender dan MaritalStatus
<- left_join(ovt, ovttos, by = "OverTime")
ovt
# Menghitung persentase attrition untuk setiap kelompok Gender dan MaritalStatus
<- ovt %>%
ovt mutate(attr_perc = n / num * 100)
# Filter hanya data dengan Attrition == "Yes"
<- ovt %>%
plot8 filter(Attrition == "Yes") %>%
ggplot(aes(x = OverTime, y = attr_perc, fill = OverTime)) +
geom_bar(stat = "identity", alpha = 0.9) +
labs(x = "Overtime", y = "% of employee attrition") +
ggtitle("Attrition % by Overtime (Y/N)") +
theme_classic()
# Tampilkan plot
print(plot8)
Hasil diatas menunjukkan bahwa Income memengaruhi terjadinya turnover (Attrition) dimana semakin besar income bulanannya, semakin rendah kemungkinan terjadinya turnover. Sedangkan untuk Department. Business Travel, Education Level, Gender, dan Marital status tidak menunjukkan pengaruh yang jelas terhadap terjadinya turnover.
Pada Job Involvement, terlihat semakin rendah involvement levelnya, semakin tinggi terjadinya turnover, sedangkan semakin tinggi involvement levelnya, semakin rendah tingkat terjadinya turnover itu sendiri. Perbedaan yang sangat jelas terjadi pada Attrition yang disebabkan oleh Overtime atau lembur. Hasil menunjukkan dari 237 karyawan yang turnover, tingkat yang keluar karena Overtime jauh lebih tinggi dibandingkan yang tidak. Hal ini menunjukkan Overtime memengaruhi terjadinya Employee Turnover. Hal ini akan dibahas secara lebih rincinya di bagian survival analysis.
# attrition by years worked for company
<- data %>%
yrcomp group_by(YearsAtCompany) %>%
filter(Attrition == "Yes") %>%
summarise(n = n())
$Attrition <- "Left"
yrcomp
<- data %>%
yrno group_by(YearsAtCompany) %>%
filter(Attrition == "No") %>%
summarise(n = n())
$Attrition <- "Stayed"
yrno
<- rbind(yrcomp, yrno)
yearscomp
<- ggplot(yearscomp, aes(x = YearsAtCompany, y = n, fill = Attrition)) +
plot9 geom_bar(position = "stack", stat = "identity", alpha = 0.9) +
xlab("Years With Company") +
ylab("# of employees") +
ggtitle("Attrition by years worked for company") +
theme_classic()
# attrition by years of years in current role
<- data %>%
yrscomp group_by(YearsInCurrentRole) %>%
filter(Attrition == "Yes") %>%
summarise(n = n())
$Attrition <- "Left"
yrscomp
<- data %>%
yrsno group_by(YearsInCurrentRole) %>%
filter(Attrition == "No") %>%
summarise(n = n())
$Attrition <- "Stayed"
yrsno
<- rbind(yrscomp, yrsno)
jyearscomp
<- ggplot(jyearscomp, aes(x = YearsInCurrentRole, y = n, fill = Attrition)) +
plot10 geom_bar(position = "stack", stat = "identity", alpha = 0.9) +
xlab("Years In CUrrent Role") +
ylab("# of employees") +
ggtitle("Attrition by years In Current Role") +
theme_classic()
# attrition by number of years with current manager
<- data %>%
mancomp group_by(YearsWithCurrManager) %>%
filter(Attrition == "Yes") %>%
summarise(n = n())
$Attrition <- "Left"
mancomp
<- data %>%
manno group_by(YearsWithCurrManager) %>%
filter(Attrition == "No") %>%
summarise(n = n())
$Attrition <- "Stayed"
manno
<- rbind(mancomp, manno)
managercomp
<- ggplot(managercomp, aes(x = YearsWithCurrManager, y = n, fill = Attrition)) +
plot11 geom_bar(position = "stack", stat = "identity", alpha = 0.9) +
xlab("Years In CUrrent Manager") +
ylab("# of employees") +
ggtitle("Attrition by years With Current Manager") +
theme_classic()
library(ggpubr)
ggarrange(plot9, plot10, plot11, ncol = 2, nrow = 2)
Hasil diatas menunjukkan bahwa Years in Current Role dan Years with Currrent Manager memengaruhi terjadinya attrition. Pada years in current role, dapat dilihat bahwa semakin lama orang itu berkutat di bidangnya atau rolenya, maka kemungkinan terjadinya turnover lebih kecil. Mungkin dikarenakan bila di tahun-tahun awal employee merasa tidak cocok dengan rolenya sehingga mengundurkan diri. Jika yang sdah bertahan lama, orang tersebut sudah nyaman dan merasa itu adalah role yang tepat untuknya.
Pada attrition with current manager di beberapa waktu tertentu tingkat turnovernya lebih tinggi dibandingkan dengan yang bertahan. Tetapi semakin lama orang bersama manager yang sekarang, semakin rendah kemungkinan turnovernya.
Karena salah satu variabel yang paling menunjukkan terjadinya employee turnover adalah Overtime, karena itu kami akan mencari peluang bertahannya karyawan 10 tahun kedepan berdasarkan overtimenya atau kerja lembur. Dengan itu, variabel yang akan digunakan adalah Attrition dengan Yes = 1 dan No = 0, Overtime dengan Yes = 1 dan No = 0 , serta waktunya menggunakan YearsAtCompany.
library(survival)
$event <- with(data,ifelse(Attrition=="Yes", 1,0))
data$Overtimework <- with(data, ifelse(OverTime == "Yes", 1, 0))
data<- data$YearsAtCompany
time <- data$event
event <- data$overtimework
group <- Surv(time,event) survival
Pada tahap pertama akan dihitung dan divisualisasikan Kurva Survivalnya untuk melihat survival data dari waktu kewaktu.
library(survival)
# Asumsikan 'time' adalah waktu survival dan 'event' adalah indikator peristiwa
<- survfit(Surv(time, event) ~ 1)
model
# Mendapatkan tabel dengan kolom-kolom yang diinginkan
<- data.frame(
surv_summary time = model$time,
n.risk = model$n.risk,
n.event = model$n.event,
n.censor = model$n.censor,
surv = model$surv,
std.err = model$std.err,
upper = model$upper,
lower = model$lower
)
# Mencetak tabel
surv_summary
## time n.risk n.event n.censor surv std.err upper lower
## 1 0 1456 16 28 0.9890110 0.002762473 0.9943804 0.9836706
## 2 1 1412 59 112 0.9476855 0.006205986 0.9592830 0.9362281
## 3 2 1241 27 100 0.9270670 0.007512371 0.9408181 0.9135169
## 4 3 1114 20 108 0.9104231 0.008535012 0.9257810 0.8953199
## 5 4 986 19 91 0.8928794 0.009631916 0.9098955 0.8761816
## 6 5 876 21 175 0.8714748 0.010991449 0.8904525 0.8529015
## 7 6 680 9 67 0.8599406 0.011854816 0.8801552 0.8401902
## 8 7 604 11 79 0.8442794 0.013086183 0.8662139 0.8229003
## 9 8 514 9 71 0.8294963 0.014349944 0.8531574 0.8064914
## 10 9 434 8 74 0.8142060 0.015785794 0.8397910 0.7894005
## 11 10 352 18 102 0.7725705 0.020057272 0.8035463 0.7427888
## 12 11 232 2 30 0.7659104 0.020970823 0.7980468 0.7350681
## 13 12 200 0 14 0.7659104 0.020970823 0.7980468 0.7350681
## 14 13 186 2 22 0.7576748 0.022320707 0.7915571 0.7252428
## 15 14 162 2 16 0.7483208 0.023986964 0.7843421 0.7139537
## 16 15 144 1 19 0.7431241 0.024978731 0.7804108 0.7076189
## 17 16 124 1 11 0.7371312 0.026258373 0.7760612 0.7001541
## 18 17 112 1 6 0.7305497 0.027747788 0.7713806 0.6918800
## 19 18 105 1 10 0.7235920 0.029351573 0.7664395 0.6831399
## 20 19 94 1 9 0.7158943 0.031239480 0.7610969 0.6733762
## 21 20 84 1 22 0.7073717 0.033456479 0.7553111 0.6624750
## 22 21 61 1 12 0.6957754 0.037317021 0.7485717 0.6467029
## 23 22 48 1 12 0.6812801 0.042846499 0.7409633 0.6264043
## 24 23 35 1 1 0.6618150 0.051731601 0.7324376 0.5980019
## 25 24 33 1 4 0.6417600 0.060192427 0.7221186 0.5703438
## 26 25 28 0 4 0.6417600 0.060192427 0.7221186 0.5703438
## 27 26 24 0 4 0.6417600 0.060192427 0.7221186 0.5703438
## 28 27 20 0 2 0.6417600 0.060192427 0.7221186 0.5703438
## 29 29 18 0 2 0.6417600 0.060192427 0.7221186 0.5703438
## 30 30 16 0 1 0.6417600 0.060192427 0.7221186 0.5703438
## 31 31 15 1 2 0.5989760 0.091569826 0.7167270 0.5005702
## 32 32 12 1 2 0.5490613 0.126336023 0.7033286 0.4286309
## 33 33 9 1 4 0.4880545 0.172770598 0.6847482 0.3478610
## 34 36 4 0 2 0.4880545 0.172770598 0.6847482 0.3478610
## 35 37 2 0 1 0.4880545 0.172770598 0.6847482 0.3478610
## 36 40 1 1 0 0.0000000 Inf NA NA
plot(model, xlab="Time elapsed", ylab = "% surviving", main="Survival Plot")
Pada hasil di atas dapat dilihat bahwa peluang survivenya semakin turun dan berakhir pada tahun ke-40 dimana penurunan paling sering terjadi di tahun pertama hingga tahun ke-10.
Pada bagian ini akan dilihat model survivalnya jika dikelompoknya pada yang kerja lembur (overtime) dan yang tidak lembur.
# Memperbarui model survival dengan variabel 'Overtime'
<- survfit(Surv(time, event) ~ OverTime, data = data)
model_comp
# Plot survival
plot(model_comp, xlab = "Time elapsed", ylab = "% surviving", main = "Survival Plot", legend = TRUE, col = c('blue', 'red'))
## Warning in plot.window(...): "legend" is not a graphical parameter
## Warning in plot.xy(xy, type, ...): "legend" is not a graphical parameter
## Warning in axis(side = side, at = at, labels = labels, ...): "legend" is not a
## graphical parameter
## Warning in axis(side = side, at = at, labels = labels, ...): "legend" is not a
## graphical parameter
## Warning in box(...): "legend" is not a graphical parameter
## Warning in title(...): "legend" is not a graphical parameter
legend('bottomleft', c("Overtime=No", "Overtime=Yes"), col = c('blue', 'red'), lty = 1, bty = "n")
Pada Hasil diatas dapat dilihat untuk Group 0 (Tidak overtime) karyawan ada yang bertahan hingga 40 tahun. Sedangkan untuk Group 1 (Overtime) karyawan paling lama bertahan hingga 31 tahun. Pada grafik juga dapat dilihat peluang bertahannya karyawan yang overtime lebih rendah dibandingkan dengan yang tidak overtime.
library(DT)
library(survival)
# Asumsikan model_comp adalah model survival Anda
# Contoh: model_comp <- survfit(Surv(time, event) ~ group, data = data)
# Mendapatkan informasi yang relevan dari model survival
<- summary(model_comp)
surv_summary
# Menyiapkan data frame dari informasi yang relevan
<- data.frame(Time = surv_summary$time,
sm1 Number_at_risk = surv_summary$n.risk,
Number_of_events = surv_summary$n.event,
Number_censored = surv_summary$n.censor,
Survival_Probability = surv_summary$surv,
Standard_Error = surv_summary$std.err,
Lower_Confidence_Interval = surv_summary$lower,
Upper_Confidence_Interval = surv_summary$upper,
Strata = surv_summary$strata)
# Tampilkan tabel interaktif menggunakan DT
datatable(sm1,
caption = htmltools::tags$caption(
style = 'caption-side: bottom; text-align: center;',
::em('Summary of Survival Curves'))) htmltools
Pada tabel dapat dilihat peluang survive untuk setiap tahunnya beserta batas atas dan bawahnya. Dari sini dapat dilihat peluang karyawan bertahan 10 tahun kedepan adalah 0.8814 atau 88.14%. Hal ini menunjukkan peluang untuk bertahan 10 tahun kedepan untuk karyawan yang tidak overtime lumayan besar.
summary(model_comp)$table
## records n.max n.start events rmean se(rmean) median 0.95LCL
## OverTime=No 1042 1042 1042 110 30.52316 1.351791 33 32
## OverTime=Yes 414 414 414 127 22.58698 1.396738 24 16
## 0.95UCL
## OverTime=No NA
## OverTime=Yes NA
Pada hasil ini dapat dilihat ketika peluangnya 50% (nilai median) untuk group 0 atau tidak lembur peluangnya akan dibawah 50% ketika sudah kerja selama 33 tahun, sedangkan untuk group 1 atau yang lembur, peluangnya akan dibawah 50% jika telah bekerja hinga 24 tahun.
Visualisasi untuk data tersebut beserta batas atas dan bawahnya adalah sebagai berikut.
library(survminer)
##
## Attaching package: 'survminer'
## The following object is masked from 'package:survival':
##
## myeloma
ggsurvplot(model_comp,
data = data,
pval = TRUE,
conf.int = TRUE,
xlab = "Time in Years",
ggtheme = theme_bw(),
risk.table = "abs_pct",
risk.table.y.text.col = TRUE,
risk.table.y.text = FALSE,
ncensor.plot = TRUE,
surv.median.line = "hv",
legend.labs = c("Overtime", "No Overtime"),
palette = c("#2E9FDF", "#965986"))
Dari visualisasi ini dapat dilihat bahwa kebanyakan turnover terjadi di tahun pertama hingga tahun ke-10. dimana jumlah karyawan yang tersisa di tahun ke-10 adalah 257 untuk yang tidak lembur dan 85 untuk yang lembur. Hingga pada tahun ke-40 hanya ada 1 karyawan yang tidak lembur yang masih bertahan di perusahaan.
Untuk mengetahui apakah Overtime memengaruhi survival karyawan, dapat dilihat dari hasil comparenya.
<- survdiff(Surv(time, event) ~ OverTime, data = data)
surv_diff surv_diff
## Call:
## survdiff(formula = Surv(time, event) ~ OverTime, data = data)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## OverTime=No 1042 110 170.5 21.5 78.9
## OverTime=Yes 414 127 66.5 55.1 78.9
##
## Chisq= 78.9 on 1 degrees of freedom, p= <2e-16
Dari hasil tersebut dapat dibilang bahwa Overtime memengaruhi terjadinya employee turnover karena berdasarkan test tersebut dapat dilihat overtime memengaruhi secara signigikan dengan p value lebih kecil dari 0.05. Dari Observed dan Expected juga dapat dilihat untuk group 1 yang lembur, angka observe melebihi ekspektasi yang berarti angka turnovernya tinggi.
data.frame(subset(sm1, time==10))
## Time Number_at_risk Number_of_events Number_censored
## 2 1 1012 31 85
## 16 18 71 1 8
## 27 4 270 8 22
## 32 9 114 2 17
## NA NA NA NA NA
## NA.1 NA NA NA NA
## NA.2 NA NA NA NA
## NA.3 NA NA NA NA
## NA.4 NA NA NA NA
## NA.5 NA NA NA NA
## NA.6 NA NA NA NA
## NA.7 NA NA NA NA
## NA.8 NA NA NA NA
## NA.9 NA NA NA NA
## NA.10 NA NA NA NA
## NA.11 NA NA NA NA
## NA.12 NA NA NA NA
## NA.13 NA NA NA NA
## NA.14 NA NA NA NA
## NA.15 NA NA NA NA
## NA.16 NA NA NA NA
## NA.17 NA NA NA NA
## NA.18 NA NA NA NA
## NA.19 NA NA NA NA
## NA.20 NA NA NA NA
## NA.21 NA NA NA NA
## NA.22 NA NA NA NA
## NA.23 NA NA NA NA
## NA.24 NA NA NA NA
## NA.25 NA NA NA NA
## NA.26 NA NA NA NA
## NA.27 NA NA NA NA
## NA.28 NA NA NA NA
## NA.29 NA NA NA NA
## NA.30 NA NA NA NA
## NA.31 NA NA NA NA
## NA.32 NA NA NA NA
## NA.33 NA NA NA NA
## NA.34 NA NA NA NA
## NA.35 NA NA NA NA
## NA.36 NA NA NA NA
## NA.37 NA NA NA NA
## NA.38 NA NA NA NA
## NA.39 NA NA NA NA
## NA.40 NA NA NA NA
## NA.41 NA NA NA NA
## NA.42 NA NA NA NA
## NA.43 NA NA NA NA
## NA.44 NA NA NA NA
## NA.45 NA NA NA NA
## NA.46 NA NA NA NA
## NA.47 NA NA NA NA
## NA.48 NA NA NA NA
## NA.49 NA NA NA NA
## NA.50 NA NA NA NA
## NA.51 NA NA NA NA
## NA.52 NA NA NA NA
## NA.53 NA NA NA NA
## NA.54 NA NA NA NA
## NA.55 NA NA NA NA
## NA.56 NA NA NA NA
## NA.57 NA NA NA NA
## NA.58 NA NA NA NA
## NA.59 NA NA NA NA
## NA.60 NA NA NA NA
## NA.61 NA NA NA NA
## NA.62 NA NA NA NA
## NA.63 NA NA NA NA
## NA.64 NA NA NA NA
## NA.65 NA NA NA NA
## NA.66 NA NA NA NA
## NA.67 NA NA NA NA
## NA.68 NA NA NA NA
## NA.69 NA NA NA NA
## NA.70 NA NA NA NA
## NA.71 NA NA NA NA
## NA.72 NA NA NA NA
## NA.73 NA NA NA NA
## NA.74 NA NA NA NA
## NA.75 NA NA NA NA
## NA.76 NA NA NA NA
## NA.77 NA NA NA NA
## NA.78 NA NA NA NA
## NA.79 NA NA NA NA
## NA.80 NA NA NA NA
## NA.81 NA NA NA NA
## NA.82 NA NA NA NA
## NA.83 NA NA NA NA
## NA.84 NA NA NA NA
## NA.85 NA NA NA NA
## NA.86 NA NA NA NA
## NA.87 NA NA NA NA
## NA.88 NA NA NA NA
## NA.89 NA NA NA NA
## NA.90 NA NA NA NA
## NA.91 NA NA NA NA
## NA.92 NA NA NA NA
## NA.93 NA NA NA NA
## NA.94 NA NA NA NA
## NA.95 NA NA NA NA
## NA.96 NA NA NA NA
## NA.97 NA NA NA NA
## NA.98 NA NA NA NA
## NA.99 NA NA NA NA
## NA.100 NA NA NA NA
## NA.101 NA NA NA NA
## NA.102 NA NA NA NA
## NA.103 NA NA NA NA
## NA.104 NA NA NA NA
## NA.105 NA NA NA NA
## NA.106 NA NA NA NA
## NA.107 NA NA NA NA
## NA.108 NA NA NA NA
## NA.109 NA NA NA NA
## NA.110 NA NA NA NA
## NA.111 NA NA NA NA
## NA.112 NA NA NA NA
## NA.113 NA NA NA NA
## NA.114 NA NA NA NA
## NA.115 NA NA NA NA
## Survival_Probability Standard_Error Lower_Confidence_Interval
## 2 0.9637858 0.005845325 0.9523970
## 16 0.7995182 0.025475587 0.7511142
## 27 0.8043154 0.020433015 0.7652481
## 32 0.6574766 0.028478792 0.6039629
## NA NA NA NA
## NA.1 NA NA NA
## NA.2 NA NA NA
## NA.3 NA NA NA
## NA.4 NA NA NA
## NA.5 NA NA NA
## NA.6 NA NA NA
## NA.7 NA NA NA
## NA.8 NA NA NA
## NA.9 NA NA NA
## NA.10 NA NA NA
## NA.11 NA NA NA
## NA.12 NA NA NA
## NA.13 NA NA NA
## NA.14 NA NA NA
## NA.15 NA NA NA
## NA.16 NA NA NA
## NA.17 NA NA NA
## NA.18 NA NA NA
## NA.19 NA NA NA
## NA.20 NA NA NA
## NA.21 NA NA NA
## NA.22 NA NA NA
## NA.23 NA NA NA
## NA.24 NA NA NA
## NA.25 NA NA NA
## NA.26 NA NA NA
## NA.27 NA NA NA
## NA.28 NA NA NA
## NA.29 NA NA NA
## NA.30 NA NA NA
## NA.31 NA NA NA
## NA.32 NA NA NA
## NA.33 NA NA NA
## NA.34 NA NA NA
## NA.35 NA NA NA
## NA.36 NA NA NA
## NA.37 NA NA NA
## NA.38 NA NA NA
## NA.39 NA NA NA
## NA.40 NA NA NA
## NA.41 NA NA NA
## NA.42 NA NA NA
## NA.43 NA NA NA
## NA.44 NA NA NA
## NA.45 NA NA NA
## NA.46 NA NA NA
## NA.47 NA NA NA
## NA.48 NA NA NA
## NA.49 NA NA NA
## NA.50 NA NA NA
## NA.51 NA NA NA
## NA.52 NA NA NA
## NA.53 NA NA NA
## NA.54 NA NA NA
## NA.55 NA NA NA
## NA.56 NA NA NA
## NA.57 NA NA NA
## NA.58 NA NA NA
## NA.59 NA NA NA
## NA.60 NA NA NA
## NA.61 NA NA NA
## NA.62 NA NA NA
## NA.63 NA NA NA
## NA.64 NA NA NA
## NA.65 NA NA NA
## NA.66 NA NA NA
## NA.67 NA NA NA
## NA.68 NA NA NA
## NA.69 NA NA NA
## NA.70 NA NA NA
## NA.71 NA NA NA
## NA.72 NA NA NA
## NA.73 NA NA NA
## NA.74 NA NA NA
## NA.75 NA NA NA
## NA.76 NA NA NA
## NA.77 NA NA NA
## NA.78 NA NA NA
## NA.79 NA NA NA
## NA.80 NA NA NA
## NA.81 NA NA NA
## NA.82 NA NA NA
## NA.83 NA NA NA
## NA.84 NA NA NA
## NA.85 NA NA NA
## NA.86 NA NA NA
## NA.87 NA NA NA
## NA.88 NA NA NA
## NA.89 NA NA NA
## NA.90 NA NA NA
## NA.91 NA NA NA
## NA.92 NA NA NA
## NA.93 NA NA NA
## NA.94 NA NA NA
## NA.95 NA NA NA
## NA.96 NA NA NA
## NA.97 NA NA NA
## NA.98 NA NA NA
## NA.99 NA NA NA
## NA.100 NA NA NA
## NA.101 NA NA NA
## NA.102 NA NA NA
## NA.103 NA NA NA
## NA.104 NA NA NA
## NA.105 NA NA NA
## NA.106 NA NA NA
## NA.107 NA NA NA
## NA.108 NA NA NA
## NA.109 NA NA NA
## NA.110 NA NA NA
## NA.111 NA NA NA
## NA.112 NA NA NA
## NA.113 NA NA NA
## NA.114 NA NA NA
## NA.115 NA NA NA
## Upper_Confidence_Interval Strata
## 2 0.9753108 OverTime=No
## 16 0.8510416 OverTime=No
## 27 0.8453771 OverTime=Yes
## 32 0.7157319 OverTime=Yes
## NA NA <NA>
## NA.1 NA <NA>
## NA.2 NA <NA>
## NA.3 NA <NA>
## NA.4 NA <NA>
## NA.5 NA <NA>
## NA.6 NA <NA>
## NA.7 NA <NA>
## NA.8 NA <NA>
## NA.9 NA <NA>
## NA.10 NA <NA>
## NA.11 NA <NA>
## NA.12 NA <NA>
## NA.13 NA <NA>
## NA.14 NA <NA>
## NA.15 NA <NA>
## NA.16 NA <NA>
## NA.17 NA <NA>
## NA.18 NA <NA>
## NA.19 NA <NA>
## NA.20 NA <NA>
## NA.21 NA <NA>
## NA.22 NA <NA>
## NA.23 NA <NA>
## NA.24 NA <NA>
## NA.25 NA <NA>
## NA.26 NA <NA>
## NA.27 NA <NA>
## NA.28 NA <NA>
## NA.29 NA <NA>
## NA.30 NA <NA>
## NA.31 NA <NA>
## NA.32 NA <NA>
## NA.33 NA <NA>
## NA.34 NA <NA>
## NA.35 NA <NA>
## NA.36 NA <NA>
## NA.37 NA <NA>
## NA.38 NA <NA>
## NA.39 NA <NA>
## NA.40 NA <NA>
## NA.41 NA <NA>
## NA.42 NA <NA>
## NA.43 NA <NA>
## NA.44 NA <NA>
## NA.45 NA <NA>
## NA.46 NA <NA>
## NA.47 NA <NA>
## NA.48 NA <NA>
## NA.49 NA <NA>
## NA.50 NA <NA>
## NA.51 NA <NA>
## NA.52 NA <NA>
## NA.53 NA <NA>
## NA.54 NA <NA>
## NA.55 NA <NA>
## NA.56 NA <NA>
## NA.57 NA <NA>
## NA.58 NA <NA>
## NA.59 NA <NA>
## NA.60 NA <NA>
## NA.61 NA <NA>
## NA.62 NA <NA>
## NA.63 NA <NA>
## NA.64 NA <NA>
## NA.65 NA <NA>
## NA.66 NA <NA>
## NA.67 NA <NA>
## NA.68 NA <NA>
## NA.69 NA <NA>
## NA.70 NA <NA>
## NA.71 NA <NA>
## NA.72 NA <NA>
## NA.73 NA <NA>
## NA.74 NA <NA>
## NA.75 NA <NA>
## NA.76 NA <NA>
## NA.77 NA <NA>
## NA.78 NA <NA>
## NA.79 NA <NA>
## NA.80 NA <NA>
## NA.81 NA <NA>
## NA.82 NA <NA>
## NA.83 NA <NA>
## NA.84 NA <NA>
## NA.85 NA <NA>
## NA.86 NA <NA>
## NA.87 NA <NA>
## NA.88 NA <NA>
## NA.89 NA <NA>
## NA.90 NA <NA>
## NA.91 NA <NA>
## NA.92 NA <NA>
## NA.93 NA <NA>
## NA.94 NA <NA>
## NA.95 NA <NA>
## NA.96 NA <NA>
## NA.97 NA <NA>
## NA.98 NA <NA>
## NA.99 NA <NA>
## NA.100 NA <NA>
## NA.101 NA <NA>
## NA.102 NA <NA>
## NA.103 NA <NA>
## NA.104 NA <NA>
## NA.105 NA <NA>
## NA.106 NA <NA>
## NA.107 NA <NA>
## NA.108 NA <NA>
## NA.109 NA <NA>
## NA.110 NA <NA>
## NA.111 NA <NA>
## NA.112 NA <NA>
## NA.113 NA <NA>
## NA.114 NA <NA>
## NA.115 NA <NA>
Data awal terdapat 1470 data dan ketika di lakukan pembersihan tersisa 1456 data. Dari data tersebut, dibuat visualisasi dan didapatkan ada beberapa variabel yang memengaruhi terjadinya Employe Turnover, yaitu: Overtime, MonthlyIncome, BusinessTravel, JobInvolvement, TotalWorkingYears, YearsAtCompany, dan YearsWithCurrManager. Karena pada penelitian ini berfokus pada OverTime, maka data yang digunakan adalah YearsAtCompany sebagai time, Attrition sebagai event, dan Overtime sebagai Group untuk pembanding.
Dari hasil penelitian, didapatkan bahwa Overtime memengaruhi secara signifikan terjadinya Employee Turnover. Peluang bertahannya karyawan 10 tahun kedepan untuk employee yang mengalami Overtime adalah 58.83% dan yang tidak mengalami overtime adalah 88.14%. Jika dilihat secara keseluruhan tanpa memperhitungkan overtime, peluang bertahan employee untuk 10 tahun kedepan adalah 77.3%. Dengan itu, mungkin lebih baik agar jam kerja karyawan diperhatikan agar dapat menurunkan kemungkinan turnover sehingga dapat mengurangi kerugian perusahaan juga.