emilienschultz / pyshs-bib

MIT License
9 stars 4 forks source link

Utiliser la fonction native de pandas pour gérer les variables catégorielles ordinales #40

Open jeanbaptisteb opened 1 year ago

jeanbaptisteb commented 1 year ago

Les exemples donnés dans la documentation (Exemple PySHS.ipynb) ne tirent pas partie de la fonction native de pandas qui permet de donner un ordre aux modalités d'une variable catégorielle.

Plutôt qu'ajouter un numéro devant les variables, on peut les trier explicitement selon un ordre qui sera pris en compte lorsqu'on fait un tableau croisé. Pour reprendre un des exemples de la doc, en l'adaptant :

data = pd.read_spss('bdd2011.sav')
data["genre"] = data["rs1"].astype("O")
reco = {'Assez':"Assez", 'Peu':"Peu ou pas du tout", 
        'Pas du tout':"Peu ou pas du tout", 'Beaucoup':"Beaucoup",
       "Sans réponse":"Sans réponse"} #ici, plus besoin de mettre des numéros devant les noms des variables
data["interetscience"] = data['q2'].astype("O").fillna("Sans réponse").replace(reco)
data["interetscience"] = pd.Categorical(
    data['interetscience'], 
    ordered=True, #ici on indique qu'on veut définir un ordre pour les catégories
    categories=["Peu ou pas du tout", 
                'Assez', 'Beaucoup',"Sans réponse" ]) #et ici on liste toutes les catégories, dans l'ordre qu'on souhaite leur donner

Ce qui donne, quand on fait un tableau croisé avec la fonction pandas.crosstab : pd.crosstab(data["interetscience"], data["genre"])

interetscience Femme Homme
Peu ou pas du tout 270 196
Assez 205 208
Beaucoup 59 88
Sans réponse 0 1

Les modalités de interetscience sont bien triées selon l'ordre qu'on leur a donné, et non selon l'ordre alphabétique. Voir aussi la documentation officielle de pandas pour plus de détails sur cette fonction pour les variables ordinales.

Je ne sais pas si prendre cela en compte nécessite simplement une mise à jour de la documentation, ou si ça nécessite aussi de revoir la logique du code de la librairie, je n'ai pas fouillé.

Intérêt de cette méthode :

  1. ça enlève le besoin d'ajouter manuellement un numéro en face de chaque modalité, et donc réduit le risque d'erreur humaine,
  2. et d'autre part cela permet de faire des régressions ordinales avec statsmodels, qui peuvent être plus appropriées/plus précises qu'un test du chi2 dans le cas où la variable à expliquer est ordinale. Exemple avec interetscienceen variable à expliquer et la variable genre comme variable explicative :
from statsmodels.miscmodels.ordinal_model import OrderedModel
mod_log = OrderedModel.from_formula('interetscience ~C(genre)',data=data,  distr='logit')
res_log = mod_log.fit(method='bfgs', disp=False)
res_log.summary()

Résultat:

"""
                             OrderedModel Results                             
==============================================================================
Dep. Variable:         interetscience   Log-Likelihood:                -1029.3
Model:                   OrderedModel   AIC:                             2067.
Method:            Maximum Likelihood   BIC:                             2086.
Date:                Sun, 05 Feb 2023                                         
Time:                        18:59:05                                         
No. Observations:                1027                                         
Df Residuals:                    1023                                         
Df Model:                           4                                         
============================================================================================
                               coef    std err          z      P>|z|      [0.025      0.975]
--------------------------------------------------------------------------------------------
C(genre)[T.Homme]            0.4713      0.119      3.962      0.000       0.238       0.705
Peu ou pas du tout/Assez     0.0363      0.084      0.430      0.667      -0.129       0.202
Assez/Beaucoup               0.6878      0.045     15.453      0.000       0.601       0.775
Beaucoup/Sans réponse        1.6412      0.193      8.490      0.000       1.262       2.020
============================================================================================
"""

Voir la documentation de statsmodels pour plus d'informations sur la régression ordinale (mais ça sort un peu du cadre de ce ticket).

emilienschultz commented 1 year ago

Tout à fait d'accord sur l'idée que s'appuyer sur les objets Pandas catégoriels pourraient faciliter ce traitement. Je vais voir à refactoriser le code autour de ça. Je l'avais pas initialement fait car certaines erreurs spécifiques peuvent arriver sur la gestion d'objet Categories qui sont peu utilisés par ceux qui débutent avec Pandas (ou du moins était-ce mon impression). Mais ça permettrait de renforcer la stabilité du code je pense, donc c'est l'occasion.