top of page

Sélection du candidat : validation et prévision avec la régression linéaire



Sujet éminemment important, la sélection des employés comprend les processus et les systèmes utilisés pour déterminer qui, parmi les candidats, devrait se voir proposer un poste au sein de l'organisation. Ce sujet central l'est encore plus en France car l'employé nouvellement intégré est très protégé par la lois. En effet, la séparation de l'employé par l'entreprise ne peut se faire que sous certaine conditions très fortes. Il est donc essentiel de disposer des bons outils pour décider qui sera le/la meilleur(re) candidat(e). Ces outils de sélection comprennent les entretiens, les tests de personnalité, les tests d'aptitude cognitive, les formulaires de candidature, les recommandations, etc...


Le problème est que ces outils sont rarement évaluer puis valider par quelque manière que se soit, théoriquement et/ou empiriquement. Le principe est regarder si un lien existe entre ces outils (qu'on appel aussi "variable") à un critère / indicateur (en générale la performance mais cela peut être autre chose, à vous de voir). Ce lien peut être caractérisé de plusieurs manières (coefficient de corrélation, régression, etc...). Pour ce cas, je vous montrerais la méthode régressive (simple) qui peut être étendue à plusieurs variables (régression multiple).



Sommaire :


 

Un mot sur le jeu de données


La table contient 4 variables pour 163 individus : EmployeeID, Conscientiousness, Interview et Performance. On suppose que ces données aient été collectées dans le cadre d'une étude cherchant à estimer la validité des critères liés aux outils de sélection (par exemple, procédures, évaluations, tests) ; cela signifie que les outils de sélection (c'est-à-dire, les variables "Conscientiousness" et "Interview") ont été administrés aux titulaires de postes et que la mesure du critère ("Performance") a été administrée à peu près au même moment.


Pour commencer, "EmployeeID" est la variable d'identification unique. La variable "Conscientiousness" contient les scores d'un test de personnalité conçu pour évaluer le concept psychologique de conscience ; les scores potentiels sur cette variable pourraient varier de 1 (faible conscience) à 5 (forte conscience). La variable "Interview" contient les scores d'un entretien structuré conçu pour évaluer le niveau de connaissances et de compétences en matière de service à la clientèle des personnes interviewées ; les scores potentiels sur cette variable pourraient varier de 1 (mauvais service à la clientèle) à 5 (excellent service à la clientèle).


Enfin, le critère de cette étude de validation est la variable "Performance", qui contient les évaluations de la performance professionnelle des titulaires de postes ; les scores potentiels sur cette variable pourraient varier de 1 (ne répond pas aux normes de performance) à 5 (dépasse les normes de performance).



Analyse préliminaire

# Import les package
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import statsmodels.api as sm
from pandas.plotting import lag_plot

# Charge les données
df = pd.read_csv(".../SelectionData.csv")

df.info() # Check la dataframe

Résultats :

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 163 entries, 0 to 162
Data columns (total 4 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   EmployeeID         163 non-null    int64  
 1   Conscientiousness  163 non-null    float64
 2   Interview          163 non-null    float64
 3   Performance        163 non-null    float64
dtypes: float64(3), int64(1)
memory usage: 5.2 KB

La base de données ne contient pas de valeurs nulles et le type donnée colle bien pour chaque variable, on procède à la suite de l'analyse.

Disclaimer : dans la pratique, les jeux de données ne sont quasiment jamais propre. L'analyste passe au moins 60% à 70% de son temps à préparer le jeu avant une quelconque restitution d'analyse.


# Visualisation 
sns.scatterplot(data=df, x="Conscientiousness", y="Performance", 
                palette='deep', s=50, edgecolor='k', alpha=0.5)
sns.regplot(data=df, x="Conscientiousness", y="Performance", 
            scatter=False, color='black', line_kws={'lw': 2})

plt.title("Nuage de point : Conscientiousness vs Performance")
plt.xlabel('Conscientiousness')
plt.ylabel('Performance')
plt.show()

En traçant le nuage de point entre les variables "Performance" (axe y) et "Conscientiousness" (axe x) ainsi qu'une rapide droite de régression (en noire). On voit un certain lien entre les deux. Il existe néanmoins une certaine disparité autour de la droite de régression. La modélisation avec un modèle MCO (moindre carré ordinaire) va nous permettre de vérifié et caractérisé en détail ce lien.



Modélisation


Y = df['Performance']
X = sm.add_constant(df['Conscientiousness'])
model = sm.OLS(Y,X).fit()

fitted_values = model.fittedvalues
residuals = model.resid

Petit check : un modèle de régression linéaire doit respecter quatre hypothèses. Ce n'est pas une règle absolue, c'est hypothèse sont rarement vérifié dans la réalité mais plus elle sont validé, plus on peut être confiant dans les résultats de notre régression. Les quatre hypothèse :

  1. La relation entre les deux variables numériques continues est réellement de forme linéaire (au moins grossièrement) : vérifié pour notre cas

  2. Les résidus de la régression linéaire sont indépendants. : vérifié aussi (à la suite)

  3. Les résidus de la régression linéaire sont distribués selon une loi Normale de moyenne 0 (au moins grossièrement) : vérifié aussi (à la suite)

  4. Les résidus de la régression linéaire sont distribués de façon homogène : non vérifié


# Graphique de la distribution des résidus
plt.figure(figsize=(10, 6))
sns.displot(residuals, kde=True, color='blue', bins=20)
plt.title('Distribution des résidus')
plt.xlabel('Résidus')
plt.ylabel('Fréquence')
plt.show()

Comme vous pouvez le voir, nos résidus montrent une distribution principalement normale, ce qui est excellent et conforme à l'hypothèse. On vérifie aussi que les résidus sont linéairement indépedant :


residuals_series = pd.Series(residuals)
lag_plot(residuals_series, lag=1, c='blue', alpha=0.6, marker='o', s=50, edgecolor='k')
plt.title('Lag Plot des résidus (lag=1)')
plt.xlabel('Résidus au temps t')
plt.ylabel('Résidus au temps t+1')
plt.show()

Nous avons trois hypothèses sur quatre, on peut donc être confiants dans l'interprétation des résultats de notre modèle.


Résultat de la table de régression :

print(model.summary())
                            OLS Regression Results                            
===================================================================
Dep. Variable:       Performance   R-squared:                0.220
Model:                       OLS   Adj. R-squared:           0.215
Method:            Least Squares   F-statistic:              45.32
Date:           Sun, 16 Apr 2023   Prob (F-statistic):    2.80e-10
Time:                   16:20:34   Log-Likelihood:         -204.86
No. Observations:            163  AIC:                       413.7
Df Residuals:                161  BIC:                       419.9
Df Model:                      1                                         
Covariance Type:       nonrobust                                         
===================================================================
                 coef    std err        t     P>|t| [0.025 0.975]
-------------------------------------------------------------------
const              0.7798  0.333    2.343    0.020   0.123  1.437
Conscientiousness  0.6313  0.094    6.732    0.000   0.446  0.817
===================================================================
Omnibus:                  1.462   Durbin-Watson:         2.444
Prob(Omnibus):            0.481   Jarque-Bera (JB):      1.523
Skew:                    -0.178   Prob(JB):              0.467
Kurtosis:                 2.688   Cond. No.              19.0
===================================================================

Le coefficient de régression associé à la variable prédictive (Conscientiousness) par rapport à la variable de résultat (Performance) est souvent d'un intérêt substantiel. Ici, nous voyons que le coefficient de régression non standardisé pour "Conscientiousness" est de 0,6313 et que sa valeur p associée est inférieure à 0,001. Nous rejetons l'hypothèse nulle selon laquelle le coefficient de régression est égal à zéro, ce qui signifie que le coefficient de régression est statistiquement significativement différent de zéro. La variable "Conscientiousness" est donc valide !


Nous l'avons fait avec une variable, mais nous pouvons allez plus vite en suivant le même principe avec une régression linéaire multiple. De plus, et une fois les variables validées, la régression nous permet de faire des prévisions sur les performances futurs des candidats et ainsi, améliorer la sélection.



Prévoir la performance avec une variable


Nous avons calculé les paramètres du modèle suivant :

Y = df['Performance']
X = sm.add_constant(df['Conscientiousness'])
model = sm.OLS(Y,X).fit()

print(model.params)
# Résultats :
const                0.779808 
Conscientiousness    0.631335 
dtype: float64

On peut donc écrire l'équation suivante :


Performance = 0.7798 + 0.6313 * (Conscientiousness)


Nous pouvons interpréter le coefficient de régression comme suit : Pour chaque augmentation d'une unité de la variable explicative (Conscience professionnelle), la variable résultat (Performance) augmente de 0,6313 unités.


Notez que l'équation ci-dessus est simplement l'équation d'une droite :


Performance(prévue) = 0.7798 + (0.6313∗X)

Si nous insérons, par exemple, la valeur 3 comme valeur observée de Conscience professionnelle, alors nous obtenons un score de critère prédit de 2,673, comme indiqué ci-dessous :

2,673 = 0.7798 + (0.6313∗3)



Prévision avec un nouveau jeu de données


On commence par enregistré nos paramètres dans des variables :

const = model.params[0]
coef1 = model.params[1]

On charge le nouveau jeu de données :

new_df = pd.read_csv(".../ApplicantData.csv")
new_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 3 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   ApplicantID        10 non-null     object 
 1   Conscientiousness  10 non-null     float64
 2   Interview          10 non-null     float64
dtypes: float64(2), object(1)
memory usage: 368.0+ bytes

Notez que ce jeu ne contient pas la performance des candidats. Comme nous avons calculé les paramètres du modèle régressif et que nous avons la variable "Conscientiousness". Nous pouvons prévoir la performance de chaque candidat. Il suffit de créer une nouvelle colonne qui est égale à l'équation que nous avons écrite plus en haut :


new_df["Prev_performance"] = const + coef1 * \     new_df["Conscientiousness"]

La colonne "Prev_performance" correspond donc au résultat de l'équation :


Performance = 0.7798 + 0.6313 * (Conscientiousness)


Il ne nous reste plus qu'à sortir les valeurs trier par ordre décroissant :

new_df.sort_values(by=['Prev_performance'], ascending=False)

Et voilà ! Bon, encore une fois, c'est un cas extrêmement simple. Dans la réalité, il faudra passer beaucoup de temps à pré-traiter le jeu de données et à sélectionner les variables pour que votre modèle ai un pouvoir prédictif puissant.



Votre serviteur,

Thomas


Comentarios


bottom of page