Interpréter les statistiques avec R et Python

Intro à marginaleffects

vincent.arel-bundock@umontreal.ca

Présentation:

https://arelbundock.com/precrisa.html

Code pour l’atelier:

https://arelbundock.com/precrisa.R

Données:

https://arelbundock.com/hiv.csv

Qui suis-je?

  • Science Politique, UdeM
  • Associate Editor R Journal
  • Librairies R et Python depuis 2009:
    • marginaleffects
    • modelsummary
    • tinytable
    • countrycode
    • etc.

tinytable

modelsummary

marginaleffects.com

3 problèmes

  1. Terminologie
    • Disciplines
    • Incohérences
    • Contradictions
  2. R est incohérent
    • Librairies
    • Syntaxe
    • Modèles
    • Objets
  3. Statistiques = Difficile

Statistiques = Difficile

Difficile

\[ P_{X=1}=Pr(Y=1|X=1) \]

Peu de nuances:

  • Grosse probabilité \(\approx 1\)
  • Petite probabilité \(\approx 0\)
  • Entre les deux: 50/50

Kahneman & Tversky (1979), Nickerson (2004), etc.

Ultra difficile

Odds (chances):

\[ \frac{P_{X=1}}{1 - P_{X=1}} \]

Difficile

\[ P_{X=1}=Pr(Y=1|X=1) \]

Peu de nuances:

  • Grosse probabilité \(\approx 1\)
  • Petite probabilité \(\approx 0\)
  • Entre les deux: 50/50

Kahneman & Tversky (1979), Nickerson (2004), etc.

Impossible

Odds ratio:

\[ \frac{P_{X=1}}{1 - P_{X=1}} / \frac{P_{X=0}}{1 - P_{X=0}} \]

Exemple: Thornton (2008)

Demand for, and Impact of, Learning HIV Status. American Economic Review.

RCT dans les zones rurales du Malawi

  • Tests gratuits pour le VIH à la maison.
  • Traitement aléatoire:
    • 2$ pour récupérer le résultat du test à la clinique
  • Résultats:
    • \(Pr(Result=1)\) est doublée par le traitement
    • Apprendre un résultat positif augmente la demande de préservatifs

Exemple: Thornton (2008)

library(marginaleffects)

dat <- read.csv("http://arelbundock.com/hiv.csv")

mod <- glm(
  result ~ incentive + age + distance,  
  data = dat, 
  family = binomial)
$ → HIV test
(1)
(Intercept) -1.297
(0.176)
incentive 2.005
(0.100)
age 0.010
(0.003)
distance [Medium] 0.193
(0.132)
distance [Short] 0.484
(0.126)
Num.Obs. 2825
AIC 3041.4
BIC 3071.1
Log.Lik. -1515.693
F 103.281
RMSE 0.42

Statistiques = Difficiles

\(P_{X=1}\)=Difficile

Impossible:

\[\ln \left[ \left (\frac{P_{X=1}}{1-P_{X=1}}\right ) / \left (\frac{P_{X=0}}{1-P_{X=0}} \right ) \right]\]

  • Signe et signification = insuffisant
  • Interactions, splines, logit multinomial ou XGBoost?

¯\_(ツ)_/¯

$ → HIV test
(1)
(Intercept) -1.297
(0.176)
incentive 2.005
(0.100)
age 0.010
(0.003)
distance [Medium] 0.193
(0.132)
distance [Short] 0.484
(0.126)
Num.Obs. 2825
AIC 3041.4
BIC 3071.1
Log.Lik. -1515.693
F 103.281
RMSE 0.42

Solution: Transformations post-hoc

Focus: Quantités statistiques intuitives

  1. Ignorer les coefficients
  2. Transformer les coefficients
    • Prédictions
    • Comparer les prédictions contrefactuelles

Solution: marginaleffects 📦

Guichet unique pour l’interprétation des modèles statistiques et d’apprentissage automatique :

  • 100+ types de modèles
  • Interface simple et unifiée
  • Cadre conceptuel clair
  • Ultra flexible

Livre en ligne gratuit avec plus de 30 chapitres et études de cas :

https://marginaleffects.com

Solution: marginaleffects 📦

  • Bayes, GAM, Effets mixtes, etc.
  • Inférence causale
  • Variable dépendante catégorielle
  • Prédiction conforme
  • Tests d’équivalence
  • Interactions
  • Expériences conjointes
  • Inverse probability weighing
  • Apprentissage automatique
  • Matching
  • Régression multi-niveau
  • Post-stratification
  • Imputation multiple
  • etc.

Plan de match

  1. Tests d’hypothèse nulle
  2. Prédictions
  3. Comparaisons contrefactuelles

Tests d’hypothèse nulle

Peut-on rejeter l’hypothèse nulle qu’un coefficient – ou une fonction de coefficients – est égal à une valeur \(H_0\)?

hypotheses()

Coefficients logit:

b <- coef(mod)
b
   (Intercept)      incentive            age distanceMedium  distanceShort 
  -1.297433301    2.005157150    0.009644433    0.192817773    0.484206558 

Pouvons-nous rejetter \(H_0: Short-Medium=0\)?

b[5] - b[4]
[1] 0.2913888

hypotheses()

library(marginaleffects)
hypotheses(mod, "b5 - b4 = 0")

 Estimate Std. Error    z Pr(>|z|)   S  2.5 % 97.5 %
    0.291        0.1 2.91  0.00365 8.1 0.0949  0.488

Term: b5 - b4 = 0

Fonctions complexes ou non-linéaires

hypotheses(mod, "b1 - 10 * log(b2) = 0")

 Estimate Std. Error     z Pr(>|z|)     S 2.5 % 97.5 %
    -8.25      0.601 -13.7   <0.001 140.4 -9.43  -7.08

Term: b1 - 10 * log(b2) = 0
hypotheses(mod, "(b1 - b2) - (b3 - b4) = 0")

 Estimate Std. Error     z Pr(>|z|)     S 2.5 % 97.5 %
    -3.12      0.222 -14.1   <0.001 146.9 -3.55  -2.68

Term: (b1 - b2) - (b3 - b4) = 0

Pertinent? Probablement pas. Flexible? Oui!

Même mécanisme dans toutes les fonctions marginaleffects

Applicable pour comparer toutes les quantités estimées par la librairie:

  • Prédictions (fitted values)
  • Comparisons contrefactuelles
    • Risk difference
    • Risk ratio
    • Average treatment effects (on the treated)
  • Pentes (dérivées partielles, effets marginaux)

À vous!

https://arelbundock.com/precrisa.R

Predictions

Définition: Prédiction

Résultat prédit par un modèle sur une échelle spécifiée pour une combinaison donnée de valeurs des variables prédictrices.

“fitted values”

predictions(), avg_predictions(), plot_predictions()

Régression logistique en 2 étapes

  1. Spécifier une équation linéaire : l’échelle de “liaison” (“linkscale)

\[\beta_1 + \beta_2 X + \beta_3 Z\]

  1. Utiliser une fonction pour faire correspondre l’équation linéaire à l’échelle de réponse : \([0,1]\)

\[P(Y=1) = \phi (\beta_1 + \beta_2 X + \beta_3 Z),\]

\(\phi\) est la fonction logistique :

\[P(Y=1) = \frac{1}{1 + e^{-(\beta_1 + \beta_2 X + \beta_3 Z)}}\]

Prédiction : Incitatif, 30 ans, Short distance

\[ P(Result=1) = \phi (\beta_1 + \beta_2 Incentive + \beta_3 Age + \beta_4 Medium + \beta_5 Short)\\ \]

Prédiction (fitted value):

\[ \widehat{P(Result=1)} = \phi (\hat{\beta}_1 + \hat{\beta}_2 \cdot 1 + \hat{\beta}_3 \cdot 30 + \hat{\beta}_4 \cdot 0 + \hat{\beta}_5 \cdot 1), \]

b <- coef(mod)
plogis(b[1] + b[2] * 1 + b[3] * 30 + b[4] * 0 + b[5] * 1)
[1] 0.8147633

predictions() et datagrid()

predictions(mod, newdata = datagrid(
    incentive = 1,
    age = 30,
    distance = "Short")
)

 incentive age distance Estimate Pr(>|z|)     S 2.5 % 97.5 %
         1  30    Short    0.815   <0.001 312.9 0.793  0.835

Plus qu’un individu à la fois

  • Vecteurs ou fonctions qui renvoient des vecteurs
  • Les variables non-définies sont maintenues à leurs moyennes ou modes
predictions(mod, newdata = datagrid(
  age = c(20, 30),
  incentive = unique)
)

 age incentive Estimate Pr(>|z|)     S 2.5 % 97.5 %
  20         1    0.800   <0.001 209.4 0.773  0.824
  20         0    0.350   <0.001  29.3 0.305  0.397
  30         1    0.815   <0.001 312.9 0.793  0.835
  30         0    0.372   <0.001  25.0 0.330  0.416

Prédictions pour tous les individus observés

predictions(mod)

 Estimate Pr(>|z|)     S 2.5 % 97.5 %
    0.818   <0.001 324.3 0.796  0.838
    0.798   <0.001 198.3 0.770  0.823
    0.852   <0.001 179.6 0.822  0.878
    0.795   <0.001 177.1 0.766  0.822
    0.853   <0.001 173.6 0.823  0.879
--- 2815 rows omitted. See ?avg_predictions and ?print.marginaleffects --- 
    0.729   <0.001 60.7 0.684  0.769
    0.315   <0.001 22.4 0.256  0.381
    0.738   <0.001 68.1 0.694  0.777
    0.240   <0.001 47.0 0.191  0.297
    0.253   <0.001 47.0 0.204  0.307

Prédiction moyenne

Calculer les prédictions pour 2825 :

p <- predictions(mod)
nrow(p)
[1] 2825

Prendre leur moyenne :

mean(p$estimate)
[1] 0.6916814

Ou :

avg_predictions(mod)

 Estimate Std. Error    z Pr(>|z|)   S 2.5 % 97.5 %
    0.692    0.00791 87.4   <0.001 Inf 0.676  0.707

Prédictions moyennes par sous-groupe : by

avg_predictions(mod, by = "distance")

 distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
   Short     0.721     0.0107 67.4   <0.001   Inf 0.700  0.741
   Medium    0.670     0.0142 47.2   <0.001   Inf 0.642  0.697
   Long      0.643     0.0208 30.9   <0.001 694.7 0.602  0.684

Tests d’hypothèse nulle 💀🔪🩸✨📈

avg_predictions(mod, by = "distance")

 distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
   Short     0.721     0.0107 67.4   <0.001   Inf 0.700  0.741
   Medium    0.670     0.0142 47.2   <0.001   Inf 0.642  0.697
   Long      0.643     0.0208 30.9   <0.001 694.7 0.602  0.684

Est-ce que la probabilité prédite moyenne de récupérer son résultat est la même dans le groupe “Short” et le groupe “Long”?

avg_predictions(mod, by = "distance",
    hypothesis = "b3 - b1 = 0")

 Estimate Std. Error     z Pr(>|z|)    S  2.5 %  97.5 %
  -0.0777     0.0234 -3.32   <0.001 10.1 -0.124 -0.0319

Term: b3-b1=0

Non!

Erreur et intervalles

  • Huber-White
  • Cluster
  • Bootstrap
  • Simulation
avg_predictions(mod, vcov = "HC3")

 Estimate Std. Error    z Pr(>|z|)   S 2.5 % 97.5 %
    0.692    0.00792 87.3   <0.001 Inf 0.676  0.707
avg_predictions(mod, vcov = ~village)

 Estimate Std. Error    z Pr(>|z|)   S 2.5 % 97.5 %
    0.692     0.0108 63.8   <0.001 Inf  0.67  0.713

plot_predictions()

plot_predictions(mod, condition = c("age", "distance"))
  • Plus âgé -> Haute probabilité d’aller chercher son résultat
  • Plus loin -> Faible probabilité

100+ modèles;
même syntaxe!

Linéaire, GLM, GAM, Multi-niveau, Bayesien, etc.

À vous!

https://arelbundock.com/precrisa.R

Comparaisons
contre-factuelles

Comparaison contre-factuelle

Comparer les prédictions faites par un modèle pour différentes valeurs d’une variable explicative (p. ex., diplômés du collège vs. autres) :

Exemples: Contrastes, différences, rapports de risque (risk ratios), cotes (odds), hausse (lift), etc.

comparisons(), avg_comparisons(), plot_comparisons()

Comparaison contre-factuelle

Supposons:

  • \(\hat{Y}_{X=1}\) est la probabilité prédite pour \(Y=1\) quand \(X=1\)
  • \(\hat{Y}_{X=0}\) est…

Nombreuses quantités d’intérêt :

  • Risk difference: \(\hat{Y}_{X=1} - \hat{Y}_{X=0}\)
  • Risk ratio: \(\frac{\hat{Y}_{X=1}}{\hat{Y}_{X=0}}\)
  • Odds ratio: \(\log\left(\frac{\hat{Y}_{X=1}}{1 - \hat{Y}_{X=1}}\right) - \log\left(\frac{\hat{Y}_{X=0}}{1 - \hat{Y}_{X=0}}\right)\)

Comparaison contre-factuelle

Les quantités mentionnées plus haut sont conditionnelles.

Estimés différents pour différentes valeurs des prédicteurs. Ex: La “Risk Difference” pour un homme de 30 ans est différente de celle pour une femme de 50 ans.

Il faut définir les valeurs des variables de contrôle qui nous intéressent. Comme avec les prédictions.

Différence de risques

En moyenne, est-ce que passer de 0 à 1 sur la variable incentive augmente la probabilité de result=1?

Un individu de 30 ans qui habite à près du centre :

\[\hat{Y}_{I=1, A=30, D=S} - \hat{Y}_{I=0, A=30, D=S}\]

comparisons(mod, 
  variables = "incentive", 
  newdata = datagrid(age = 30, distance = "Short", incentive = 0))

 age distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
  30    Short    0.443     0.0215 20.5   <0.001 309.3 0.401  0.485

Term: incentive
Comparison: 1 - 0

Différence de risques pour toutes les observations

comparisons(mod, variables = "incentive")

 Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
    0.441     0.0216 20.4   <0.001 305.4 0.399  0.484
    0.451     0.0213 21.1   <0.001 326.6 0.409  0.493
    0.415     0.0247 16.8   <0.001 209.0 0.367  0.464
    0.452     0.0213 21.2   <0.001 329.4 0.410  0.494
    0.414     0.0249 16.6   <0.001 204.0 0.365  0.463
--- 2815 rows omitted. See ?avg_comparisons and ?print.marginaleffects --- 
    0.463     0.0196 23.6   <0.001 405.8 0.425  0.502
    0.459     0.0214 21.4   <0.001 335.1 0.417  0.501
    0.463     0.0199 23.3   <0.001 395.3 0.424  0.502
    0.461     0.0192 24.0   <0.001 420.6 0.423  0.499
    0.463     0.0194 23.9   <0.001 416.8 0.425  0.500
Term: incentive
Comparison: 1 - 0

Moyenne des différences de risques

avg_comparisons(mod, variables = "incentive")

 Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
    0.449     0.0209 21.5   <0.001 338.3 0.408  0.489

Term: incentive
Comparison: mean(1) - mean(0)

aka:

  • Average Treatment Effect (ATE)
  • G-Computation

Moyenne des différences de risques par groupe

avg_comparisons(mod, variables = "incentive", 
                by = "distance")

 distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
   Long      0.461     0.0199 23.1   <0.001 390.8 0.422  0.500
   Medium    0.457     0.0208 22.0   <0.001 353.9 0.417  0.498
   Short     0.439     0.0216 20.3   <0.001 302.3 0.397  0.481

Term: incentive
Comparison: mean(1) - mean(0)

Moyenne des différences de risques par groupe

avg_comparisons(mod, variables = "incentive", 
                by = "incentive")

 Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
    0.450     0.0208 21.6   <0.001 340.8 0.409  0.490
    0.448     0.0209 21.5   <0.001 337.6 0.407  0.489

Term: incentive
Comparison: mean(1) - mean(0)

aka:

  • Average Treatment Effect on the Treated (ATT)
  • Average Treatment Effect on the Un-Treated (ATU)

Prédicteurs numériques

Quel est l’effet d’une augmentation de age de…

  • 5 unités?
  • 1 écart-type?
  • Mouvement entre deux valeurs spécifiques ?

Prédicteurs numériques

avg_comparisons(mod, variables = list(age = 5))
avg_comparisons(mod, variables = list(age = "sd"))
avg_comparisons(mod, variables = list(age = c(18, 55)))

Prédicteurs catégoriels

Exemple de différents contrastes avec la variable distance :

avg_comparisons(mod, variables = "distance")

                  Contrast Estimate Std. Error    z Pr(>|z|)    S   2.5 % 97.5 %
 mean(Medium) - mean(Long)   0.0365     0.0252 1.45    0.148  2.8 -0.0129 0.0859
 mean(Short) - mean(Long)    0.0875     0.0235 3.73   <0.001 12.4  0.0416 0.1335

Term: distance

Ratio de risque

\[\frac{\hat{Y}_{I=1, A=30, D=S}}{\hat{Y}_{I=0, A=30, D=S}}\]

avg_comparisons(mod, 
  variables = "incentive", 
  comparison = "ratio")

 Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
     2.31      0.131 17.7   <0.001 230.0  2.06   2.57

Term: incentive
Comparison: mean(1) / mean(0)

Comparaison personnalisée

\[\log\left(\frac{\hat{Y}_{I=1,A=30,D=S}}{1 - \hat{Y}_{I=1,A=30,D=S}}\right) - \log\left(\frac{\hat{Y}_{I=0,A=30,D=S}}{1 - \hat{Y}_{I=0,A=30,D=S}}\right)\]

avg_comparisons(mod, 
  variables = "incentive", 
  comparison = function(hi, lo) log((hi/(1 - hi))/(lo/(1 - lo))))

 Estimate Std. Error  z Pr(>|z|)     S 2.5 % 97.5 %
     2.01        0.1 20   <0.001 292.2  1.81    2.2

Term: incentive
Comparison: 1, 0

Tests d’hypothèse nulle 💀🔪🩸✨📈

Effet de traitement moyen pour les individus à différentes distance:

avg_comparisons(mod, 
  variables = "incentive", 
  by = "distance")

 distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
   Long      0.461     0.0199 23.1   <0.001 390.8 0.422  0.500
   Medium    0.457     0.0208 22.0   <0.001 353.9 0.417  0.498
   Short     0.439     0.0216 20.3   <0.001 302.3 0.397  0.481

Term: incentive
Comparison: mean(1) - mean(0)
  • Hétérogénéité
  • Intéraction
  • Modification

Tests d’hypothèse nulle 💀🔪🩸✨📈

Effet de traitement moyen pour les individus à différentes distance:

avg_comparisons(mod, 
  variables = "incentive", 
  by = "distance")

 distance Estimate Std. Error    z Pr(>|z|)     S 2.5 % 97.5 %
   Long      0.461     0.0199 23.1   <0.001 390.8 0.422  0.500
   Medium    0.457     0.0208 22.0   <0.001 353.9 0.417  0.498
   Short     0.439     0.0216 20.3   <0.001 302.3 0.397  0.481

Term: incentive
Comparison: mean(1) - mean(0)

Est-ce que l’effet de incentive est plus fort pour ceux qui habitent loin (Long) que pour ceux qui habitent près (Short)?

\[\beta_1 - \beta_3 = 0\]

Tests d’hypothèse nulle 💀🔪🩸✨📈

avg_comparisons(mod, 
  hypothesis = "b1 - b3 = 0",
  variables = "incentive", 
  by = "distance")

 Estimate Std. Error    z Pr(>|z|)    S  2.5 % 97.5 %
   0.0224    0.00567 3.95   <0.001 13.6 0.0113 0.0335

Term: b1-b3=0

Oui!

plot_comparisons(): 1 condition

Quel est l’“effet” de incentive sur result, à différents niveaux de distance ?

plot_comparisons(mod, variables = "incentive", condition = "distance")

marginaleffects.com

À vous!

https://arelbundock.com/precrisa.R