Projekt Grupa E

Raport projektu “Analityka HR”

Celem niniejszego projektu jest zrozumienie i przewidywanie czynników wpływających na decyzję pracowników o odejściu z pracy. Odejście kwalifikowanych pracowników może być kosztowne dla organizacji, powodując straty zarówno wiedzy, jak i doświadczenia, a także pociągając za sobą konieczność ponoszenia dodatkowych kosztów związanych z rekrutacją i szkoleniem nowych osób. Przez dogłębne zbadanie różnorodnych danych, takich jak wiek pracowników, ich miesięczny dochód, zawód, stan cywilny, poziom satysfakcji z obowiązków zawodowych oraz innych istotnych informacji, można wyszukać wzorców i korelacji, które mogą przyczyniać się do rotacji pracowniczej. Przyjrzenie się tym danym i przeanalizowanie występujących zależności między nimi pomoże nam w odpowiedzi na postawione pytanie.

Czyszczenie danych

Pracę nad projektem rozpoczynamy od przyjrzenia się naszemu zbiorowi danych.

summary(HR)
##       Age         Attrition         BusinessTravel       DailyRate     
##  Min.   :18.00   Length:1470        Length:1470        Min.   : 102.0  
##  1st Qu.:30.00   Class :character   Class :character   1st Qu.: 465.0  
##  Median :36.00   Mode  :character   Mode  :character   Median : 802.0  
##  Mean   :36.92                                         Mean   : 802.5  
##  3rd Qu.:43.00                                         3rd Qu.:1157.0  
##  Max.   :60.00                                         Max.   :1499.0  
##   Department        DistanceFromHome   Education     EducationField    
##  Length:1470        Min.   : 1.000   Min.   :1.000   Length:1470       
##  Class :character   1st Qu.: 2.000   1st Qu.:2.000   Class :character  
##  Mode  :character   Median : 7.000   Median :3.000   Mode  :character  
##                     Mean   : 9.193   Mean   :2.913                     
##                     3rd Qu.:14.000   3rd Qu.:4.000                     
##                     Max.   :29.000   Max.   :5.000                     
##  EmployeeCount EmployeeNumber   EnvironmentSatisfaction    Gender         
##  Min.   :1     Min.   :   1.0   Min.   :1.000           Length:1470       
##  1st Qu.:1     1st Qu.: 491.2   1st Qu.:2.000           Class :character  
##  Median :1     Median :1020.5   Median :3.000           Mode  :character  
##  Mean   :1     Mean   :1024.9   Mean   :2.722                             
##  3rd Qu.:1     3rd Qu.:1555.8   3rd Qu.:4.000                             
##  Max.   :1     Max.   :2068.0   Max.   :4.000                             
##    HourlyRate     JobInvolvement    JobLevel       JobRole         
##  Min.   : 30.00   Min.   :1.00   Min.   :1.000   Length:1470       
##  1st Qu.: 48.00   1st Qu.:2.00   1st Qu.:1.000   Class :character  
##  Median : 66.00   Median :3.00   Median :2.000   Mode  :character  
##  Mean   : 65.89   Mean   :2.73   Mean   :2.064                     
##  3rd Qu.: 83.75   3rd Qu.:3.00   3rd Qu.:3.000                     
##  Max.   :100.00   Max.   :4.00   Max.   :5.000                     
##  JobSatisfaction MaritalStatus      MonthlyIncome    MonthlyRate   
##  Min.   :1.000   Length:1470        Min.   : 1009   Min.   : 2094  
##  1st Qu.:2.000   Class :character   1st Qu.: 2911   1st Qu.: 8047  
##  Median :3.000   Mode  :character   Median : 4919   Median :14236  
##  Mean   :2.729                      Mean   : 6503   Mean   :14313  
##  3rd Qu.:4.000                      3rd Qu.: 8379   3rd Qu.:20462  
##  Max.   :4.000                      Max.   :19999   Max.   :26999  
##  NumCompaniesWorked    Over18            OverTime         PercentSalaryHike
##  Min.   :0.000      Length:1470        Length:1470        Min.   :11.00    
##  1st Qu.:1.000      Class :character   Class :character   1st Qu.:12.00    
##  Median :2.000      Mode  :character   Mode  :character   Median :14.00    
##  Mean   :2.693                                            Mean   :15.21    
##  3rd Qu.:4.000                                            3rd Qu.:18.00    
##  Max.   :9.000                                            Max.   :25.00    
##  PerformanceRating RelationshipSatisfaction StandardHours StockOptionLevel
##  Min.   :3.000     Min.   :1.000            Min.   :80    Min.   :0.0000  
##  1st Qu.:3.000     1st Qu.:2.000            1st Qu.:80    1st Qu.:0.0000  
##  Median :3.000     Median :3.000            Median :80    Median :1.0000  
##  Mean   :3.154     Mean   :2.712            Mean   :80    Mean   :0.7939  
##  3rd Qu.:3.000     3rd Qu.:4.000            3rd Qu.:80    3rd Qu.:1.0000  
##  Max.   :4.000     Max.   :4.000            Max.   :80    Max.   :3.0000  
##  TotalWorkingYears TrainingTimesLastYear WorkLifeBalance YearsAtCompany  
##  Min.   : 0.00     Min.   :0.000         Min.   :1.000   Min.   : 0.000  
##  1st Qu.: 6.00     1st Qu.:2.000         1st Qu.:2.000   1st Qu.: 3.000  
##  Median :10.00     Median :3.000         Median :3.000   Median : 5.000  
##  Mean   :11.28     Mean   :2.799         Mean   :2.761   Mean   : 7.008  
##  3rd Qu.:15.00     3rd Qu.:3.000         3rd Qu.:3.000   3rd Qu.: 9.000  
##  Max.   :40.00     Max.   :6.000         Max.   :4.000   Max.   :40.000  
##  YearsInCurrentRole YearsSinceLastPromotion YearsWithCurrManager
##  Min.   : 0.000     Min.   : 0.000          Min.   : 0.000      
##  1st Qu.: 2.000     1st Qu.: 0.000          1st Qu.: 2.000      
##  Median : 3.000     Median : 1.000          Median : 3.000      
##  Mean   : 4.229     Mean   : 2.188          Mean   : 4.123      
##  3rd Qu.: 7.000     3rd Qu.: 3.000          3rd Qu.: 7.000      
##  Max.   :18.000     Max.   :15.000          Max.   :17.000
str(HR)
## tibble [1,470 × 35] (S3: tbl_df/tbl/data.frame)
##  $ Age                     : num [1:1470] 41 49 37 33 27 32 59 30 38 36 ...
##  $ Attrition               : chr [1:1470] "Yes" "No" "Yes" "No" ...
##  $ BusinessTravel          : chr [1:1470] "Travel_Rarely" "Travel_Frequently" "Travel_Rarely" "Travel_Frequently" ...
##  $ DailyRate               : num [1:1470] 1102 279 1373 1392 591 ...
##  $ Department              : chr [1:1470] "Sales" "Research & Development" "Research & Development" "Research & Development" ...
##  $ DistanceFromHome        : num [1:1470] 1 8 2 3 2 2 3 24 23 27 ...
##  $ Education               : num [1:1470] 2 1 2 4 1 2 3 1 3 3 ...
##  $ EducationField          : chr [1:1470] "Life Sciences" "Life Sciences" "Other" "Life Sciences" ...
##  $ EmployeeCount           : num [1:1470] 1 1 1 1 1 1 1 1 1 1 ...
##  $ EmployeeNumber          : num [1:1470] 1 2 4 5 7 8 10 11 12 13 ...
##  $ EnvironmentSatisfaction : num [1:1470] 2 3 4 4 1 4 3 4 4 3 ...
##  $ Gender                  : chr [1:1470] "Female" "Male" "Male" "Female" ...
##  $ HourlyRate              : num [1:1470] 94 61 92 56 40 79 81 67 44 94 ...
##  $ JobInvolvement          : num [1:1470] 3 2 2 3 3 3 4 3 2 3 ...
##  $ JobLevel                : num [1:1470] 2 2 1 1 1 1 1 1 3 2 ...
##  $ JobRole                 : chr [1:1470] "Sales Executive" "Research Scientist" "Laboratory Technician" "Research Scientist" ...
##  $ JobSatisfaction         : num [1:1470] 4 2 3 3 2 4 1 3 3 3 ...
##  $ MaritalStatus           : chr [1:1470] "Single" "Married" "Single" "Married" ...
##  $ MonthlyIncome           : num [1:1470] 5993 5130 2090 2909 3468 ...
##  $ MonthlyRate             : num [1:1470] 19479 24907 2396 23159 16632 ...
##  $ NumCompaniesWorked      : num [1:1470] 8 1 6 1 9 0 4 1 0 6 ...
##  $ Over18                  : chr [1:1470] "Y" "Y" "Y" "Y" ...
##  $ OverTime                : chr [1:1470] "Yes" "No" "Yes" "Yes" ...
##  $ PercentSalaryHike       : num [1:1470] 11 23 15 11 12 13 20 22 21 13 ...
##  $ PerformanceRating       : num [1:1470] 3 4 3 3 3 3 4 4 4 3 ...
##  $ RelationshipSatisfaction: num [1:1470] 1 4 2 3 4 3 1 2 2 2 ...
##  $ StandardHours           : num [1:1470] 80 80 80 80 80 80 80 80 80 80 ...
##  $ StockOptionLevel        : num [1:1470] 0 1 0 0 1 0 3 1 0 2 ...
##  $ TotalWorkingYears       : num [1:1470] 8 10 7 8 6 8 12 1 10 17 ...
##  $ TrainingTimesLastYear   : num [1:1470] 0 3 3 3 3 2 3 2 2 3 ...
##  $ WorkLifeBalance         : num [1:1470] 1 3 3 3 3 2 2 3 3 2 ...
##  $ YearsAtCompany          : num [1:1470] 6 10 0 8 2 7 1 1 9 7 ...
##  $ YearsInCurrentRole      : num [1:1470] 4 7 0 7 2 7 0 0 7 7 ...
##  $ YearsSinceLastPromotion : num [1:1470] 0 1 0 3 2 3 0 0 1 7 ...
##  $ YearsWithCurrManager    : num [1:1470] 5 7 0 0 2 6 0 0 8 7 ...
data_class <- data.frame(class = sapply(HR, class))
data_class
##                              class
## Age                        numeric
## Attrition                character
## BusinessTravel           character
## DailyRate                  numeric
## Department               character
## DistanceFromHome           numeric
## Education                  numeric
## EducationField           character
## EmployeeCount              numeric
## EmployeeNumber             numeric
## EnvironmentSatisfaction    numeric
## Gender                   character
## HourlyRate                 numeric
## JobInvolvement             numeric
## JobLevel                   numeric
## JobRole                  character
## JobSatisfaction            numeric
## MaritalStatus            character
## MonthlyIncome              numeric
## MonthlyRate                numeric
## NumCompaniesWorked         numeric
## Over18                   character
## OverTime                 character
## PercentSalaryHike          numeric
## PerformanceRating          numeric
## RelationshipSatisfaction   numeric
## StandardHours              numeric
## StockOptionLevel           numeric
## TotalWorkingYears          numeric
## TrainingTimesLastYear      numeric
## WorkLifeBalance            numeric
## YearsAtCompany             numeric
## YearsInCurrentRole         numeric
## YearsSinceLastPromotion    numeric
## YearsWithCurrManager       numeric

Nie wszystkie zmienne w naszym zbiorze danych przydadzą się do niniejszej analizy. Usunięcie zbędnych kolumn nie jest jednak konieczne. W przyszłości możemy chcieć przeprowadzić bardziej złożone analizy, które wymagają wielowymiarowego podejścia. Zmienne, które obecnie wydają się zbędne, mogą okazać się kluczowe w dalszych analizach wieloczynnikowych.

Puste wartości

W pierwszej kolejności sprawdzamy czy w zbiorze danych występują puste wartości.

is.null(HR)
## [1] FALSE
miss_var_summary(HR)
## # A tibble: 35 × 3
##    variable         n_miss pct_miss
##    <chr>             <int>    <dbl>
##  1 Age                   0        0
##  2 Attrition             0        0
##  3 BusinessTravel        0        0
##  4 DailyRate             0        0
##  5 Department            0        0
##  6 DistanceFromHome      0        0
##  7 Education             0        0
##  8 EducationField        0        0
##  9 EmployeeCount         0        0
## 10 EmployeeNumber        0        0
## # ℹ 25 more rows
n_miss(HR)
## [1] 0

W naszej bazie danych nie występują żadne puste wartości.

Wartości mniejsze od zera

W następnym kroku sprawdzamy czy w naszych danych ilościowych są jakieś wartości ujemne (oczekujemy, że takich nie będzie, ponieważ, przykładowo, ujemny dystans od domu do miejsca pracy byłby nielogiczny).

rules <- validator(HR$DailyRate >= 0
  , HR$DistanceFromHome >= 0
  , HR$Education >= 0
  , HR$EmployeeCount >= 0
  , HR$EmployeeNumber >= 0
  , HR$HourlyRate >0
  , HR$JobInvolvement >= 0 
  , HR$JobLevel >= 0
  , HR$JobSatisfaction >= 0
  , HR$MonthlyIncome >= 0
  , HR$MonthlyRate >= 0
  , HR$NumCompaniesWorked >= 0 
  , HR$PercentSalaryHike >= 0
  , HR$PerformanceRating >= 0
  , HR$RelationshipSatisfaction >= 0
  , HR$StandardHours >= 0
  , HR$StockOptionLevel >= 0 
  , HR$TotalWorkingYears >= 0
  , HR$TrainingTimesLastYear >=0
  , HR$WorkLifeBalance >= 0
  , HR$YearsAtCompany >= 0
  , HR$YearsInCurrentRole >= 0
  , HR$YearsSinceLastPromotion >= 0
  , HR$YearsWithCurrManager >= 0
)

cf <- confront(HR, rules)
summary(cf)
##    name items passes fails nNA error warning
## 1   V01  1470   1470     0   0 FALSE   FALSE
## 2   V02  1470   1470     0   0 FALSE   FALSE
## 3   V03  1470   1470     0   0 FALSE   FALSE
## 4   V04  1470   1470     0   0 FALSE   FALSE
## 5   V05  1470   1470     0   0 FALSE   FALSE
## 6   V06  1470   1470     0   0 FALSE   FALSE
## 7   V07  1470   1470     0   0 FALSE   FALSE
## 8   V08  1470   1470     0   0 FALSE   FALSE
## 9   V09  1470   1470     0   0 FALSE   FALSE
## 10  V10  1470   1470     0   0 FALSE   FALSE
## 11  V11  1470   1470     0   0 FALSE   FALSE
## 12  V12  1470   1470     0   0 FALSE   FALSE
## 13  V13  1470   1470     0   0 FALSE   FALSE
## 14  V14  1470   1470     0   0 FALSE   FALSE
## 15  V15  1470   1470     0   0 FALSE   FALSE
## 16  V16  1470   1470     0   0 FALSE   FALSE
## 17  V17  1470   1470     0   0 FALSE   FALSE
## 18  V18  1470   1470     0   0 FALSE   FALSE
## 19  V19  1470   1470     0   0 FALSE   FALSE
## 20  V20  1470   1470     0   0 FALSE   FALSE
## 21  V21  1470   1470     0   0 FALSE   FALSE
## 22  V22  1470   1470     0   0 FALSE   FALSE
## 23  V23  1470   1470     0   0 FALSE   FALSE
## 24  V24  1470   1470     0   0 FALSE   FALSE
##                               expression
## 1                 HR[["DailyRate"]] >= 0
## 2          HR[["DistanceFromHome"]] >= 0
## 3                 HR[["Education"]] >= 0
## 4             HR[["EmployeeCount"]] >= 0
## 5            HR[["EmployeeNumber"]] >= 0
## 6                 HR[["HourlyRate"]] > 0
## 7            HR[["JobInvolvement"]] >= 0
## 8                  HR[["JobLevel"]] >= 0
## 9           HR[["JobSatisfaction"]] >= 0
## 10            HR[["MonthlyIncome"]] >= 0
## 11              HR[["MonthlyRate"]] >= 0
## 12       HR[["NumCompaniesWorked"]] >= 0
## 13        HR[["PercentSalaryHike"]] >= 0
## 14        HR[["PerformanceRating"]] >= 0
## 15 HR[["RelationshipSatisfaction"]] >= 0
## 16            HR[["StandardHours"]] >= 0
## 17         HR[["StockOptionLevel"]] >= 0
## 18        HR[["TotalWorkingYears"]] >= 0
## 19    HR[["TrainingTimesLastYear"]] >= 0
## 20          HR[["WorkLifeBalance"]] >= 0
## 21           HR[["YearsAtCompany"]] >= 0
## 22       HR[["YearsInCurrentRole"]] >= 0
## 23  HR[["YearsSinceLastPromotion"]] >= 0
## 24     HR[["YearsWithCurrManager"]] >= 0
plot(cf)

Nie ma wartości mniejszych od zera, zatem nie musimy poprawiać ich wartości w tym zakresie.

Wartości odstające

Następnie spojrzymy na dane pod względem wartości odstających.

boxplot(HR$DailyRate)

boxplot(HR$DistanceFromHome)

boxplot(HR$Education)

boxplot(HR$MonthlyIncome)

boxplot(HR$EnvironmentSatisfaction)

boxplot(HR$HourlyRate)

boxplot(HR$JobInvolvement)

boxplot(HR$MonthlyRate)

Jak możemy zauważyć, wartości odstające zaczynają się pojawiać w miesięcznych dochodach pracowników.

Aby pozbyć się obserwacji odstających, posłużymy się powszechną metodą wykorzystującą rozstęp międzykwartylowy (IQR). Za wartość odstającą często uważa się dowolną wartość, która jest większa niż 1,5 * IQR powyżej trzeciego kwartyla lub poniżej pierwszego kwartyla.

replace_outliers <- function(x) {
  Q1 <- quantile(x, 0.25)
  Q3 <- quantile(x, 0.75)
  IQR <- Q3 - Q1
  x[x < (Q1 - 1.5 * IQR) | x > (Q3 + 1.5 * IQR)] <- median(x, na.rm = TRUE)
  return(x)
}

HR <- HR %>%
  mutate_if(is.numeric, replace_outliers)
head(HR)
## # A tibble: 6 × 35
##     Age Attrition BusinessTravel DailyRate Department DistanceFromHome Education
##   <dbl> <chr>     <chr>              <dbl> <chr>                 <dbl>     <dbl>
## 1    41 Yes       Travel_Rarely       1102 Sales                     1         2
## 2    49 No        Travel_Freque…       279 Research …                8         1
## 3    37 Yes       Travel_Rarely       1373 Research …                2         2
## 4    33 No        Travel_Freque…      1392 Research …                3         4
## 5    27 No        Travel_Rarely        591 Research …                2         1
## 6    32 No        Travel_Freque…      1005 Research …                2         2
## # ℹ 28 more variables: EducationField <chr>, EmployeeCount <dbl>,
## #   EmployeeNumber <dbl>, EnvironmentSatisfaction <dbl>, Gender <chr>,
## #   HourlyRate <dbl>, JobInvolvement <dbl>, JobLevel <dbl>, JobRole <chr>,
## #   JobSatisfaction <dbl>, MaritalStatus <chr>, MonthlyIncome <dbl>,
## #   MonthlyRate <dbl>, NumCompaniesWorked <dbl>, Over18 <chr>, OverTime <chr>,
## #   PercentSalaryHike <dbl>, PerformanceRating <dbl>,
## #   RelationshipSatisfaction <dbl>, StandardHours <dbl>, …

Podsumowując pierwszy etap, posłużymy się wykresem, który upewni nas, że nie mamy żadnych wartości brakujących.

VIM::aggr(HR[3:7])

Możemy zatem przejść do kolejnego etapu projektu.

Wizualizacja danych

ggplot(HR, aes(y=Age,x=Attrition)) +
  geom_boxplot() +
  coord_flip()+
  labs(x = "Rezygnacja z pracy", y = "Wiek", title="Rezygnacja z pracy ze względu na wiek")

Na powyższym wykresie możemy zauważyć, że z pracy częściej rezygnują młodsi pracownicy.

ggplot(HR, aes(x=MonthlyIncome,size=Attrition,y=Age,color=Gender)) +
  geom_point() +
  labs(x="Miesięczny dochód", y="Wiek", color="Płeć", size="Rezygnacja z pracy", title="Rezygnacja z pracy ze względu na wiek, płeć i miesięczne dochody")
## Warning: Using size for a discrete variable is not advised.

Możemy zaobserwować, że na rezygnację z pracy częściej decydują się młodsze osoby z niższymi dochodami.

HR %>%
  filter(Attrition=="Yes") %>%
  ggplot(aes(x = YearsAtCompany)) +
  geom_histogram(
    aes(fill = OverTime)
    ,bins = 10
    ,color = 'black'
  ) +
  labs(x="Liczba przepracowanych lat w firmie", fill="Nadgodziny", y="Liczba pracowników", filter="Rezygnacja z pracy",title = 'Staż pracy i nadgodziny pracowników, którzy opuścili firmę')

Duża część pracowników decyduje się na odejście z pracy w początkowych latach pracy w firmie,a wśród nich spora część pracowników pracuje ponad wymiar godzin.

HR %>%
  ggplot(aes(x = PercentSalaryHike)) +
  geom_histogram(
    aes(fill = Attrition)
    ,bins = 50
    ,color = 'black'
  ) + labs(x="Procentowa podwyżka wynagrodzeń", fill="Rezygnacja z pracy", 
           y="Liczba pracowników", title="Rezygnacja z pracy ze względu na procentową podwyżkę wynagrodzeń")

Osoby z niższą procentową podwyżką wynagrodzeń są bardziej skłonne do odejścia z pracy.

ggplot(HR, aes(y=DailyRate,x=Attrition)) +
  geom_boxplot() +
  coord_flip() +
  labs(y="Dzienna stawka", x="Rezygnacja z pracy", title="Rezygnacja z pracy ze względu na dzienną stawkę")

Osoby z niższą dzienną stawką częściej rezygnują z pracy.

ggplot(HR, aes(y=DistanceFromHome,x=Attrition)) +
  geom_boxplot() +
  coord_flip() +
  labs(y="Odległość od domu", x="Rezygnacja z pracy", title="Rezygnacja z pracy ze względu na odległość miejsca pracy od domu")

Jak możemy zauważyć, odległość między miejscem pracy a domem ma również znaczenie. Pracownicy, którzy muszą pokonać więcej kilometrów w drodze do pracy, częściej decydują się na odejście z pracy.

HR %>%
  ggplot(aes(x = JobLevel)) +
  geom_bar(aes(fill = Attrition), color = 'black') +
  scale_x_discrete(breaks = 1:5, labels = c("b.niski", "niski", "średni", "wysoki", "b.wysoki")) +
  labs(x = "Stopień stanowiska", y = "Liczba pracowników", fill="Rezygnacja z pracy")

Im niższy poziom pracy, tym pracownicy coraz częściej decydują się na rezygnację z pracy.

HR %>%
  ggplot(aes(x = BusinessTravel)) +
  geom_bar(
    aes(fill = Attrition),
    color = 'black'
  ) +
  labs(x="Podróże służbowe",fill="Odejście z pracy", y="Liczba pracowników") +
  scale_x_discrete(labels = c("Non-Travel" = "Nie podróżuje", 
                              "Travel_Frequently" = "Często podróżuje",
                              "Travel_Rarely" = "Rzadko podróżuje"))

Pracownicy, którzy rzadko podróżują, najczęściej decydują się na odejście z pracy.

HR %>%
  ggplot(aes(x = EducationField)) +
  geom_bar(
    aes(fill = Attrition),
    color = 'black'
  ) +
  labs(x="Dziedzina edukacji", fill="Rezygnacja z pracy", y="Liczba pracowników") + 
  scale_x_discrete(labels = c("Human Resources" = "Zasoby ludzkie(HR)", 
                              "Life Sciences" = "Nauki przyrodnicze",
                              "Marketing" = "Marketing",
                              "Medical" = "Medycyna",
                              "Other"="Inne",
                              "Technical Degree"="Techniczny stopień naukowy"))

Analizując dziedzinę edukacji pracowników, na rezygnację z pracy najczęściej decydują się pracownicy z dziedziny nauk przyrodniczych i medycznych.

HR %>%
  ggplot(aes(x = MaritalStatus)) +
  geom_bar(
    aes(fill = Attrition),
    color = 'black'
  ) + labs(x="Stan cywilny", fill="Rezygnacja z pracy") + 
  scale_x_discrete(labels = c("Divorced" = "Rozwiedziony", 
                              "Married" = "Żonaty",
                              "Single" = "Wolny"
                              ))

Biorąc pod uwagę życie prywatne pracowników, osoby niebędące w związku małżeńskim częściej odchodzą z pracy.

Podsumowując, na decyzję o odejściu z pracy wpływają różne czynniki. Na podstawie dokonanej analizy w niniejszym etapie, możemy powiedzieć, że najczęściej rezygnują młodsi pracownicy, zwłaszcza ci z niższymi zarobkami. Wysoka rotacja jest obserwowana w początkowych latach pracy, zwłaszcza wśród osób pracujących ponad normę godzinową. Niższa procentowa podwyżka wynagrodzeń i niższa dzienna stawka również sprzyjają odejściu z pracy. To pokazuje, że pracownicy nie czują się docenieni przez swoich przełożonych i nie widzą przyszłości w tej firmie. Długa droga do pracy, niższy stopień stanowiska, rzadkie podróże służbowe, specjalizacja w dziedzinie nauk przyrodniczych i medycznych, a także brak związku małżeńskiego to determinanty sprzyjające rezygnacji z pracy.

Statystyki opisowe

Przed rozpoczęciem tego etapu, należy w pierwszej kolejności dobrać właściwe formaty zmiennych.

HR$Attrition<-as.factor(HR$Attrition)
HR$JobLevel<-factor(HR$JobLevel)
HR$JobInvolvement<-factor(HR$JobInvolvement)

Poniżej utworzono tabelę przedstawiającą statystyki opisowe miesięcznych dochodów pracowników w zależności od ich zaangażowania w pracę, które jest podzielone na cztery kategorie: “bardzo małe”, “małe”, “duże” i “bardzo duże”.

HR %>%
  select(MonthlyIncome, JobInvolvement) %>%
  mutate(JobInvolvement = factor(JobInvolvement, 
                                 levels = c(1, 2, 3, 4),
                                 labels = c("bardzo małe", "małe", "duże", "bardzo duże")),
         Zaangażowanie_w_pracy = JobInvolvement) %>%
  select(-JobInvolvement) %>%
  group_by(Zaangażowanie_w_pracy) %>%
  dplyr::summarize(Min=min(MonthlyIncome),
                   Max=max(MonthlyIncome),
                   średnia=mean(MonthlyIncome),
                   odchylenie=sd(MonthlyIncome),
                   mediana=median(MonthlyIncome),
                   Q1=quantile(MonthlyIncome, 0.25),
                   Q3=quantile(MonthlyIncome, 0.75),
                   Skośność=skewness(MonthlyIncome),
                   Kurtoza=kurtosis(MonthlyIncome)) %>%
  kbl() %>%
  kable_paper("striped", full_width = F) %>%
  column_spec(1:2, bold = T) %>%
  row_spec(c(1,3), bold = T, color = "white", background = "#C8A2C8") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), 
                full_width = F, 
                position = "center", 
                fixed_thead = T) %>%
  add_header_above(c(" " = 1, "Miesięczne dochody pracowników ze względu na zaangażowanie w pracy" = 9))
Miesięczne dochody pracowników ze względu na zaangażowanie w pracy
Zaangażowanie_w_pracy Min Max średnia odchylenie mediana Q1 Q3 Skośność Kurtoza
bardzo małe 1118 14411 5394.892 2935.034 4919.0 2877.50 6407.0 0.9939833 0.6018108
małe 1102 16124 5673.024 3285.346 4919.0 2905.00 7101.0 1.0303064 0.3045292
duże 1009 16555 5375.486 3160.500 4815.5 2929.75 6500.5 1.3705526 1.5908040
bardzo duże 1091 16184 5426.604 3284.560 4888.0 2910.00 6380.0 1.4873744 1.7236479

Na podstawie tej tabeli możemy zauważyć, że:

  • Kategoria z “bardzo małym” zaangażowaniem ma największy zakres dochodów (Min-Max), a także najwyższą wartość średnich dochodów.
  • Skośność w każdej kategorii jest dodatnia, co wskazuje na to, że rozkład dochodów jest prawostronnie skośny - większość pracowników zarabia mniej niż średnia, a mniejsza liczba pracowników zarabia znacznie więcej.
  • Kurtoza w każdej kategorii jest dodatnia, co sugeruje, że rozkład dochodów ma bardziej wyostrzony szczyt niż rozkład normalny, wskazując na większą koncentrację wartości wokół średniej.

W ostatniej części tego etapu utworzono macierz korelacji między (prawie) wszystkimi zmiennymi. W celu jej wykonania zakodowano zmienne jakościowe na zmienne ilościowe, przypisując im etykiety liczbowe.

qualitative_columns <- HR %>% select_if(is.character)
qualitative_columns <- qualitative_columns %>% 
  select(-Over18)
qualitative_columns <- qualitative_columns %>%
  mutate(BusinessTravel = case_when(
    BusinessTravel == "Non-Travel" ~ 0,
    BusinessTravel == "Travel_Rarely" ~ 1,
    BusinessTravel == "Travel_Frequently" ~ 2)
  )
qualitative_columns <- qualitative_columns %>%
  mutate(Department = case_when(
      Department == "Human Resources" ~ 0,
      Department == "Sales" ~ 1,
      Department == "Research & Development" ~ 2)
  )
qualitative_columns <- qualitative_columns %>%
  mutate(EducationField = case_when(
    EducationField == "Human Resources" ~ 0,
    EducationField == "Marketing" ~ 1,
    EducationField == "Life Sciences" ~ 2,
    EducationField == "Technical Degree" ~ 3,
    EducationField == "Medical" ~ 4,
    EducationField == "Other" ~ 5)
    )
qualitative_columns <- qualitative_columns %>%
  mutate(Gender = case_when(
    Gender == "Male" ~ 0,
    Gender == "Female" ~ 1)
  )
qualitative_columns <- qualitative_columns %>%
  mutate(JobRole = case_when(
    JobRole == "Human Resources" ~ 0,
    JobRole == "Healthcare Representative" ~ 1,
    JobRole == "Research Scientist" ~ 2,
    JobRole == "Research Director" ~ 3,
    JobRole == "Laboratory Technician" ~ 4,
    JobRole == "Manager" ~ 5,
    JobRole == "Manufacturing Director" ~ 6,
    JobRole == "Sales Representative" ~ 7,
    JobRole == "Sales Executive" ~ 8)
  )
qualitative_columns <- qualitative_columns %>%
  mutate(MaritalStatus = case_when(
    MaritalStatus == "Single" ~ 0,
    MaritalStatus == "Married" ~ 1,
    MaritalStatus == "Divorced" ~ 2)
  )
qualitative_columns <- qualitative_columns %>%
  mutate(OverTime = case_when(
    OverTime == "No" ~ 0,
    OverTime == "Yes" ~ 1)
  )
factor_columns <- HR %>% select_if(is.factor)
factor_columns <- factor_columns %>%
  mutate(Attrition = case_when(
    Attrition == "No" ~ 0,
    Attrition == "Yes" ~ 1)
  )
factor_columns$JobInvolvement <- as.numeric(factor_columns$JobInvolvement)
factor_columns$JobLevel <- as.numeric(factor_columns$JobLevel)
numeric_data <- HR %>% select_if(is.numeric)
combined_data <- bind_cols(factor_columns, qualitative_columns, numeric_data)

combined_data <- combined_data %>% 
  select(-EmployeeCount) %>%
  select(-StandardHours) %>%
  select(-PerformanceRating)
cor_matrix <- cor(combined_data, use="complete.obs")
corrplot(cor_matrix, method="color", title="Macierz korelacji")

Z powyższej macierzy korelacji możemy odczytać, że występuje silny związek między miesięcznym dochodem i stopniem stanowiska oraz między łączną liczbą przepracowanych lat a stopniem stanowiska. Możemy również zauważyć występującą zależność pomiędzy oceną wydajności pracownika a podwyżką wynagrodzeń za wyniki pracy.Performance Rating ~ Performance Salary Hike Całkiem mocne korelacje obserwujemy w prawym dolnym rogu macierzy, czyli m.in. między liczbą przepracowanych lat na obecnym stanowisku a liczbą przepracowanych lat w firmie, czy też między liczbą lat od ostatniego awansu a liczbą przepracowanych lat w firmie.

Wnioskowanie statystyczne

Czy miesięczny dochód pracownika zależy od tego jak często pracownik wyjeżdża służbowo?

W wyniku wcześniejszej wizualizacji, można było zauważyć, że najwięcej pracowników, którzy rzadko podróżują, najczęściej decydują się na odejście z pracy, w porównaniu do osób często podróżujących służbowo i niepodróżujących. Podróże służbowe mogą być postrzegane jako przywilej lub obciążenie, w zależności od kontekstu pracy i preferencji indywidualnych. Zrozumienie, jak podróże służbowe wpływają na dochód, może pomóc w ocenie, czy są one motywującym czynnikiem dla pracowników. W celu sprawdzenia założonej hipotezy, utworzono wykres zawierających szczegóły z testów statystycznych, przedstawiający analizę dochodów pracowników w zależności od ich częstotliwości podróży służbowych.

ggbetweenstats(HR, BusinessTravel, MonthlyIncome)+ggtitle("Miesięczny dochód pracownika w zależności od częstotliwości wyjazdów służbowych")+labs(x="Podróże służbowe", y="Miesięczny dochód")

Na podstawie uzyskanego p-value odrzucamy założoną hipotezę. Obserwujemy brak statystycznie istotnych różnic w miesięcznym dochodzie ze względu na częstotliwość wyjazdów służbowych.

Czy procentowa podwyżka wynagrodzenia pracownika zależy od odejścia z pracy (job level)?

Wcześniej obserwowaliśmy również zależność, że osoby z niższą procentową podwyżką wynagrodzenia częściej odchodzą z pracy. Poznając zależność między poziomem stanowiska a podwyżkami wynagrodzenia, firma może lepiej zrozumieć, jakie czynniki wpływają na decyzje pracowników o pozostaniu w firmie lub odejściu. To z kolei może pomóc w opracowaniu strategii zatrzymywania kluczowych talentów. Aby sprawdzić założoną hipotezę, tworzymy poniższy wykres przedstawiający analizę statystyczną związków między odejściem z pracy a procentową podwyżką wynagrodzenia.

ggbetweenstats(HR, Attrition, PercentSalaryHike)+ggtitle("Procentowa podwyżka wynagrodzeń a odejście z pracy")+labs(x="Rezygnacja z pracy", y="Procentowa podwyżka wynagrodzeń")

Wyniki testu wskazują, że nie ma statystycznie istotnej różnicy w procentowych podwyżkach wynagrodzenia między pracownikami, którzy odeszli, a tymi, którzy pozostali w pracy.

Czy odejście z firmy zależy od wykonywanego zawodu w firmie?

Z uwagi na sporą ilość wariantów zmiennej JobRole, we wcześniejszym etapie nie analizowano zawodów firmy. Jest to dobry moment, aby sprawdzić, czy wykonywanie danego zawodu w firmie wpływa na decyzję o odejściu z pracy. Poznanie wzorców rotacji w kontekście różnych zawodów może pomóc firmie zidentyfikować specyficzne role lub działania, które mają wysoki wskaźnik odejść. To z kolei może skłonić do bliższego przyjrzenia się warunkom pracy lub satysfakcji pracowniczej w tych obszarach.

ggbarstats(HR, Attrition, JobRole)+ggtitle("Odejście z pracy w zależności od wykonywanego zawodu")+labs(x="Zawód", y="Odsetek", fill="Rezygnacja z pracy")

Wykres ilustruje, że istnieje różnica w odsetku odejść z pracy między różnymi zawodami w firmie. Przyjmując poziom istotności na poziomie 0.10, obserwujemy, że Przedstawiciele handlowi (ang. Sales Representative) najczęściej decydują się na rezygnację z pracy, a najmniej osób na stanowisku Dyrektora badań odchodzi z firmy.

Czy rezygnacja z pracy zależy od miesięcznego dochodu?

W poprzednim etapie wizualizacji można było zauważyć, że na rezygnację z pracy częściej decydują się młodsze osoby z niższymi zarobkami. Wynagrodzenie jest podstawowym motywatorem dla wielu pracowników. Zrozumienie, jak poziom wynagrodzenia wpływa na decyzje pracowników o pozostaniu w firmie lub odejściu, może dostarczyć cennych informacji do tworzenia polityk wynagradzania.

ggbetweenstats(HR, Attrition, MonthlyIncome)+ggtitle("Miesięczne dochody pracowników a odejście z pracy")+labs(x="Rezygnacja z pracy", y="Miesięczny dochód")

Rozkłady wynagrodzeń pracowników, którzy odeszli, są węższe i skoncentrowane wokół niższych dochodów w porównaniu do tych, którzy pozostali w pracy. Wyniki testów pokazują, że wynagrodzenie ma istotny wpływ na decyzję o odejściu z pracy.

Podsumowanie

Przeprowadzone wnioskowanie statystyczne wskazuje na kilka kluczowych wniosków dotyczących wpływu różnych czynników na decyzje o odejściu z pracy. Przede wszystkim, na podstawie uzyskanego p-value, odrzuciliśmy założoną hipotezę o wpływie częstotliwości wyjazdów służbowych na miesięczny dochód pracownika. To oznacza, że podróżowanie służbowe nie miało znaczącego wpływu na dochody pracowników.

Po drugie, analiza wykazała, że nie istnieje statystycznie istotna różnica w procentowych podwyżkach wynagrodzenia między pracownikami, którzy zdecydowali się odejść, a tymi, którzy pozostali w firmie. Ta obserwacja sugeruje, że podwyżki wynagrodzenia nie były kluczowym czynnikiem wpływającym na decyzję o odejściu.

Jednakże, analiza wykresów wykazała wyraźne różnice w odsetku odejść z pracy między różnymi grupami zawodowymi w firmie. Zauważono, że Przedstawiciele handlowi najczęściej decydowali się na rezygnację z pracy, podczas gdy najmniej osób na stanowisku Dyrektora badań opuszczało firmę. Ten trend był wyraźny przy założonym poziomie istotności na poziomie 0.10.

Dodatkowo, rozkłady wynagrodzeń pracowników, którzy odeszli z pracy, były bardziej skoncentrowane wokół niższych dochodów i charakteryzowały się mniejszą zmiennością w porównaniu do tych, którzy pozostali. To sugeruje, że pracownicy o niższych dochodach byli bardziej skłonni do rezygnacji z pracy, co podkreśla znaczenie wynagrodzenia jako czynnika wpływającego na decyzje o odejściu.

Na podstawie analizy można przedstawić następujące rekomendacje dla firmy, aby zmniejszyć rotację pracowników:

Rewizja Polityki Wynagrodzeń: Z uwagi na istotność wynagrodzenia w decyzjach o odejściu, firma powinna przeprowadzić analizę rynku, aby upewnić się, że wynagrodzenia są konkurencyjne, szczególnie dla stanowisk o najwyższej rotacji. Może to obejmować wprowadzenie elastycznych pakietów wynagrodzeń lub premii dla stanowisk o wysokiej rotacji.

Programy Rozwoju Zawodowego: Rozwój zawodowy może zwiększyć zaangażowanie pracowników i zmniejszyć rotację. Firma powinna oferować szkolenia, warsztaty i możliwości awansu, szczególnie dla grup zawodowych o wyższym odsetku odejść.

Zrównoważone Wyjazdy Służbowe: Chociaż częstotliwość podróży służbowych nie wykazała bezpośredniego wpływu na dochody, warto rozważyć wprowadzenie bardziej zrównoważonych polityk dotyczących podróży służbowych, aby uniknąć wypalenia zawodowego.

Ankiety Satysfakcji Pracowników: Regularne ankiety i spotkania z pracownikami mogą pomóc w identyfikacji i adresowaniu indywidualnych niepokojów i oczekiwań, zanim przerodzą się one w decyzję o odejściu.

Zarządzanie Przez Cele: Ustalenie jasnych, osiągalnych celów dla pracowników może zwiększyć ich motywację i zaangażowanie w pracę.

Implementacja tych rekomendacji może pomóc firmie w zmniejszeniu rotacji pracowników, zwiększeniu ich satysfakcji oraz poprawie ogólnej wydajności i atmosfery w miejscu pracy.