INTRODUCTION - OBJECTIF

Une entreprise se définit non seulement par son capital économique et ses compétences techniques mais aussi par son capital humain. Les succès d’une entreprise dépendent en grande partie des performances de l’ensemble de sa main d’oeuvre (ce qu’on appelle workforce en Anglais). Le Data Analyst peut apporter son expertise en analysant les données des employés afin d’aider l’entreprise (à traver le Responsable RH) à prendre de bonne décisions sur la gestion du personnel.

Loin d’être un spécialiste des Ressources Humaines, mon but ici est de montrer comment un Data Analyst peut étudier différentes problématiques concernant la gestion des employés auxquelles une entreprise peut faire face. Ces problématiques sont :

Vous pouvez télécharger via ce lien toutes les données utilisées dans cette étude.

LE RECRUTEMENT

Les succès d’une entreprise dépendent en grande partie des performances de l’ensemble de son effectif et son effectif dérive de ses recruitements. Donc le recruitement est un maillon important de tout le dispositif RH.

#Importation des données
recrutement <- read.csv("recruitment_data.csv")

#Structure des données
glimpse(recrutement)
## Observations: 446
## Variables: 4
## $ attrition          <int> 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0...
## $ performance_rating <int> 3, 3, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 4, 3, 2...
## $ sales_quota_pct    <dbl> 1.08819016, 2.39417262, 0.49753021, 2.51395...
## $ recruiting_source  <fct> Applied Online, NA, Campus, NA, Applied Onl...

Il y a 446 observations (446 employés du département des ventes d’une entreprise X) et 4 variables pour mesurer la qualité du recruitement.

Bien sûr que dans la réalité, la qualité d’une embauche va dépendre de plusieurs autres facteurs. Ces quelques variables peuvent déjà être un point de départ pour une analyse.

A partir de ces données, le but est d’aider l’équipe d’acquisition de talents (puisque c’est maintenant le terme qu’emploie les recruiteurs :) ) à comprendre quel canal de recruitement produit les meilleures embauches (c’est- à dire les meilleurs vendeurs) pour le département VENTE de cette entreprise. Pour ce faire, je vais dans un premier temps calculer le quota moyen de ventes atteint par les vendeurs de chaque source de recrutement et dans un second temps le taux d’attrition pour chaque source de recruitement.

#quota moyen de ventes
avg_quota_data <- recrutement %>%
  group_by(recruiting_source) %>%
  summarize(avg_sales_quota = mean(sales_quota_pct)) %>%
  arrange(desc(avg_sales_quota))

avg_quota_data
## # A tibble: 5 x 2
##   recruiting_source avg_sales_quota
##   <fct>                       <dbl>
## 1 <NA>                        1.17 
## 2 Applied Online              1.06 
## 3 Referral                    1.02 
## 4 Campus                      0.908
## 5 Search Firm                 0.887
#Taux d'attrition
attrition_rate_data <- recrutement %>%
  group_by(recruiting_source) %>%
  summarize(attrition_rate = sum(attrition == 1)/n()) %>%
  arrange(desc(attrition_rate))

attrition_rate_data
## # A tibble: 5 x 2
##   recruiting_source attrition_rate
##   <fct>                      <dbl>
## 1 Search Firm                0.5  
## 2 Referral                   0.333
## 3 Campus                     0.286
## 4 Applied Online             0.246
## 5 <NA>                       0.132

Hormis les sources de recruitement inconnues, les recrus qui avaient candidaté en ligne sont en moyenne les meilleurs vendeurs et ils sont aussi ceux qui ont le moins tendance à quitter l’entreprise. Tandis que les recrus des Cabinets ont la plus faible performance en matière de vente et ils ils sont les plus aptes à quitter l’entreprise. Cette information est confirmée par les graphiques ci-dessous :

#quota moyen de ventes atteint en fonction de la source de recrutement
ggplot(avg_quota_data, aes(recruiting_source, avg_sales_quota)) +
  geom_col() +
  labs(
    x = "Source de recrutement", 
  y = "Quota moyen de ventes atteint", 
  title = "Quota moyen de ventes atteint en fonction de la source de recrutement") +
  theme_minimal()

#Taux d'attrition en fonction de la source de recruitement
ggplot(attrition_rate_data, aes(recruiting_source, attrition_rate)) +
  geom_col() +
  labs(
    x = "Source de recrutement", 
  y = "Taux d'attrition", 
  title = "Taux d'attrition selon la source de recrutement") +
  theme_minimal()

A partir de cette analyse et hormis les sources inconnues, on peut dire que la meilleure source de recrutement est en ligne et la mauvaise est de passer par les cabinets de recrutement.

Que peut-on dire en matière de performance générale de l’employé. Je vais créer une nouvelle variable unable qui est égale à 1 si la note de performance de l’employé est 1 ou 2 et égale à 0 dans les autres cas.

#Création de la variable 'unable*
recrutement_unable <- recrutement %>%
  mutate(unable = ifelse(performance_rating %in% c(1, 2), 1, 0))

#Vérification de la structure du nouveau jeu de données
glimpse(recrutement_unable)
## Observations: 446
## Variables: 5
## $ attrition          <int> 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0...
## $ performance_rating <int> 3, 3, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 4, 3, 2...
## $ sales_quota_pct    <dbl> 1.08819016, 2.39417262, 0.49753021, 2.51395...
## $ recruiting_source  <fct> Applied Online, NA, Campus, NA, Applied Onl...
## $ unable             <dbl> 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1...
#proportion des employés non performants
recrutement_unable_prop <- recrutement_unable %>%
  group_by(recruiting_source) %>%
  summarize(unable_prop = sum(unable == 1)/n()) %>%
  arrange(desc(unable_prop))

recrutement_unable_prop
## # A tibble: 5 x 2
##   recruiting_source unable_prop
##   <fct>                   <dbl>
## 1 Search Firm             0.3  
## 2 Referral                0.267
## 3 <NA>                    0.259
## 4 Campus                  0.25 
## 5 Applied Online          0.238

Les employés provenant des cabinets de recuitement ont le plus grand pourcentage d’inaptitude (inapte doit être compris ici comme étant le contraire de performant) tandis que ceux ayant candidaté en ligne ont le plus faible taux d’inaptitude.

L’ENGAGEMENT DES EMPLOYES

L’engagement du personnel est une autre problématique auqelle se confronte un responsable RH. Il y a plusieurs définitions de l’engagement et le lien entre l’engagement d’un employé et les résultats commerciaux fait toujours object de débat. L’idée générale qu’on a est que des employés engagés seront plus productifs et plus aptes à rester longtemps dans l’entreprise. A partir des données des employés de son entreprise, un data analyst peut investiguer la relation entre l’engagement et les résultats commerciaux et aider le responsable RH à mieux prendre les décisions.

Les données utilisées pour cette partie proviennet d’un sondage.

#Importation des données
survey_data <- read.csv("survey_data.csv")

#Structure des données
glimpse(survey_data)
## Observations: 1,470
## Variables: 5
## $ employee_id         <int> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, ...
## $ department          <fct> Sales, Engineering, Engineering, Engineeri...
## $ engagement          <int> 3, 3, 3, 3, 3, 3, 3, 1, 4, 2, 5, 3, 3, 1, ...
## $ salary              <dbl> 103263.64, 80708.64, 60737.05, 99116.32, 5...
## $ vacation_days_taken <int> 7, 12, 12, 7, 18, 9, 18, 4, 12, 14, 13, 13...

Ici, on a 1470 employés et cinq variables :

Quel est le département qui a le plus faible score (en moyenne) d’engagement ?

#Score moyen d'engagement selon le département
engag_summary <- survey_data %>%
  group_by(department) %>%
  summarize(avg_engag = mean(engagement)) %>%
  arrange(avg_engag)

engag_summary
## # A tibble: 3 x 2
##   department  avg_engag
##   <fct>           <dbl>
## 1 Sales            2.81
## 2 Engineering      3.15
## 3 Finance          3.24

En moyenne, c’est le département des ventes qui a le plus faible score d’engagement.

Quels sont les employés qui ne sont pas engagés ? Je vais définir dans un premier temps une variable disengaged qui est égale à 1 si l’employé à un sore d’enggagement de 1 ou 2 et égal à 0 dans les autres cas. Dans un second temps, je calculerai pour chaque département, le pourcentage d’employés qui ne sont pas engagés, le salaire moyen et le nombre moyen de jours de congés pris.

#Création de la variable 'disengaged'
survey_data <- survey_data %>%
  mutate(disengaged = ifelse(engagement %in% c(1, 2), 1, 0))

#Calculs de quelques statistiques par département
survey_data_summary <- survey_data %>%
  group_by(department) %>%
  summarize(
    pct_disengag = sum(disengaged == 1)/n(),
    avg_salary = mean(salary),
    avg_vacation_days_taken = mean(vacation_days_taken)
  ) %>%
  arrange(desc(pct_disengag))

survey_data_summary
## # A tibble: 3 x 4
##   department  pct_disengag avg_salary avg_vacation_days_taken
##   <fct>              <dbl>      <dbl>                   <dbl>
## 1 Sales              0.330     75074.                    9.22
## 2 Engineering        0.206     73576.                   12.2 
## 3 Finance            0.190     76652.                   11.5

Selon ces chiffres, le département VENTES a le plus grand pourcentage d’employés qui ne sont pas engagés mais c’est aussi dans ce département que les employés partent, en moyenne, le moins en congé. Le département Ingénierie a le plus faible salaire annuel moyen et le département Finance a le plus fort salaire annuel moyen. Ces constats sont confirmés par les graphiques ci-dessous.

#Format long de la table de données 'survey_data_summary'
survey_data_summary_long <- survey_data_summary %>%
  gather(-department, key = "measure", value = "value")

head(survey_data_summary_long, n = 10)
## # A tibble: 9 x 3
##   department  measure                     value
##   <fct>       <chr>                       <dbl>
## 1 Sales       pct_disengag                0.330
## 2 Engineering pct_disengag                0.206
## 3 Finance     pct_disengag                0.190
## 4 Sales       avg_salary              75074.   
## 5 Engineering avg_salary              73576.   
## 6 Finance     avg_salary              76652.   
## 7 Sales       avg_vacation_days_taken     9.22 
## 8 Engineering avg_vacation_days_taken    12.2  
## 9 Finance     avg_vacation_days_taken    11.5
#Visualisation des résultats précédents
ggplot(survey_data_summary_long, aes(measure, value, fill = department)) +
  geom_col(position = "dodge") +
  facet_wrap(~ measure, scales = "free") +
  theme_minimal()

SALAIRES DES NOUVEAUX EMPLOYES

Les employeurs doivent déterminer avec soin les salaires des nouveaux employés. Bien que le salaire se négocie et qu’il dépend de plusieurs paramètres, Certains anciens employés de l’entreprise peuvent être frustrés de voir un nouveau employé qui touche un salaire plus élevé que leur salaire.

Dans cette partie, j’analyse les salaires des nouvelles recrues d’une entreprise et ceux des employés actuels afin de déterminer si les nouveaux gagnent plus.

#Importation des données
pay <- read.csv("fair_pay_data.csv")

#Structure des données
glimpse(pay)
## Observations: 1,470
## Variables: 5
## $ employee_id <int> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, ...
## $ department  <fct> Sales, Engineering, Engineering, Engineering, Engi...
## $ salary      <dbl> 103263.64, 80708.64, 60737.05, 99116.32, 51021.64,...
## $ new_hire    <fct> No, No, Yes, Yes, No, No, Yes, No, No, No, Yes, No...
## $ job_level   <fct> Salaried, Hourly, Hourly, Salaried, Hourly, Salari...

Je vais calculer à présent le salaire moyen des nouveaux employés et des employés actuels.

#Calcul du salaire moyen
pay %>%
  group_by(new_hire) %>%
  summarize(avg_salary = mean(salary))
## # A tibble: 2 x 2
##   new_hire avg_salary
##   <fct>         <dbl>
## 1 No           73425.
## 2 Yes          76074.

Les nouvelles recrues ont un salaire moyen plus élevé que les employés actuels. Mais est-ce que cette différence est statistiquement significative ?

#Test satistique de l'importance de la différence des salaires des deux groupes
t.test(salary ~ new_hire, data = pay)
## 
##  Welch Two Sample t-test
## 
## data:  salary by new_hire
## t = -2.3437, df = 685.16, p-value = 0.01938
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -4869.4242  -429.9199
## sample estimates:
##  mean in group No mean in group Yes 
##          73424.60          76074.28

La p-valeur est inférieure à 0,05 donc on accepte l’hypothèse selon laquelle la différence de salaire est satistiquement significative.

Les nouvelles recrues de cette entreprise ont un salire plus important que les employés actuels. Ceci peut constituer un problème majeur pour la bonne marche de l’entreprise.

Mais ATTENTION !!! Est-ce qu’on parviendrait à cette même conclusion si on considère les autres variables ?

Dans un premier temps, je calculerai le salaire moyen des nouveaux et des anciens employés dans chaque département et dans un second temps en prenant en compte le niveau d’emploi.

#Calcul du salaire moyen département par département
avg_salary_dept  <- pay %>%
  group_by(department, new_hire) %>%
  summarize(avg_salary = mean(salary))

avg_salary_dept
## # A tibble: 6 x 3
## # Groups:   department [3]
##   department  new_hire avg_salary
##   <fct>       <fct>         <dbl>
## 1 Engineering No           72544.
## 2 Engineering Yes          76303.
## 3 Finance     No           75333.
## 4 Finance     Yes          80219.
## 5 Sales       No           75054.
## 6 Sales       Yes          75002.
#Visulaisation des résultats
ggplot(avg_salary_dept, aes(new_hire, avg_salary)) +
  geom_col() +
  facet_wrap(~ department) +
  theme_minimal()

On remarque que le salaire moyen des nouvelles recrues est supérieur à celui des emmployés actuels pour tous les départements sauf pour le département VENTES où les salaires moyens sont quasiment égaux. Est-ce que ces différences sont statisqtiquement significatives ?

for (i in c("Engineering", "Finance", "Sales")) {
  #Sous-ensemble de données de chaque département
  dept_data <- pay %>%
  filter(department == i)
  #Afficahge du nom du département
  print(i)
  #Test statistique de l'importance de la différence des salaires des deux groupes pour chaque département
  print(t.test(salary ~ new_hire, data = dept_data))
}
## [1] "Engineering"
## 
##  Welch Two Sample t-test
## 
## data:  salary by new_hire
## t = -2.5979, df = 414.78, p-value = 0.009714
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -6602.7461  -914.6488
## sample estimates:
##  mean in group No mean in group Yes 
##          72543.82          76302.51 
## 
## [1] "Finance"
## 
##  Welch Two Sample t-test
## 
## data:  salary by new_hire
## t = -0.98444, df = 36.393, p-value = 0.3314
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -14946.96   5175.74
## sample estimates:
##  mean in group No mean in group Yes 
##          75333.33          80218.93 
## 
## [1] "Sales"
## 
##  Welch Two Sample t-test
## 
## data:  salary by new_hire
## t = 0.027465, df = 242.22, p-value = 0.9781
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -3726.725  3832.119
## sample estimates:
##  mean in group No mean in group Yes 
##          75054.23          75001.54

Pour le départment INGENIERIE, la p-valeur est inférieure à 0,05 donc on accepte l’hypothèse selon laquelle la différence des salaires moyens des deux groupes d’employés (nouveaux et actuels) est statistiquement significative (importante).

Pour les départements FINANCE et VENTES, les p-valeurs sont supérieures à 0,05 donc on rejette l’hypothèse on ne peut pas accepter l’hypothèse selon laquelle la différence des salaires moyens des deux groupes d’employés (nouveaux et actuels) est statistiquement significative (importante).

Les tests statistiques département par département montrent que la différence de salire entre les deux groupes d’employés est significiative seulement dans le département INGENIERIE.

Qu’en est t-il de cette différence de salire si on prend en compte le niveau d’emploi (salarié, horaire ou manager) ?

Avant de répondre à cette question, je vais vérifier si la composition du niveau d’emploi est la même pour les nouveaux employés que pour les employés actuels.

#Composition du niveau d'emploi
ggplot(pay, aes(new_hire, fill = job_level)) +
  geom_bar(position = "fill") +
  theme_minimal()

Les nouvelles recrues sont moins susceptibles d’être des employés horaires et plus susceptibles d’être des salariés ou des managers.

Est-ce que les nouveaux employés ont un salaire moyen plus élevé que les employés actuels lorsque le niveau d’emploi est pris en compte ?

pay %>%
  group_by(new_hire, job_level) %>%
  summarize(avg_salary = mean(salary)) %>%
  ggplot(aes(new_hire, avg_salary)) +
    geom_col() +
    facet_wrap(~ job_level) +
    theme_minimal()

Les barres ont quasiment les mêmes tailles. Cela soutient l’idée que le fait d’avoir omis précédemment la variable job_level (niveau d’emploi) est à l’origine de la différence de salire entre les nouveaux employés et les employés actuels.

La différence de salaire entre les nouveaux employés et les actuels semble disparaître quand on on prend en compte une autre variable (le niveau d’emploi). Le fait d’analyser une seule varibale est très souvent pas assez suffisant pour être confiant qu’il existe une différence significative entre les moyennes de deux groupes.

NOTES DE PERFORMANCE

La notation de la performance des employés est un aspect très important dans la gestion des ressources humaines. Les employés considérés comme hautement performants sont plus susceptibles de recevoir des promotions, augmentations de salaires, bonus, etc.

Les données sont souvent stocjées dans différents endroits et pour les étudier, il faut les joindre.

#Données sur la performance
performance_data <- read.csv("performance_data.csv")
glimpse(performance_data)
## Observations: 1,470
## Variables: 2
## $ employee_id <int> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, ...
## $ rating      <int> 4, 4, 4, 4, 2, 5, 3, 4, 3, 3, 2, 3, 3, 2, 4, 3, 3,...
#Données sur les RH
hr <- read.csv("hr_data.csv")
glimpse(hr)
## Observations: 1,470
## Variables: 4
## $ employee_id <int> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, ...
## $ department  <fct> Sales, Engineering, Engineering, Engineering, Engi...
## $ job_level   <fct> Salaried, Hourly, Hourly, Salaried, Hourly, Salari...
## $ gender      <fct> Female, Female, Female, Male, Male, Female, Female...

Chaque employé a son identifiant unique stocké dans la variable employee_id qui appartient aux deux dataframe. Donc je vais joindre la table performance_data à la table hr.

#Jointure des deux tables 
joined_data <- left_join(hr, performance_data, by = "employee_id")
glimpse(joined_data)
## Observations: 1,470
## Variables: 5
## $ employee_id <int> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, ...
## $ department  <fct> Sales, Engineering, Engineering, Engineering, Engi...
## $ job_level   <fct> Salaried, Hourly, Hourly, Salaried, Hourly, Salari...
## $ gender      <fct> Female, Female, Female, Male, Male, Female, Female...
## $ rating      <int> 4, 4, 4, 4, 2, 5, 3, 4, 3, 3, 2, 3, 3, 2, 4, 3, 3,...

Je vais claculer la performance moyenne par sexe.

#Performance moyenne par sexe
joined_data %>%
  group_by(gender) %>%
  summarize(avg_rating = mean(rating))
## # A tibble: 2 x 2
##   gender avg_rating
##   <fct>       <dbl>
## 1 Female       2.75
## 2 Male         2.92

Avec ces seuls chiffres, on ne peut pas conclure que les hommes sont mieux notés en terme de performance que les femmes.

Je vais considérer qu’un employé est hautement qualifié lorsque sa note est supérieure ou égale à 4 sur l’échelle des 5 points.

#Création de la variable 'high_performer'
performance <- joined_data %>%
  mutate(high_performer = ifelse(rating < 4, 0, 1)) 

#Test si l'un des 2 genres est plus susceptible d'être hautement performant
chisq.test(performance$high_performer, performance$gender)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  performance$high_performer and performance$gender
## X-squared = 22.229, df = 1, p-value = 2.42e-06
#Visulaisation de la variable 'high_performer' selon le genre
ggplot(performance, aes(gender, fill = factor(high_performer))) +
  geom_bar(position = "fill")

#Visulaisation de la variable 'rating' selon le genre
ggplot(performance, aes(gender, fill = factor(rating))) +
  geom_bar(position = "fill")

La p-valeur est inférieure à 0,05 donc l’un des 2 genres est significativement plus hautement performant que l’autre. De plus, les figurent confirment que ce sont les hommes qui sont plus hautement performants que les femmes. Mais attention !! Il peut avoir un biais. Donc il faudra explorer d’autres variables indépendantes comme le niveau d’emploi (job_level). En effet la répartition des notes de performance n’est pas toujours la même à différents niveaux d’emploi.

#Distribution du niveau d'emploi par sexe
ggplot(performance, aes(gender, fill = job_level)) +
  geom_bar(position = "fill") +
  theme_minimal()

On remarque que les hommes et les femmes n’ont pas les mêmes distributions de niveau d’emploi. Les hommes sont plus susceptibles d’être des salariés et des Manager que les femmes. Les femmes sont plus susceptibles d’être des employés horaires que les hommes.

Il faudra tester si cette différence observée est statistiquement significative.

#Test si les hommes et les femmes ont différentes distributions de niveau d'emploi
chisq.test(performance$gender, performance$job_level)
## 
##  Pearson's Chi-squared test
## 
## data:  performance$gender and performance$job_level
## X-squared = 44.506, df = 2, p-value = 2.166e-10

Le test statistique confirme bel et bien que la distribution du niveau d’emploi des hommes et différente de celle des femmes.

#Distribution de 'high_performer' par sexe et pour chaque niveau d'emploi
ggplot(performance, aes(factor(high_performer), fill = gender)) +
  geom_bar(position = "fill") +
  facet_wrap(~ job_level) +
  theme_minimal()

Cette figure suggère que même lorsque les niveaux d’emploi sont pris en compte, les femmes sont moins susceptibles d’être qualifiées de ‘hautement performantes’ que les hommes dans cette entreprise. De ce fait, quelle recommandation le data analyst peut t-il faire ?

Avant de recommander au responsable RH de recruter plus d’hommes que de femmes, il faudra explorer d’autres variables explicatives pour s’assurer qu’il n’y a pas de biais. De toute façon, il serait inadmissible d’avoir de discrimination selon le genre dans le management des effectifs d’une entreprise. Hommes et Femmes ont les mêmes droits et doivent donc être traités de la même façon.

AMELIORATION DE LA SECURITE DES EMPLOYES

La sécurité est un paramètre très important dans une entreprise. Il est naturel qu’un employé qui ne se sent pas en sécurité sera moins efficace dans son travail ce qui impactera négativement l’entreprise.

Voici le scénario qui sous-tend cette partie : le responsable RH estime que les accidents du travail ont augmenté l’année dernière sur les sites de production. Le Data analyst va donc vérifier si cela est vrai à partir des données et si c’est le cas, il va chercher ce qui pourrait être à l’origine de l’augmentation.

#Chargement des données HR
hr_data <- read.csv("hr_data_2.csv")
glimpse(hr_data)
## Observations: 2,940
## Variables: 4
## $ year           <int> 2016, 2017, 2016, 2017, 2016, 2017, 2016, 2017,...
## $ employee_id    <int> 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8, 8, 10, 10, 11,...
## $ location       <fct> Northwood, Northwood, East Valley, East Valley,...
## $ overtime_hours <int> 14, 8, 8, 11, 4, 2, 0, 17, 21, 9, 5, 23, 4, 2, ...
#Chargement des données sur les accidents
accident_data <- read.csv("accident_data.csv")
glimpse(accident_data)
## Observations: 302
## Variables: 3
## $ year          <int> 2017, 2017, 2017, 2017, 2017, 2016, 2016, 2016, ...
## $ employee_id   <int> 1, 4, 11, 19, 22, 23, 24, 38, 41, 47, 54, 61, 75...
## $ accident_type <fct> Mild, Mild, Mild, Mild, Mild, Mild, Mild, Modera...
#Jointure des deux dataframe
hr_accident <- left_join(hr_data, accident_data, by = c("employee_id", "year"))
glimpse(hr_accident)
## Observations: 2,940
## Variables: 5
## $ year           <int> 2016, 2017, 2016, 2017, 2016, 2017, 2016, 2017,...
## $ employee_id    <int> 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8, 8, 10, 10, 11,...
## $ location       <fct> Northwood, Northwood, East Valley, East Valley,...
## $ overtime_hours <int> 14, 8, 8, 11, 4, 2, 0, 17, 21, 9, 5, 23, 4, 2, ...
## $ accident_type  <fct> NA, Mild, NA, NA, NA, Mild, NA, NA, NA, NA, NA,...
unique(hr_accident$accident_type)
## [1] <NA>     Mild     Moderate Severe  
## Levels: Mild Moderate Severe

On a différents types d’accidents. NA veut provient de la jointure des deux tables et signifie tout simplement que cet employé n’a pas eu d’accident. Puisqu’un accident qu’il soit modéré ou pas reste un accident, je vais créer une variable had_accident qui est égale à 0 si accident_type prend la valeur ‘NA’ et 1 dans le cas contraire.

#Création de la variable 'had_accident'
hr_accident <- hr_accident %>%
  mutate(had_accident = ifelse(is.na(accident_type), 0, 1))

#Calcul du taux d'accident pour chaque année
hr_accident %>%
  group_by(year) %>%
  summarize(accident_rate = mean(had_accident))
## # A tibble: 2 x 2
##    year accident_rate
##   <int>         <dbl>
## 1  2016        0.0850
## 2  2017        0.120

Le taux d’accident en 2017 est supérieur à celui de 2016. Est-ce que cette différence est statistiquement significative ?

#Test statistique
chisq.test(hr_accident$had_accident, hr_accident$year)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  hr_accident$had_accident and hr_accident$year
## X-squared = 9.5986, df = 1, p-value = 0.001947

La p-valeur est inférieure à 0,05 donc la différence observée dans le taux d’accident entre 2016 et 2017 est significative.

Quelle est le site de production qui a le plus grand taux d’accident ?

#Taux d'accident par localisation
hr_accident %>%
  group_by(location) %>%
  summarize(accident_rate = mean(had_accident)) %>%
  arrange(desc(accident_rate))
## # A tibble: 4 x 2
##   location    accident_rate
##   <fct>               <dbl>
## 1 East Valley        0.128 
## 2 Southfield         0.103 
## 3 West River         0.0961
## 4 Northwood          0.0831

Le site ayant le plus grand taux d’accident se trouve à East Valley. L’endroit avec le taux d’accident le plus élevé est un bon point de départ mais il sera plus productif de se concentrer sur l’endroit où le taux d’accident a le plus augmenté de 2016 à 2017.

#Taux d'accident par localisation et par année
rate_loc_yr  <- hr_accident %>%
  group_by(location, year) %>%
  summarize(accident_rate = mean(had_accident))

rate_loc_yr
## # A tibble: 8 x 3
## # Groups:   location [4]
##   location     year accident_rate
##   <fct>       <int>         <dbl>
## 1 East Valley  2016        0.113 
## 2 East Valley  2017        0.143 
## 3 Northwood    2016        0.0644
## 4 Northwood    2017        0.102 
## 5 Southfield   2016        0.0764
## 6 Southfield   2017        0.130 
## 7 West River   2016        0.0824
## 8 West River   2017        0.110
#Visualisation du résultat
ggplot(rate_loc_yr, aes(factor(year), accident_rate)) +
  geom_col() +
  facet_wrap(~ location) +
  theme_minimal()

East Valley est bien le site où on a enregistré le plus d’accidents pendant les deux années, mais c’est à Southfield que les accidents ont le plus augmenté entre 2016 et 2017.

Qu’est-ce qui s’est donc passé dans le site de Southfield ? Je vais commencer par supposer que les employés qui font plus d’heures supplémentaires sont plus susceptibles de faire des erreurs qui conduisent à des accidents. Je vais donc comparer les heures supplémentaires moyennes travaillées chaque année.

#Sous-ensemble de données pour Southfield
southfield_data <- hr_accident %>%
  filter(location == "Southfield")

#Temps supplémentaire moyen travaillé chaque année
southfield_data %>%
  group_by(year) %>%
  summarize(avg_overtime_hours = mean(overtime_hours))
## # A tibble: 2 x 2
##    year avg_overtime_hours
##   <int>              <dbl>
## 1  2016               8.67
## 2  2017               9.97

Selon ces chiffres, les employés ont en moyenne réalisé plus d’heures supplémentaires en 2017 comparé à 2016. Cette différence est t-elle significative ?

#Test statistique
t.test(overtime_hours ~ year, data = southfield_data) 
## 
##  Welch Two Sample t-test
## 
## data:  overtime_hours by year
## t = -1.6043, df = 595.46, p-value = 0.1092
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -2.904043  0.292747
## sample estimates:
## mean in group 2016 mean in group 2017 
##           8.667774           9.973422

La p-valeur est supérieure à 0,05 donc il n’y a pas de différence statistiquement significative entre les heures supplémentaires faites en 2016 et celles faites en 2017. Donc les heures supplémentaires ne peuvent expliqué l’augmentation d’accidents à Southfield.

Étant donné que les heures supplémentaires n’ont pas eu de changement significatif de 2016 à 2017, quelle autre variable peut expliquer l’augmentation d’accidents à Southfield. Serait-ce l’engagement du personnel ?

#Importation des données
survey_data_2 <- read.csv("survey_data_2.csv")
glimpse(survey_data_2)
## Observations: 2,940
## Variables: 3
## $ year        <int> 2016, 2017, 2016, 2017, 2016, 2017, 2016, 2017, 20...
## $ employee_id <int> 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8, 8, 10, 10, 11, 11...
## $ engagement  <int> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 1, 4,...
#Jointure
total_data <- left_join(hr_accident, survey_data_2, by = c("employee_id", "year"))
glimpse(total_data)
## Observations: 2,940
## Variables: 7
## $ year           <int> 2016, 2017, 2016, 2017, 2016, 2017, 2016, 2017,...
## $ employee_id    <int> 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8, 8, 10, 10, 11,...
## $ location       <fct> Northwood, Northwood, East Valley, East Valley,...
## $ overtime_hours <int> 14, 8, 8, 11, 4, 2, 0, 17, 21, 9, 5, 23, 4, 2, ...
## $ accident_type  <fct> NA, Mild, NA, NA, NA, Mild, NA, NA, NA, NA, NA,...
## $ had_accident   <dbl> 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,...
## $ engagement     <int> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 1,...

Je vais considérer su’un employé n’est pas engagé si son score d’engagement est inférieur ou égal à 2.

#Création de la variable 'disengaged'
total_data <- total_data %>%
  mutate(disengaged = ifelse(engagement > 2, 0, 1), year = factor(year))

#Différence d'engagement par année à Southfield
total_data %>%
  filter(location == "Southfield") %>%
  ggplot(aes(year, fill = factor(disengaged))) +
    geom_bar(position = "fill") +
  theme_minimal()

La proportion d’employés désengagés a augmenté entre 2016 et 2017 ? Est-ce que cette différence est statistiquement significative ?

#Test statistique
southfield <- total_data %>%
  filter(location == "Southfield")

chisq.test(southfield$year, southfield$disengaged) 
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  southfield$year and southfield$disengaged
## X-squared = 7.1906, df = 1, p-value = 0.007329

La p-valeur est inférieure à 0,05 donc la différence de désengagement observée entre les deux années à Southfield est bel et bien significative.

Jusque là, j’ai constaté une différence de désengagement entre les deux années à Southfield et aucune différence dans les heures supplémentaires travaillées. Maintenant, je vais également vérifier les autres emplacements pour m’assurer que ces emplacements n’ont pas également connu un grand changement dans le désengagement. Si le taux de désengagement a augmenté aux autres endroits où le taux d’accident n’a pas beaucoup changé, alors les taux de désengagement et d’accident sont liés.

#Sous-ensemble de données constitué des autres emplacements en dehors de Southfield
other_locs <- total_data %>%
  filter(location != "Southfield")

#Différence d'engagement par année dans les autres sites
other_locs %>%
  ggplot(aes(year, fill = factor(disengaged))) +
    geom_bar(position = "fill") +
    facet_wrap(~ location) +
    theme_minimal()

Il n’y a pas de différence de désengagement de 2016 à 2017 dans les autres sites.

#Test satistique
chisq.test(other_locs$year, other_locs$disengaged)  
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  other_locs$year and other_locs$disengaged
## X-squared = 0.0023091, df = 1, p-value = 0.9617

La p-valeur est supériure à 0,05 cela confirmequ’il n’y a pas de différence significative de désengagement entre 2016 et 2017 sur tous les autres sites.

#Différence d'heures supplémentaires par année dans les autres sites
other_locs %>%
  ggplot(aes(year, overtime_hours)) +
    geom_col() +
    facet_wrap(~ location) +
    theme_minimal()

#Test statistique
t.test(overtime_hours ~ year, data = other_locs)
## 
##  Welch Two Sample t-test
## 
## data:  overtime_hours by year
## t = -0.48267, df = 2320.3, p-value = 0.6294
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -0.9961022  0.6026035
## sample estimates:
## mean in group 2016 mean in group 2017 
##           9.278015           9.474765

La figure ainsi que le statistique montrent qu’il n’y a a pas de différence d’heures supplémentaires travaillées entre les 2 années sur tous les autres sites en dehors de Southfield.

Pour revenir au scénario de départ, le data anlyst peut répondre au responsable RH que c’est le site de Southfield qui a connu la plus forte augmentation du taux d’accidents entre 2016 et 2017 et que le désengagement des employés peut expliquer cela.

Pour finir, je vais utiliser une régression logistique pour mieux comprendre pourquoi le taux d’accidents a augmenté à Southfield ? Exprimé d’une autre manière, y a-t-il une variable dans l’ensemble de données qui, ajoutée à une régression multiple, peut expliquer la différence de taux d’accidents entre les deux années ?

#Modèle de régression logistique pour expliquer la variable 'had_accident'
regression <- glm(had_accident ~ year + disengaged + overtime_hours, family = "binomial", data = southfield)

broom::tidy(regression)
## # A tibble: 4 x 5
##   term            estimate std.error statistic  p.value
##   <chr>              <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)    -2.92        0.281   -10.4    2.59e-25
## 2 year2017        0.440       0.285     1.55   1.22e- 1
## 3 disengaged      1.44        0.278     5.18   2.19e- 7
## 4 overtime_hours -0.000314    0.0139   -0.0226 9.82e- 1

Avec les valeurs des p-value, le modèle confirme un lien statistiquement significatif entre le désengagement des employés et les taux d’accidents à Southfield. Le désengagement des employés peut expliquer la différence de taux d’accidents entre les années. Le data analyst peut donc recommander au responsable RH de trouver des stratégies pour augmenter l’engagement des employés ce qui ne serait que bénéfique pour l’entreprise.

CONCLUSION

Dans ce document, j’ai montré comment l’analyse des données des employés d’une entreprise peut aider les Gestionnaires de Ressources Humaines à prendre de bonnes décisions en ce qui concerne cinq différentes problématiques que sont : Recruitement, Engagement du personnel, Salaires, Performance et Sécurité des employés. Grâce à cette analyse, le data analyst peu faire des recommandations précises au responsable RH pour une bonne gestion des effectifs et donc pour une amélioration des résultats de l’entreprise.