HR projekt Analiza danych - Projekt dotyczy analizy odejść pracowników (Attrition) w firmie, mającej na celu zrozumienie, jakie czynniki wpływają na decyzję o opuszczeniu miejsca pracy. Skupiono się na identyfikacji różnic między pracownikami, którzy pozostali, a tymi, którzy odeszli.

2025-02-15

Projekt HR

Wstęp

Poniższy projekt przedstawia analizę danych opartą na danych zawierających informacje dotyczące pracowników pewnej firmy. Główną zmienną, której dotyczy analiza to attrition, czyli rezygnowanie z pracy. Analiza opiera się na zbadaniu motywacji, przyczyn, zależności pomiędzy odchodzeniem z pracy a innymi zmiennymi.

Powstawić można hipotezę badawczą - co wpływa na odchodzenie z pracy przez pracowników?

Projekt zawiera się w trzech głównych etapach: 1. Data cleansing, Wrangling, 2. Wizualizacja danych, 3. Analiza opisowa i wnioskowanie statystyczne.

Celem projektu jest wykonanie analizy skupiając się na zmiennej ‘attrition’, opartej na przygotowanym zbiorze danych.

Wczytanie bibliotek

Etap 1. Data cleansing

Wstęp

W procesie analizy danych kluczowym etapem jest ich oczyszczenie, aby zapewnić poprawność i spójność zbioru. W pierwszej kolejności sprawdzono kompletność danych, identyfikując brakujące wartości oraz analizując ich rozkład za pomocą wykresów i podsumowań statystycznych. Następnie zweryfikowano zgodność zmiennych z określonymi regułami, skorygowano błędy oraz uzupełniono braki imputacją, co umożliwiło dalszą rzetelną analizę danych.

W celu wykonania projektu wczytano zestaw danych.

dane <- read.csv(file = "HR.csv")

Kolejne wczytanie danych z nagłówkami i specyficznymi separatorami.

tabela <- read.csv("HR.csv", header= TRUE, sep=",", dec=",")

Następnie wyświetlono dane z tabeli, aby wstępnie zweryfikować ich poprawność, ilość oraz aby zyskać ogólny pogląd na badane wartości.

View(tabela)

Ważnym krokiem analizy jest sprawdzenie liczby brakujących wartości w całym zbiorze danych. Uzyskana wartość to łączna liczba brakujących obserwacji wśród wszystkich zmiennych.

sum(is.na(tabela))
## [1] 400
datatable(tabela, options = list(pageLength = 5, autoWidth = TRUE))

Aby dowiedzieć się szczegółów dotyczących braków zmiennych wykonano wykres brakujących wartości. Przedstawia on wszystkie zmienne występujące w zbiorze danych oraz wskazuje te, które zawierają puste wartości. Jak można odczytać z poniższego wykresu, w zbiorze występują trzy zmienne, które mają braki. Są to kolejno: Age (100), MonthlyIncome (150) oraz Attrition(150).

plot_missing(tabela)

Kolejną metodą sprawdzającą braki danych jest shadowmapa. Pokazuje ona, w których kolumnach znajdują się brakujące dane oraz zwraca ich liczbę.

gg_miss_var(tabela) + labs( title = "Shadowmapa brakujących danych", x =
"Kolumny", y = "Liczba brakujących wartości" )

Poniżej zastosowano bardziej szczegółowy wykres, który pokazuje nie tylko w których zmiennych są braki, ale także wskazuje wiersz, w którym one występują.

vis_miss(tabela)

Podsumowanie braków danych w formie tabeli.

wybrane_kolumny <- dplyr::select(tabela, Attrition, MonthlyIncome, Age)
miss_var_summary(wybrane_kolumny)
## # A tibble: 3 × 3
##   variable      n_miss pct_miss
##   <chr>          <int>    <num>
## 1 Attrition        150    10.2 
## 2 MonthlyIncome    150    10.2 
## 3 Age              100     6.80

Wizualizacja braków danych w formie wykresu słupkowego. Poniżej wyświetlane są zależności pomiędzy brakami danych. Z wykresu wynika, że nie ma wzorca braku danych, są one niezależne. Jest niewiele przypadków (łącznie 34), gdzie brakuje więcej niż jednej zmiennej w wierszu, ale nie można tego uznać za regułę.

gg_miss_upset(tabela)

Kolejnym krokiem w przygotowaniu danych do ich póżniejszej analizy jest sprawdzenie ich spójności. Dane powinny spełniać określone przez analityka reguły, aby nie fałszowały późniejszych obliczeń. W tym projekcie określono pięć reguł, które musiały zostać spełnione przez odpowiednie zmienne. Są to kolejno: wiek pracownika większy lub równy od 18 i mniejszy od 100, odejście pracownika z firmy ma mieć wartość prawda lub fałsz, miesięczny dochód większy od zera oraz lata pracy z firmie większe lub równe zero.

RULE <- editset(c(
  "Age >= 18",
  "Age <100",
  "Attrition %in% c('Yes','No')",
  "MonthlyIncome > 0",
  "YearsAtCompany >= 0"
))
RULE
## 
## Data model:
## dat1 : Attrition %in% c('No', 'Yes') 
## 
## Edit set:
## num1 : 18 <= Age
## num2 : Age < 100
## num3 : 0 < MonthlyIncome
## num4 : 0 <= YearsAtCompany

Wynik analizy przedstawia liczbę naruszeń określonych reguł w zbiorze danych, wskazując, które zmienne nie spełniają założeń dotyczących poprawnych wartości. Wykres dodatkowo wizualizuje skalę tych naruszeń, co pozwala na szybkie zidentyfikowanie obszarów wymagających korekty.

violations <- violatedEdits(RULE, tabela)
summary(violations)
## Edit violations, 1470 observations, 0 completely missing (0%):
## 
##  editname freq   rel
##      dat1  150 10.2%
## 
## Edit violations per record:
## 
##  errors freq   rel
##       0 1105 75.2%
##       1  253 17.2%
##       2   90  6.1%
##       3   21  1.4%
##       4    1  0.1%
summary(violatedEdits(RULE, tabela))
## Edit violations, 1470 observations, 0 completely missing (0%):
## 
##  editname freq   rel
##      dat1  150 10.2%
## 
## Edit violations per record:
## 
##  errors freq   rel
##       0 1105 75.2%
##       1  253 17.2%
##       2   90  6.1%
##       3   21  1.4%
##       4    1  0.1%
plot(violations)

Zidentyfikowane w poprzednim kroku naruszenia reguł zamieniono na NA - puste wartości. W kolejnych krokach dane te zostaną wypełnione wybraną metodą.

for (i in 1:nrow(violations)) {
  for (j in 1:ncol(violations)) {
    if (!is.na(violations[i, j]) && violations[i, j]) {
      dane[i, colnames(violations)[j]] <- NA
    }
  }
}

Funkcja kNN() z pakietu VIM uzupełnia brakujące wartości w zbiorze danych za pomocą metody k najbliższych sąsiadów (k-Nearest Neighbors, k-NN). Wybiera k = 5 najbardziej podobnych obserwacji i na ich podstawie imputuje brakujące wartości. Dzięki temu powstaje nowy zbiór danych (tabela_knn), w którym luki w danych zostały zastąpione przewidywanymi wartościami.

tabela_knn <- kNN(tabela, k = 5)
sum(is.na(tabela_knn))
## [1] 0

Poniższy wykres przedstawia wartości odstające w badanej populacji.

boxplot(tabela$Age)

boxplot(tabela$MonthlyIncome)

Podsumowanie

Proces oczyszczania danych pozwolił na identyfikację brakujących wartości oraz niezgodności w zbiorze, co umożliwiło ich skuteczną korektę. Zastosowane metody wizualizacji i analizy wykazały, że trzy zmienne zawierały braki, które następnie uzupełniono metodą imputacji. Dodatkowo zweryfikowano zgodność danych z określonymi regułami i skorygowano błędne wartości, aby zapewnić ich spójność. Finalnie, oczyszczony zbiór danych jest gotowy do dalszej analizy, eliminując ryzyko błędnych wniosków wynikających z braków i niespójności.

Etap 2. Data wrangling

Wstęp

W celu lepszego zrozumienia struktury zatrudnienia i wynagrodzeń w firmie dokonano filtrowania oraz analizy wybranych zmiennych. Szczególną uwagę poświęcono młodym pracownikom o niskich dochodach i krótkim stażu, a także ogólnej strukturze zatrudnienia w różnych działach i stanowiskach.

Poniżej zastosowano filtrowanie wybranych zmiennych. Wynik zawiera pracowników młodych, o niskich dochodach i krótkim stażu u obecnego menedżera, co może wskazywać na grupę o wyższym ryzyku rotacji lub dopiero rozpoczynającą karierę.

tabela_knn %>%
  filter(Age < 22, MonthlyIncome < 1500, YearsWithCurrManager < 5) %>%
  dplyr::select(Age, BusinessTravel, Department, Education, Gender, MonthlyIncome, YearsWithCurrManager)
##   Age BusinessTravel             Department Education Gender MonthlyIncome
## 1  21  Travel_Rarely Research & Development         2   Male          1232
## 2  19  Travel_Rarely Research & Development         3   Male          1102
## 3  18  Travel_Rarely Research & Development         3   Male          1420
## 4  18     Non-Travel Research & Development         2   Male          1051
## 5  21  Travel_Rarely Research & Development         3 Female          1416
##   YearsWithCurrManager
## 1                    0
## 2                    0
## 3                    0
## 4                    0
## 5                    0

Poniżej dokonano sortowania danych za pomocą jednej lub więcej zmiennych. Dodatkowo użyto funkcji ‘count’, aby zliczyć posortowane obserwacje.

tabela_knn %>%
  count(Department)
##               Department   n
## 1        Human Resources  63
## 2 Research & Development 961
## 3                  Sales 446
tabela_knn %>%
  count(Gender)
##   Gender   n
## 1 Female 588
## 2   Male 882
tabela_knn %>%
  count(JobRole)
##                     JobRole   n
## 1 Healthcare Representative 131
## 2           Human Resources  52
## 3     Laboratory Technician 259
## 4                   Manager 102
## 5    Manufacturing Director 145
## 6         Research Director  80
## 7        Research Scientist 292
## 8           Sales Executive 326
## 9      Sales Representative  83

Zagłębiając się w funkcję sortowania, wykonano podsumowanie libczy osób na danych stanowiskach wraz z podaniem średniego zarobku na tym stanowisku. Uzyskane informacje posortowano malejąco. Z poniższej analizy wynika, że najwięcej osób pracowało na stanowisku dyrektora ds. sprzedaży, natomiast najmniej w dziale zasobów ludzkich. Średnio najlepiej wynagradzanym stanowiskiem był przedstawiciel handlowy, natomiast najlepiej wynagradzanym był manager.

tabela_knn %>%
  group_by(JobRole) %>%
  summarise(
    Liczba_osób = n(),
    Średni_zarobek = mean(MonthlyIncome)
  ) %>%
  arrange(desc(Liczba_osób))
## # A tibble: 9 × 3
##   JobRole                   Liczba_osób Średni_zarobek
##   <chr>                           <int>          <dbl>
## 1 Sales Executive                   326          6902.
## 2 Research Scientist                292          3204.
## 3 Laboratory Technician             259          3218.
## 4 Manufacturing Director            145          7265.
## 5 Healthcare Representative         131          7427.
## 6 Manager                           102         17119.
## 7 Sales Representative               83          2705.
## 8 Research Director                  80         15818.
## 9 Human Resources                    52          4241.

Podsumowanie

Analiza wykazała, że wśród młodych pracowników o niskich dochodach mogą znajdować się osoby o wyższym ryzyku rotacji. Dodatkowo, struktura zatrudnienia wskazuje na dominację mężczyzn w firmie oraz największą liczbę pracowników w dziale badań i rozwoju. Pod względem wynagrodzenia najlepiej opłacanym stanowiskiem był manager, co sugeruje istotne różnice w wynagrodzeniach zależnie od roli w organizacji.

Etap 3. Data visualization

Wstęp

Analiza danych wizualizacyjnych pozwala na lepsze zrozumienie czynników wpływających na satysfakcję i rotację pracowników. W niniejszym etapie przedstawiono różne aspekty związane z zatrudnieniem, takie jak wynagrodzenie, staż pracy, wykształcenie, wiek oraz satysfakcja zawodowa, które mogą wpływać na decyzje o pozostaniu lub odejściu z firmy. Pozwolą one na lepsze zrozumienie co motywuje pracowników do opuszczania firmy.

Wykres 1 - Satysfakcja z pracy

Poniższy wykres przedstawia jak pracownicy z podziałem na płeć oceniali swoją staysfkację z pracy w czteropunktowej skali. Zdecydowanie więcej mężczyzn wskazało dwa najwyższe poziomy zadowolenia w porównaniu do kobiet.

ggplot(tabela_knn, aes(x=JobSatisfaction)) +
  geom_histogram(bins=10) +
  labs(title="Satysfakcja z pracy", x="Ocena od 1 do 4", y="Liczba osób") +
  theme_bw() +
  facet_grid(~Gender)

Wykres 2 - Zależność wieku względem zarobków

Ten punktowy wykres przedstawia zależność wieku pracownika względem jego zarobków. W tym przypadku logiczne założenie, że wraz z wiekiem zarobki powinny rosnąć jak najbardziej się sprawdza. Jak widać linia trendu jest rosnącą funckją liniową, wskazującą na średni wzrost zarobków względem wieku. Naturalnie występują wartości skrajnie odstające, gdzie 60 - latek zarabia mniej niż 20 - latek czy 30 - latek. Dodatkowo wykres podzielono względem płci. Linie trendu praktycznie się na siebie nakładają, co wskazuje na to, że płeć nie ma zbytniego wpływu na zarobki względem wieku - liczy się doświadczenie.

ggplot(tabela_knn, aes(x=Age, y=MonthlyIncome, color=Gender)) +
  geom_point() +
  geom_smooth(method="lm") + 
  theme_light() +
  theme(legend.position = c(0.1, 0.85)) + 
  labs(
    title = "Zależność wieku względem zarobków",
    x = "Wiek",
    y = "Miesięczne zarobki") 
## Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2
## 3.5.0.
## ℹ Please use the `legend.position.inside` argument of `theme()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'

Wykres 3 - Zależność satysfakcji z pracy względem odejść praconików

Na poniższym wykresie przedstawiono jak satysfakcja z pracy wpływa na decyzję pracownika o odejściu. Zdecydowanie widać, iż więcej jest pracowników, którzy zostają w firmie niż tych, którzy z niej odchodzą. Badana populacja wykazuje się bardzo wysoką satysfakcją z pracy - tych, którzy zostali i ocenili swoje zadowolonie najwyższym punktem jest niemalże dwuktornie więcej niż tych najmniej zadowolonych. Natomiast odejście z pracy nie do końca podyktowane jest małą satysfakcją z pracy. Najwięcej pracowników, którzy odeszli z firmy ocenili swoje zadolowanie na 3 w czterostopniowej skali. Zatem wysoka satysfakcja nie gwarantuje zatrzymania pracownika.

ggplot(data = tabela_knn, aes(x = JobSatisfaction, fill = Attrition)) +
  geom_histogram(binwidth = 1, position = "dodge", color = "black") +
  facet_grid(. ~ Attrition) +
  scale_fill_manual(values = c("skyblue", "orange")) +
  labs(
    title = "Rozkład satysfakcji pracy do odejść pracowników",
    x = "Satysfakcja z pracy",
    y = "Liczba osób",
    fill = "Odejście pracownika"
  ) +
  theme_minimal()

Wykres 4 - Zależność liczby lat pracy w firmie względem odejść

Wykres ten pokazuje, jak liczba lat pracy rozkłada się w grupie pracowników, którzy odeszli, w porównaniu do tych, którzy pozostali w firmie. Widać bardzo wyraźnie, iż najwięcej ludzi odchodzi z pracy do 10 lat stażu. Później odsetek ten znacznie spada, a czasem nawet nie występuje - staje się wyjątkiem. Mimo to należy zauważyć, że znaczna większość pracowników nie decyduje się w ogóle rezygnować z pracy w przeciągu tych 10 pierwszych lat.

ggplot(data = tabela_knn, aes(x = YearsAtCompany, fill = Attrition)) +
  geom_histogram(binwidth = 1, position = "dodge", color = "black") +
  facet_grid(. ~ Attrition) +
  scale_fill_manual(values = c("darkgreen", "orange")) +
  labs(
    title = "Zależność liczby lat pracy w firmie względem odejść",
    x = "liczby lat pracy w firmie",
    y = "Liczba osób",
    fill = "Odejście pracownika"
  ) +
  theme_minimal()

Wykres 5 - Zależność liczby stawki godzinowej względem odejść

Rozkłady dla obu grup są bardzo podobne – zakres wartości, mediana oraz rozstęp międzykwartylowy są niemal identyczne, co sugeruje, że wysokość stawki godzinowej nie miała istotnego wpływu na decyzję o odejściu. Brak wyraźnych różnic między grupami oznacza, że inne czynniki, a nie wysokość wynagrodzenia godzinowego, mogły bardziej wpływać na rotację pracowników.

ggplot(tabela_knn, aes(x = Attrition, y = HourlyRate )) +
  geom_boxplot() +
  coord_flip()

  labs(
    title = "Wykres pudełkowy dla zmiennej Attrition",
    x = "Attrition (Yes/No)",
    y = "HourlyRate"
  ) +
  theme_minimal()
## NULL

Wykres 6 - Zależność liczby lat od ostatniego awansu względem odejść

Wykres ten ma rozkład bardzo podobny do czwartego - większość pracowników odchodzi kiedy od ostatniego awansu nie minęło więcej niż 3 lata. Może to wskazywać na potrzebę nagradzania pracowników promocją w pierwszych latach ich stażu pracy.

ggplot(data = tabela_knn, aes(x = YearsSinceLastPromotion, fill = Attrition)) +
  geom_histogram(binwidth = 1, position = "dodge", color = "white") +
  facet_grid(. ~ Attrition) +
  scale_fill_manual(values = c("skyblue", "violet")) +
  labs(
    title = "Zależność liczby lat od ostatniego awansu względem odejść",
    x = "lata od ostatniego awansu",
    y = "Liczba osób",
    fill = "Odejście pracownika"
  ) +
  theme_minimal()

Wykres 7 - Korelacja rotacji pomiędzy działami względem odejść

Wykres ten przedstawia zestawienie działów i odejść pracowników. Można zauważyć, że są one na podobnym poziomie - największa różnica to 6 punktów procentowych. Wskazuje to na niewielki wpływ tej zmiennej na badanie motywacji pracowników do zmieniania miejsca pracy.

data_percent <- tabela_knn %>%
  group_by(Department, Attrition) %>%
  summarise(count = n()) %>%
  group_by(Department) %>%
  mutate(percent = count / sum(count) * 100)
## `summarise()` has grouped output by 'Department'. You can override using the
## `.groups` argument.
ggplot(data_percent, aes(x=Department, y=count, fill=Attrition)) +
  geom_bar(stat="identity", position="dodge") +
  geom_text(aes(label=sprintf("%.1f%%", percent)), position=position_dodge(width=0.9), vjust=-0.25) +
  theme_light() +
  labs(
    title = "Zależność rotacji pracownikow pomiedzy dzialami a odejscia z pracy",
    x = "Dział",
    y = "Liczba osób",
    fill = "Odejście pracownika"
  ) +
  theme(legend.position = c(0.85, 0.85))

Wykres 8 - Korelacja wybranych zmiennych (wiek, dochód, staż pracy, satysfakcja z pracy) względem odejść pracowników

Analizując korelacje zmiennej odejścia pracowników z innymi zmiennymi, można zauważyć następujące zależności:

  • Korelacja z wiekiem (-16,5%) – starsi pracownicy rzadziej odchodzą z firmy, co może wynikać z większej stabilności zawodowej lub większych korzyści związanych z długoterminowym zatrudnieniem.

  • Korelacja z miesięcznym dochodem (-15,5%) – osoby zarabiające więcej są mniej skłonne do odejścia, co może wskazywać na wpływ wynagrodzenia jako czynnika motywującego do pozostania w firmie.

  • Korelacja ze stażem pracy w firmie (-13,6%) – dłuższy staż pracy wiąże się z mniejszym prawdopodobieństwem rezygnacji, co może oznaczać, że pracownicy przywiązują się do organizacji wraz z upływem czasu.

  • Korelacja z satysfakcją z pracy (-10,6%) – choć relacja jest stosunkowo słaba, można zauważyć, że osoby mniej zadowolone z pracy częściej odchodzą, co potwierdza intuicyjny związek między satysfakcją a lojalnością wobec firmy.

tabela_knn$Attrition_bin <- ifelse(tabela_knn$Attrition == "Yes", 1, 0)

selected_vars <- tabela_knn[, c("Age", "MonthlyIncome", "JobSatisfaction", "YearsAtCompany", "Attrition_bin")]

cor_matrix <- cor(selected_vars, use = "complete.obs")

cor_matrix_percent <- cor_matrix * 100

melted_cor_matrix <- melt(cor_matrix_percent)

ggplot(data = melted_cor_matrix, aes(x = Var1, y = Var2, fill = value)) + 
  geom_tile(color = "white") +
  scale_fill_gradient2(low = "darkblue", high = "darkred", mid = "white", midpoint = 0, limit = c(-100, 100), space = "Lab", name="Korelacja (%)") +
  geom_text(aes(label = sprintf("%.1f%%", value)), color = "black", size = 4) +  
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
  coord_fixed()

Wykres 9 - Korelacja wybranych zmiennych (płeć, wiek, stan cywilny) względem odejść pracowników

  • Proporcje odejść wśród mężczyzn i kobiet są bardzo zbliżone. Płeć nie wydaje się zatem istotnym czynnikiem wpływającym na odejścia.
  • Osoby stanu wolnego mają wyraźnie wyższą skłonność do odejścia w porównaniu do osób zamężnych żonatych oraz rozwiedzionych.
  • Osoby, które odchodzą, częściej otrzymują niższe wynagrodzenie. Widać największą różnicę w przedziale do około 5000 jednostek – tu występuje najwięcej odejść.
  • Nadgodziny zwiększają odejścia. Jeśli pracownicy pracują ponad normę, mogą być bardziej skłonni do rezygnacji.
tabela_knn$Attrition_bin <- ifelse(tabela_knn$Attrition == "Yes", 1, 0)

ggplot(tabela_knn, aes(x = Gender, fill = Attrition)) +
  geom_bar(position = "fill") +
  labs(title = "Rozkład odejść w zależności od płci", y = "Proporcja", x = "Płeć") +
  scale_fill_manual(values = c("No" = "lightblue", "Yes" = "darkred"), name = "Attrition") +
  theme_minimal()

ggplot(tabela_knn, aes(x = MaritalStatus, fill = Attrition)) +
  geom_bar(position = "fill") +
  labs(title = "Rozkład odejść w zależności od stanu cywilnego", y = "Proporcja", x = "Stan cywilny") +
  scale_fill_manual(values = c("No" = "lightblue", "Yes" = "darkred"), name = "Attrition") +
  theme_minimal()

ggplot(tabela_knn, aes(x = MonthlyIncome, fill = Attrition)) +
  geom_density(alpha = 0.5) +
  labs(title = "Wpływ wynagrodzenia na odejścia", x = "Miesięczne wynagrodzenie", y = "Gęstość") +
  scale_fill_manual(values = c("No" = "lightblue", "Yes" = "darkred"), name = "Attrition") +
  theme_minimal()

ggplot(tabela_knn, aes(x = OverTime, fill = Attrition)) +
  geom_bar(position = "fill") +
  labs(title = "Odejścia a nadgodziny", y = "Proporcja", x = "Nadgodziny") +
  scale_fill_manual(values = c("No" = "lightblue", "Yes" = "darkred"), name = "Attrition") +
  theme_minimal()

Podsumowanie

Przedstawione wizualizacje dostarczają cennych informacji na temat czynników determinujących satysfakcję i rotację pracowników. Widać wyraźne zależności między stażem pracy, wynagrodzeniem oraz awansami a decyzją o odejściu, natomiast płeć nie wydaje się istotnym czynnikiem wpływającym na rotację. Najbardziej narażeni na odejście są pracownicy we wczesnym etapie kariery, zwłaszcza jeśli ich wynagrodzenie nie rośnie lub nie otrzymują awansu. Wyniki te mogą być pomocne w opracowaniu skutecznych strategii zarządzania zasobami ludzkimi, które pozwolą na zwiększenie rotacji pracowników i poprawę ich satysfakcji zawodowej.

Etap 3. Analiza opisowa i wnioskowanie statystyczne

Wstęp

Analiza statystyczna pozwala zrozumieć, jakie czynniki mogą wpływać na decyzję pracowników o odejściu z firmy. Wykorzystane metody, takie jak testy t-Studenta, ANOVA, analiza korelacji czy regresja liniowa, pomagają w identyfikacji zależności między zmiennymi. Szczególną uwagę poświęcono zróżnicowaniu dochodów, wpływowi wieku na decyzje o pozostaniu w firmie oraz nierównościom w rozkładzie wieku.

Podstawowe statystyki wieku w zależności od odejścia

Tabela pokazuje średni dochód, medianę oraz odchylenie standardowe wynagrodzenia dla dwóch grup pracowników - tych którzy odeszli z firmy i tych którzy w niej zostali. Pracownicy, którzy nie odeszli, mają wyższy średni dochód niż ci, którzy odeszli. To sugeruje, że osoby z niższym wynagrodzeniem częściej odchodzą. Mediana również pokazuje znaczącą różnicę: 5189.5 (pozostali) vs. 3017.5 (odeszli). Oznacza to, że większość osób odchodzących zarabiała znacznie mniej niż większość tych, którzy zostali. W obu grupach odchylenie standardowe jest wysokie, co oznacza duże zróżnicowanie wynagrodzeń. Jednak pracownicy, którzy pozostali, mają większe zróżnicowanie płac (4775 vs. 3662), co sugeruje, że w tej grupie mogą być zarówno osoby z wysokimi, jak i niskimi zarobkami.

income_summary <- tabela_knn %>% group_by(Attrition) %>%
summarise(Średni_dochód = round(mean(MonthlyIncome, na.rm = TRUE), 0),
Mediana_dochodu = median(MonthlyIncome, na.rm = TRUE),
Odchylenie_standardowe = round(sd(MonthlyIncome, na.rm = TRUE), 0))

print(income_summary)
## # A tibble: 2 × 4
##   Attrition Średni_dochód Mediana_dochodu Odchylenie_standardowe
##   <chr>             <dbl>           <dbl>                  <dbl>
## 1 No                 6767           5190.                   4775
## 2 Yes                4725           3018.                   3662

Podstawowe statystyki

Poniższa tabela przedstawia podstawowe statystyki dla analizowanego zbioru. Ich wyniki są następujące:

  1. kwartyle oraz rozstęp międzykwartylowy (IQR) dla wieku:
  • 25% obserwacji ma wartość wieku mniejszą lub równą 30

  • Połowa wartości wieku jest mniejsza lub równa 35

  • 75% obserwacji ma wartość wieku mniejszą lub równą 43

  • (30 - 43) rozstęp 13-stu lat, w którym znajduje się środkowe 50% danych

  1. miary zróżnicowania, kolejno: rozstęp, wariancję i odchylenie standardowe
  • Najmłodsza osoba w zbiorze danych ma 18 lat, a najstarsza 60 lat (zakres wieku to 42 lata)

  • Wysoka wartość wariancji sugeruje, że wartości są rozproszone i zróżnicowanie

  • Przeciętnie, wartości wieku różnią się od średniej o prawie 9 lat

  • Dane są stosunkowo wysoko zróżnicowane względem ich średniej wartości

  1. rozkład danych:
  • Skośność wieku wynosi 0,46 - dodatnia wartość wskazuje na skośność prawostronną oraz więcej młodszych pracowników

  • Kurtoza wieku wynosi 2,67, co oznacza, że rozkład wieku jest lekko spłaszczony, w porównaniu do normalnego

  1. reguły empiryczne:
  • 68% pracowników mieści się w przedziale 27-46 lat

  • 95% mieści się w przedziale 19-55 lat

  • 99,7% mieści się w przedziale 10-64 lat

quantiles <- quantile(tabela_knn$Age, probs = c(0.25, 0.5, 0.75))
iqr_value <- IQR(tabela_knn$Age)
age_range <- range(tabela_knn$Age)
variance <- round(var(tabela_knn$Age), 2)
std_dev <- round(sd(tabela_knn$Age), 2)
cv_age <- round((std_dev / mean(tabela_knn$Age)) * 100, 2)

skewness_value <- round(skewness(tabela_knn$Age), 2)
kurtosis_value <- round(kurtosis(tabela_knn$Age), 2)

mean_age <- round(mean(tabela_knn$Age), 2)

range_1sd <- paste0(round(mean_age - std_dev, 2), " - ", round(mean_age + std_dev, 2))
range_2sd <- paste0(round(mean_age - 2*std_dev, 2), " - ", round(mean_age + 2*std_dev, 2))
range_3sd <- paste0(round(mean_age - 3*std_dev, 2), " - ", round(mean_age + 3*std_dev, 2))

data_frame <- data.frame(
  Statystyka = c("1. kwartyl (Q1)", "Mediana (Q2)", "3. kwartyl (Q3)", 
                 "Rozstęp międzykwartylowy (IQR)", "Zakres (min - max)", 
                 "Wariancja", "Odchylenie standardowe", "Współczynnik zmienności (%)",
                 "Skośność (Skewness)", "Kurtoza (Kurtosis)",
                 "Średnia wieku", "Przedział (średnia ± 1σ)", 
                 "Przedział (średnia ± 2σ)", "Przedział (średnia ± 3σ)"),
  Wartość = c(quantiles[1], quantiles[2], quantiles[3], iqr_value, 
              paste0(age_range[1], " - ", age_range[2]), variance, std_dev, cv_age,
              skewness_value, kurtosis_value, mean_age, range_1sd, range_2sd, range_3sd)
)

kable(data_frame, format = "html") %>%
  kable_styling("striped", full_width = FALSE, bootstrap_options = c("hover", "condensed"))
Statystyka Wartość
  1. kwartyl (Q1)
30
Mediana (Q2) 35
  1. kwartyl (Q3)
43
Rozstęp międzykwartylowy (IQR) 13
Zakres (min - max) 18 - 60
Wariancja 80.43
Odchylenie standardowe 8.97
Współczynnik zmienności (%) 24.32
Skośność (Skewness) 0.46
Kurtoza (Kurtosis) 2.67
Średnia wieku 36.88
Przedział (średnia ± 1σ) 27.91 - 45.85
Przedział (średnia ± 2σ) 18.94 - 54.82
Przedział (średnia ± 3σ) 9.97 - 63.79

Wwykres pudełkowy wieku

Wykres został stworzony na bazie powyższej tabeli z wartościwami podstawowych statystyk.

  • Średnia i mediana są blisko siebie – oznacza to, że rozkład wieku jest dość symetryczny, bez silnej skośności.

  • Kilka wartości odstających znajduje się zarówno poniżej, jak i powyżej typowego zakresu wieku, co oznacza, że w próbie mogą być jednostki znacznie młodsze lub starsze niż większość.

  • Większość danych mieści się w zakresie pudełka (IQR) – sugeruje to, że wiek większości badanych osób nie wykazuje ekstremalnej zmienności.

  • Najwięcej punktów skupia się wokół mediany i wewnątrz pudełka, co potwierdza, że większość wartości jest w standardowym zakresie.

set.seed(42)
tabela_wykres <- data.frame(Age = rnorm(300, mean = 35, sd = 10))

quantiles <- quantile(tabela_wykres$Age, probs = c(0.25, 0.5, 0.75))
mean_age <- mean(tabela_wykres$Age)

ggplot(tabela_wykres, aes(x = "", y = Age)) +
  geom_boxplot(fill = "lightblue", color = "black", outlier.color = "red", 
               outlier.shape = 16, outlier.size = 3, linewidth = 1) +
  geom_jitter(aes(x = ""), color = "darkblue", alpha = 0.5, width = 0.2, size = 2) +
  geom_hline(yintercept = quantiles[1], linetype = "dashed", color = "red", linewidth = 1) +
  geom_hline(yintercept = quantiles[2], linetype = "dashed", color = "green", linewidth = 1) +
  geom_hline(yintercept = quantiles[3], linetype = "dashed", color = "blue", linewidth = 1) +
  geom_hline(yintercept = mean_age, linetype = "solid", color = "orange", linewidth = 1.2) +
  labs(title = "Wykres pudełkowy wieku",
       subtitle = "Boxplot + punkty indywidualne + linie kwartylowe i średnia",
       x = "",
       y = "Wiek") +
  theme_minimal()

Testy

Poniższa tabela przedstawia różne rodzaje testów statystycznych dla analizowanego zbioru. Ich wyniki są następujące:

  1. Test normlaności Shapiro-Wilka

Służy do sprawdzenia normalności rozkładu danej zmiennej. Hipoteza zerowa mówi, że dane pochodzą z rozkładu normalnego, względem alternatywy, że tak nie jest. Z racji, że wartość p-value jest niższa niż 0.01 odrzucamy hipotezę zerową na rzecz alternatywy. Oznacza to, że rozkład wieku nie jest normalny w obydwu badanych przypadkach zmiennych.

  1. Test t-studenta (dla Age względem Attrition)

Wykorzystany w celu zbadania zależności pomiędzy wiekiem a odejściem z pracy.

  • P-value jest ekstremalnie niskie (p < 0.05), więc istnieje istotna statystycznie różnica między średnimi wieku pracowników, którzy zostali, a tych, którzy odeszli.

  • Średnia wieku pracowników, którzy pozostali w firmie, wynosi 37.50 lat, podczas gdy średnia wieku osób, które odeszły, to 33,33 lat.

  • Przedział ufności (2,83 – 5,51) nie obejmuje zera, co dodatkowo potwierdza, że różnica między grupami jest statystycznie istotna.

  1. ANOVA

Zastosowana do sprawdzenia, czy istnieją istotne różnice w liczbie lat pracy między pracownikami różnych działów.

  • P-value = 0,83, co oznacza, że nie ma istotnych statystycznie różnic w liczbie lat pracy

  • Niska wartość F (0,18) sugeruje, że różnice między średnimi dla działów są bardzo małe w porównaniu do zmienności wewnątrz grup. Przynależność do działu nie wpływa istotnie na liczbę lat pracy – staż pracowników jest podobny niezależnie od działu.

  1. Test chi-kwadrat

Zastosowany do zbadania, czy istnieje zależność między odejściem pracownika z firmy a poziomem wykształcenia.

  • p-value = 0,314, co oznacza, że poziom wykształcenia nie ma istotnego wpływu na decyzję o odejściu z firmy.

  • staystyka jest niska, co oznacza, że różnice między rzeczywistymi a oczekiwanymi wartościami są niewielkie.

  • Wniosek: Wykształcenie nie wpływa istotnie na rotację pracowników.

  1. Test chi-kwadrat 2

Został użyty do zbadania, czy istnieje zależność między odejściem pracownika z firmy a stanowiskiem.

  • p-value < 0,05 co oznacza, że stanowisko ma bardzo duże znaczenie w kontekście odejść z pracy

  • staystyka jest wysoka, co oznacza, że różnice między rzeczywistymi a oczekiwanymi wartościami są duże.

  • Wniosek: Odejścia są silnie zależne od stanowiska. Niektóre stanowiska mogą mieć znacznie większą rotację niż inne.

  1. Test korelacji Pearsona

Zbadano korelację pomiędzy wiekiem a całkowitą liczbą lat pracy. Wysoka wartość statystyki wskazuje na silną zależność między zmiennymi. Bardzo niska wartość p-value oznacza, że możemy odrzucić hipotezę zerową stanowiącą o braku korelacji pomiędzy wiekiem a całkowitą liczbą lat pracy. Całkiem wysoka wartość współczynnika oznacza, że im wyższy wiek, tym większa liczba lat przepracowanych.

  1. Współczynnik Giniego

Współczynnik Giniego mierzy nierówność rozkładu danej zmiennej – w tym przypadku wieku. Wiek w tej populacji jest stosunkowo równomiernie rozłożony, z niewielkimi różnicami między wartościami.

shapiro_age <- shapiro.test(tabela_knn$Age)
shapiro_workyears <- shapiro.test(tabela_knn$TotalWorkingYears)

t_test_age <- t.test(Age ~ Attrition, data = tabela_knn)

anova_model <- aov(TotalWorkingYears ~ Department, data = tabela_knn)
anova_summary <- summary(anova_model)

chi_attrition_edu <- chisq.test(table(tabela_knn$Attrition, tabela_knn$Education))
chi_attrition_job <- chisq.test(table(tabela_knn$Attrition, tabela_knn$JobRole))

correlation_test <- cor.test(tabela_knn$Age, tabela_knn$TotalWorkingYears)

gini_coefficient <- Gini(tabela_knn$Age)

data_frame <- data.frame(
  Test = c("Shapiro-Wilk (Age)", "Shapiro-Wilk (TotalWorkingYears)", 
           "Test t-studenta (Age ~ Attrition)", "95% CI (t-test)", "Średnia w grupie 'No'", "Średnia w grupie 'Yes'",
           "ANOVA (TotalWorkingYears ~ Department)", "Stopnie swobody ANOVA",
           "Chi-kwadrat (Attrition ~ Education)", "Chi-kwadrat (Attrition ~ JobRole)",
           "Korelacja (Age ~ TotalWorkingYears)", "95% CI (korelacja)",
           "Współczynnik Giniego (Age)"),
  
  Statystyka = c(round(shapiro_age$statistic, 3), round(shapiro_workyears$statistic, 3),
                 round(t_test_age$statistic, 3), paste0("[", round(t_test_age$conf.int[1], 3), ", ", round(t_test_age$conf.int[2], 3), "]"),
                 round(t_test_age$estimate[1], 3), round(t_test_age$estimate[2], 3),
                 round(anova_summary[[1]]$F[1], 3), paste0(anova_summary[[1]]$Df[1], " / ", anova_summary[[1]]$Df[2]),
                 round(chi_attrition_edu$statistic, 3), round(chi_attrition_job$statistic, 3),
                 round(correlation_test$estimate, 3), paste0("[", round(correlation_test$conf.int[1], 3), ", ", round(correlation_test$conf.int[2], 3), "]"),
                 round(gini_coefficient, 3)),
  
  p_wartosc = c(formatC(shapiro_age$p.value, format="e", digits=6), 
                formatC(shapiro_workyears$p.value, format="e", digits=6),
                formatC(t_test_age$p.value, format="e", digits=6), "-",
                "-", "-", 
                formatC(anova_summary[[1]]$`Pr(>F)`[1], format="e", digits=6), "-",
                formatC(chi_attrition_edu$p.value, format="e", digits=6),
                formatC(chi_attrition_job$p.value, format="e", digits=6),
                formatC(correlation_test$p.value, format="e", digits=6), "-",
                "-")
)

kable(data_frame, format = "html", col.names = c("Test statystyczny", "Statystyka", "p-wartość")) %>%
  kable_styling("striped", full_width = FALSE, bootstrap_options = c("hover", "condensed"))
Test statystyczny Statystyka p-wartość
Shapiro-Wilk (Age) 0.975 2.407320e-15
Shapiro-Wilk (TotalWorkingYears) 0.907 5.630922e-29
Test t-studenta (Age ~ Attrition) 6.125 2.978655e-09
95% CI (t-test) [2.831, 5.512]
Średnia w grupie ‘No’ 37.502
Średnia w grupie ‘Yes’ 33.33
ANOVA (TotalWorkingYears ~ Department) 0.182 8.332249e-01
Stopnie swobody ANOVA 2 / 1467
Chi-kwadrat (Attrition ~ Education) 4.751 3.138231e-01
Chi-kwadrat (Attrition ~ JobRole) 84.929 4.952037e-15
Korelacja (Age ~ TotalWorkingYears) 0.68 3.484627e-200
95% CI (korelacja) [0.652, 0.707]
Współczynnik Giniego (Age) 0.137

Regresja liniowa

Poniżej sformułowano regresję liniową, w której odejscia z pracy są wyjaśniane przez wiek oraz dochód, odległość od domu, satysfakcję z pracy, staż pracy, nadgodziny, lata od ostatniego awansu. Z regresji tej wynika, że:

  • każdy dodatkowy rok życia zmniejsza prawdopodobienstwo odejścia z pracy o 0.00465

  • z każdym wzrostem miesięcznego wynagrodzenia o 1 zmniejsza się prawdopodobienstwo odejścia z pracy o 0.00000736

  • zwiększenie dystansu od domu o 1 jednostkę zwiększa prawdopodobienstwo odejścia z pracy o 0.00382

  • każdy dodatkowy rok pracy w firmie zmniejsza prawdopodobienstwo odejścia z pracy o 0.00669

  • kiedy pracownik ma nadgodziny to zwiększa się prawdopodobienstwo odejścia z pracy o 0.18509, niż gdyby ich nie miał

Wszystkie zmienne są istotne statystycznie na poziomie przynajmniej 1%. Model wyjaśniany jest w 11,3%.

tabela_knn_num <- tabela_knn
tabela_knn_num$Attrition <- as.numeric(as.factor(tabela_knn_num$Attrition)) - 1 
tabela_knn_num$OverTime <- as.numeric(as.factor(tabela_knn_num$OverTime)) - 1
reg_model <- lm(Attrition ~ Age+MonthlyIncome+DistanceFromHome+JobSatisfaction+YearsAtCompany+OverTime+YearsSinceLastPromotion, data = tabela_knn_num)
summary(reg_model)
## 
## Call:
## lm(formula = Attrition ~ Age + MonthlyIncome + DistanceFromHome + 
##     JobSatisfaction + YearsAtCompany + OverTime + YearsSinceLastPromotion, 
##     data = tabela_knn_num)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -0.4527 -0.1855 -0.1038 -0.0059  1.2483 
## 
## Coefficients:
##                            Estimate  Std. Error t value             Pr(>|t|)
## (Intercept)              0.39496243  0.04471464    8.83 < 0.0000000000000002
## Age                     -0.00483343  0.00112527   -4.30            0.0000186
## MonthlyIncome           -0.00000500  0.00000238   -2.10              0.03602
## DistanceFromHome         0.00382779  0.00107834    3.55              0.00040
## JobSatisfaction         -0.03568205  0.00792597   -4.50            0.0000073
## YearsAtCompany          -0.00669901  0.00198798   -3.37              0.00077
## OverTime                 0.18509245  0.01941256    9.53 < 0.0000000000000002
## YearsSinceLastPromotion  0.00944278  0.00345419    2.73              0.00634
##                            
## (Intercept)             ***
## Age                     ***
## MonthlyIncome           *  
## DistanceFromHome        ***
## JobSatisfaction         ***
## YearsAtCompany          ***
## OverTime                ***
## YearsSinceLastPromotion ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.335 on 1462 degrees of freedom
## Multiple R-squared:  0.117,  Adjusted R-squared:  0.113 
## F-statistic: 27.7 on 7 and 1462 DF,  p-value: <0.0000000000000002

Krzywa Lorenza

Krzywa Lorenza przedstawia nierówność rozkładu danej zmiennej – w tym przypadku wieku. Czarna linia diagonalna – reprezentuje idealną równość (każdy ma taki sam wiek). Niebieska krzywa – pokazuje rzeczywisty rozkład wieku. Im bardziej oddala się od linii równości, tym większa nierówność. Krzywa Lorenza znajduje się blisko linii równości, co oznacza, że wiek jest stosunkowo równomiernie rozłożony.

plot(Lc(tabela_knn$Age), col = "blue", lwd = 2, main = "Krzywa Lorenza dla wieku")

Różnice wieku między osobami, które odeszły i zostały

Pracownicy, którzy pozostali w firmie mają średni wiek 37,5 roku, podczas gdy Ci, którzy odeszli, średnio 33,33 roku. Test t (Welcha) wykazał istotną statystycznie różnicę (t = 6.13, p < 0.001), co wskazuje, że młodsi pracownicy częściej odchodzą z pracy. Rozkłady danych pokazują większą gęstość młodszych osób w grupie „Yes” oraz starszych w grupie „No”.

ggstatsplot::ggbetweenstats(data = tabela_knn, x = Attrition, y = Age,
title = "Porównanie wieku między osobami, które odeszły i zostały", xlab
= "Attrition (Czy pracownik odszedł?)", ylab = "Wiek", messages = FALSE)

Porównanie miesięcznych dochodów między osobami, które odeszły i zostały

Pracownicy, którzy pozostali w firmie, mają średni miesięczny dochód μ = 6766.73, natomiast ci, którzy odeszli, μ = 4725.49. Test t (Welcha) wskazuje istotną różnicę (t = 7.23, p < 0.001), co sugeruje, że osoby o niższych dochodach częściej odchodzą. Rozkłady pokazują większą gęstość wyższych dochodów w grupie „No” oraz niższych w grupie „Yes”.

ggstatsplot::ggbetweenstats(data = tabela_knn, x = Attrition, y =
MonthlyIncome, title = "Porównanie miesięcznych dochodów między osobami,
które odeszły i zostały", xlab = "Attrition (Czy pracownik odszedł?)",
ylab = "Miesięczny dochód", messages = FALSE )

Podsumowanie

Przeprowadzone analizy wskazują, że osoby o niższych dochodach oraz młodsi pracownicy częściej decydują się na odejście. Dodatkowo, zmienne takie jak odległość od miejsca pracy czy konieczność pracy w nadgodzinach również wpływają na rotację pracowników. Wyniki te mogą być cenną wskazówką dla pracodawców w kontekście strategii rotacji i polityki wynagrodzeń.

Projekt koncentrował się na analizie odejść pracowników (Attrition) w kontekście kluczowych zmiennych, takich jak wiek i miesięczne dochody. Wykazano, że młodsi pracownicy oraz osoby o niższych zarobkach częściej opuszczają firmę, co potwierdziły istotne statystycznie testy t (Welcha). Rozkłady danych pokazały różnice w strukturze grup „zostałych” i „odeszłych”, podkreślając istotne wzorce w zatrzymywaniu pracowników. Średni wiek osób, które pozostały, wynosił 37,5 roku, podczas gdy tych, którzy odeszli, 33,33 roku, a średnie miesięczne dochody w tych grupach wynosiły odpowiednio 6766,73 i 4725,49. Analiza dostarczyła cennych informacji dla działu HR, wskazując na konieczność zwiększenia wsparcia dla młodszych pracowników i tych z niższymi dochodami. Wyniki te mogą pomóc w opracowaniu strategii rotacji, minimalizując przyszłe odejścia z pracy.