1. Introduction

This report presents an analysis of the IBM HR Analytics Employee Attrition & Performance dataset from Kaggle. The primary objective is to understand the factors contributing to employee attrition and performance within the organization.

2. Data Preparation

Data cleaning involved removing columns with fixed values, unused values and handling missing data. The cleaned dataset was then used for further analysis and visualization.

# load data in 'global' chunk so it can be shared by all users of the dashboard
library(tidyverse)
## Warning: le package 'tidyverse' a été compilé avec la version R 4.3.3
## Warning: le package 'ggplot2' a été compilé avec la version R 4.3.3
## Warning: le package 'tibble' a été compilé avec la version R 4.3.3
## Warning: le package 'tidyr' a été compilé avec la version R 4.3.3
## Warning: le package 'readr' a été compilé avec la version R 4.3.3
## Warning: le package 'purrr' a été compilé avec la version R 4.3.3
## Warning: le package 'dplyr' a été compilé avec la version R 4.3.3
## Warning: le package 'stringr' a été compilé avec la version R 4.3.3
## Warning: le package 'forcats' a été compilé avec la version R 4.3.3
## Warning: le package 'lubridate' a été compilé avec la version R 4.3.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(DT)
## Warning: le package 'DT' a été compilé avec la version R 4.3.3
# read data 
data <- readr::read_csv("WA_Fn-UseC_-HR-Employee-Attrition.csv");
## Rows: 1470 Columns: 35
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (9): Attrition, BusinessTravel, Department, EducationField, Gender, Job...
## dbl (26): Age, DailyRate, DistanceFromHome, Education, EmployeeCount, Employ...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# find the columns with fixed values
fixed_value_columns <- sapply(data, function(col) length(unique(col))) == 1
fixed_value_columns_names <- names(fixed_value_columns)[fixed_value_columns]
# Remove columns with fixed values
df_cleaned <- data[, !fixed_value_columns]
df_cleaned <- data[, -which(names(data) == "EmployeeNumber")] #we have no use for this
#filter data based on Attrition
d1<-filter(df_cleaned,Attrition=="Yes");

d2<-filter(df_cleaned,Attrition=="No");

3. Data Analysis of Attrition

Attrition = Yes

This section examines the characteristics of employees who have left the company.

#visualise data where Attrition= Yes
datatable(d1,options = list(scrollX = TRUE))
#Analysis where Attrition= Yes

#display the number of employees that have left in each department
d1 %>%
       group_by(Department) %>%
       summarise(nb = n())
#display by gender
d1 %>%
       group_by(Gender) %>%
       summarise(nb = n())
#by age
d1 %>%
       group_by(Age) %>%
      summarise(nb = n())
#by Education
d1 %>%
       group_by(Education) %>%
       summarise(nb = n())
#by DistanceFromHome
d1 %>%
     group_by(DistanceFromHome) %>%
     count() 
#by BusinessTravel
d1 %>%
       group_by(BusinessTravel) %>%
 summarise(count = n()) %>%
    mutate(percentage = count / sum(count) * 100)
#By Business travel and Department
d1 %>%
      group_by(BusinessTravel, Department, .drop = FALSE) %>%
    summarise(count = n(), .groups = 'drop_last') %>%
  mutate(percentage = count / sum(count) * 100)
#BY Gender and department 
d1 %>%
      group_by(Gender, Department, .drop = FALSE) %>%
     summarise(count = n(), .groups = 'drop_last') %>%
     mutate(percentage = count / sum(count) * 100)
# By department and jobrole 
d1 %>%
       group_by(Department,JobRole  , .drop = FALSE) %>%
       summarise(count = n(), .groups = 'drop_last') %>%
       mutate(percentage = count / sum(count) * 100)
#By MONTHLY INCOME
rates1<-d1
rates1$MonthlyIncome <- as.numeric(as.character(rates1$MonthlyIncome))

# Create a new column 'IncomeCategory' based on MonthlyIncome ranges
rates1$IncomeCategory <- cut(rates1$MonthlyIncome,
                             breaks = c(1000, 2500,5000, 10000, 15000, Inf),
                             labels = c('[1000-2500]','[2500-5000]', '[5000-10000]', '[10000-15000]', '[15000+]'),
                             right = FALSE)

rates2<- rates1 %>% count(IncomeCategory)
rates2

Attrition = No

Analyze and visualize the data for employees who stayed with the company.

datatable(d2,options = list(scrollX = TRUE))
d1 <- d2
#display the number of employees that have Stayed in each department
d1 %>%
       group_by(Department) %>%
       summarise(nb = n())
#display by gender
d1 %>%
       group_by(Gender) %>%
       summarise(nb = n())
#by age
d1 %>%
       group_by(Age) %>%
      summarise(nb = n())
#by Education
d1 %>%
       group_by(Education) %>%
       summarise(nb = n())
#by DistanceFromHome
d1 %>%
     group_by(DistanceFromHome) %>%
     count() 
#by BusinessTravel
d1 %>%
       group_by(BusinessTravel) %>%
 summarise(count = n()) %>%
    mutate(percentage = count / sum(count) * 100)
#By Business travel and Department
d1 %>%
      group_by(BusinessTravel, Department, .drop = FALSE) %>%
    summarise(count = n(), .groups = 'drop_last') %>%
  mutate(percentage = count / sum(count) * 100)
#BY Gender and department 
d1 %>%
      group_by(Gender, Department, .drop = FALSE) %>%
     summarise(count = n(), .groups = 'drop_last') %>%
     mutate(percentage = count / sum(count) * 100)
# By department and jobrole 
d1 %>%
       group_by(Department,JobRole  , .drop = FALSE) %>%
       summarise(count = n(), .groups = 'drop_last') %>%
       mutate(percentage = count / sum(count) * 100)
#By MONTHLY INCOME
rates1<-d1
rates1$MonthlyIncome <- as.numeric(as.character(rates1$MonthlyIncome))

# Create a new column 'IncomeCategory' based on MonthlyIncome ranges
rates1$IncomeCategory <- cut(rates1$MonthlyIncome,
                             breaks = c(1000, 2500,5000, 10000, 15000, Inf),
                             labels = c('[1000-2500]','[2500-5000]', '[5000-10000]', '[10000-15000]', '[15000+]'),
                             right = FALSE)

rates2<- rates1 %>% count(IncomeCategory)
rates2

4. Data Visualisation of Attrition

Comparative visualizations, such as histograms and density plots, were created to illustrate differences in attributes like age and monthly income between employees who left and those who remained. These visual insights assist in identifying significant factors related to attrition.

Age Distribution of leaving employees

data1<-d1
ggplot(data1) +
  geom_histogram(aes(x = Age ,fill = Attrition), binwidth = 1.5, color="black" )

Distribution of age by attrition status

ggplot(data, aes(x = Age, fill = Attrition)) + geom_density(alpha = 0.5)

Department distribution of leaving employees

ggplot(data1, aes(x = Department, fill = Attrition)) + geom_bar(width = 1) + coord_polar(theta = "y")

Gender distribution of leaving employees

ggplot(data1, aes(x = Gender, fill = Attrition)) + geom_bar(width = 1) + coord_polar(theta = "y")

Monthly Income Distribution by Attrition Status

ggplot(data, aes(x = Attrition, y = MonthlyIncome, fill = Attrition)) + geom_boxplot()

Distance from home Distribution of leaving employees

ggplot(data1) +
  geom_histogram(aes(x = DistanceFromHome ),fill = "orange", color = "black")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

5. Conclusion

In conclusion, this analysis sheds light on the critical factors influencing employee attrition at IBM. By addressing the identified issues, the company can improve retention rates and enhance overall employee satisfaction and performance.