Auteur·rice

HAMLIL Mohamed

Date de publication

30 avril 2026

Introduction

Le but de cette séance est de s’éclairer sur le lien potentiel entre l’abolition de la peine de mort et le taux de criminalité aux États-Unis.

Les données proviennent du site du FBI. Elles donnent le nombre de meurtres dans les différents états des États-Unis en 2009 ainsi que la population. Pour s’affranchir des différences de tailles de populations, on utilise le taux de criminalité pour 1000 habitants.

Nous étudierons ensuite un second jeu de données sur l’absorption de matière grasse par des beignets, un cas classique d’ANOVA à un facteur.


1. Acquisition des données

Code
data = read.csv2("murderusa.csv")
head(data)
A data.frame: 6 × 5
State Population..en.milliers. Total.murders..2009. tauxmeurtres PDMabolie09
<chr> <int> <int> <dbl> <chr>
1 Vermont 622 7 0.113 oui
2 Iowa 3008 34 0.113 oui
3 Minnesota 5266 72 0.137 oui
4 North Dakota 647 9 0.139 oui
5 Hawaii 1295 21 0.162 oui
6 Maine 1318 26 0.197 oui

Affectons les variables :

Code
txcrime09 = data$tauxmeurtres
PDMabolie09 = data$PDMabolie09

Créons deux sous-groupes : états ayant aboli la peine de mort et ceux ne l’ayant pas aboli :

Code
abolie09 = txcrime09[PDMabolie09 == "oui"]
nonabolie09 = txcrime09[PDMabolie09 == "non"]

# Conversion en numérique si nécessaire
abolie09 = as.numeric(abolie09)
nonabolie09 = as.numeric(nonabolie09)

cat("Nombre d'états ayant aboli :", length(abolie09))
cat("\nNombre d'états n'ayant pas aboli :", length(nonabolie09))
Nombre d'états ayant aboli : 13
Nombre d'états n'ayant pas aboli : 37

2. Effet de l’abolition sur le taux de meurtres

2.1 Statistiques descriptives

Boîtes à moustaches

Code
boxplot(abolie09, nonabolie09,
        main = "Taux de criminalité selon l'abolition de la peine de mort",
        col = c("orange", "red"),
        names = c("Abolie", "Non Abolie"),
        ylab = "Taux de meurtres pour 1000 hab.")

On peut aussi utiliser directement la formule :

Code
boxplot(txcrime09 ~ PDMabolie09,
        main = "Taux de criminalité",
        col = c("orange", "red"),
        ylab = "Taux de meurtres pour 1000 hab.")

Observations :

  • Tous les indicateurs (médiane, quartiles) sont plus bas dans les états ayant aboli la peine de mort.
  • Le taux de criminalité est globalement plus bas dans les états ayant aboli.
  • Attention : Aucun lien de cause à effet ne peut être dégagé ! (Est-ce l’abolition qui fait baisser le taux ? Est-ce un taux bas qui permet d’abolir ? Ou n’y a-t-il aucun lien ?)

Comparaison des moyennes

Code
cat("Moyenne (abolie) :", mean(abolie09))
cat("\nMoyenne (non abolie) :", mean(nonabolie09))
Moyenne (abolie) : 0.2963846
Moyenne (non abolie) : 0.494973

2.2 ANOVA

On teste si la différence entre les groupes est statistiquement significative :

Code
summary(aov(txcrime09 ~ PDMabolie09))
            Df Sum Sq Mean Sq F value Pr(>F)  
PDMabolie09  1  0.379  0.3794   3.172 0.0812 .
Residuals   48  5.740  0.1196                 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

On obtient le même résultat avec anova :

Code
anova(lm(txcrime09 ~ PDMabolie09))
A anova: 2 × 5
Df Sum Sq Mean Sq F value Pr(>F)
<int> <dbl> <dbl> <dbl> <dbl>
PDMabolie09 1 0.3793872 0.3793872 3.172317 0.08122444
Residuals 48 5.7404680 0.1195931 NA NA

Interprétation : La p-value est trop grande pour rejeter l’égalité des moyennes. On ne peut pas affirmer qu’il y a un lien significatif entre l’abolition de la peine de mort et le taux de criminalité.

2.3 Retrouver les résultats du tableau d’ANOVA

SCM (Somme des Carrés du Modèle)

\[SCM = \sum_{i=1}^{2} n_i (\bar{X}_i - \bar{X})^2\]

Code
SCM = length(abolie09) * (mean(abolie09) - mean(txcrime09))^2 +
      length(nonabolie09) * (mean(nonabolie09) - mean(txcrime09))^2
cat("SCM =", round(SCM, 6))
SCM = 0.379387

SCE (Somme des Carrés des Erreurs)

\[SCE = \sum_{i=1}^{2} \sum_{j=1}^{n_i} (X_{ij} - \bar{X}_i)^2\]

Code
SCE = sum((abolie09 - mean(abolie09))^2) +
      sum((nonabolie09 - mean(nonabolie09))^2)
cat("SCE =", round(SCE, 6))
SCE = 5.740468

Statistique de Fisher

\[F_{obs} = \frac{SCM / (2-1)}{SCE / (50-2)} = \frac{SCM}{SCE / 48}\]

Code
Fobs = SCM / (SCE / 48)
cat("F observé =", round(Fobs, 4))
F observé = 3.1723

p-value

La variable du test suit une loi de Fisher \(\mathcal{F}_{1, 48}\) :

Code
pval = 1 - pf(Fobs, 1, 48)
cat("p-value =", round(pval, 4))
p-value = 0.0812

3. Et en 2016 ?

Des résultats similaires ont été montrés en 2016 en L2. On peut charger le fichier crime16.csv pour une analyse comparable :

Code
crime16 = read.csv2("crime16.csv")
head(crime16)
A data.frame: 6 × 2
crime_nonabo crime_abo
<dbl> <dbl>
1 532.3 123.8
2 470.1 158.3
3 550.9 227.1
4 445.3 238.9
5 372.2 242.6
6 501.8 245.0
Code
boxplot(crime16$crime_nonabo, crime16$crime_abo,
        names = c("Non abolie", "Abolie"),
        main = "Taux de criminalité en 2016",
        col = c("red", "orange"),
        ylab = "Taux de criminalité")


4. Beignets et matière grasse

4.1 Contexte

On étudie la variable « quantité de matière grasse absorbée par des beignets durant leur cuisson ». Pour chacune des 4 matières grasses, on a constitué 6 fournées de 24 beignets chacune. Les valeurs ont été simplifiées en soustrayant 100g.

C’est une classification à une seule voie : chaque matière grasse représente un niveau du facteur.

4.2 Acquisition des données

Code
rm(list = ls())
donnees <- read.table("beignets.txt", header = TRUE)
head(donnees)
A data.frame: 6 × 2
Gras Gr
<int> <int>
1 64 1
2 72 1
3 68 1
4 77 1
5 56 1
6 95 1
Code
X = donnees[, "Gras"]
Groupe = factor(donnees[, "Gr"])
cat("Niveaux du facteur :", levels(Groupe))
cat("\nNombre d'observations :", length(X))
Niveaux du facteur : 1 2 3 4
Nombre d'observations : 24

Note : Bien que codée de 1 à 4, la variable Groupe n’est pas numérique, c’est un facteur. C’est pour cela qu’on utilise factor().

4.3 Test de normalité par groupe

Avant de faire une ANOVA, on vérifie l’hypothèse de normalité dans chaque groupe :

Code
tapply(X, Groupe, shapiro.test)
$`1`

    Shapiro-Wilk normality test

data:  X[[i]]
W = 0.95004, p-value = 0.7406


$`2`

    Shapiro-Wilk normality test

data:  X[[i]]
W = 0.93162, p-value = 0.5926


$`3`

    Shapiro-Wilk normality test

data:  X[[i]]
W = 0.9334, p-value = 0.6066


$`4`

    Shapiro-Wilk normality test

data:  X[[i]]
W = 0.88836, p-value = 0.3097

Conclusion : On ne rejette la normalité dans aucun groupe → on peut utiliser l’ANOVA.

4.4 Tableau d’ANOVA

Code
summary(aov(X ~ Groupe))
            Df Sum Sq Mean Sq F value  Pr(>F)   
Groupe       3   1636   545.5   5.406 0.00688 **
Residuals   20   2018   100.9                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Code
anova(lm(X ~ Groupe))
A anova: 2 × 5
Df Sum Sq Mean Sq F value Pr(>F)
<int> <dbl> <dbl> <dbl> <dbl>
Groupe 3 1636.5 545.5 5.406343 0.006875948
Residuals 20 2018.0 100.9 NA NA

Conclusion : On rejette l’égalité des moyennes → il y a un effet significatif du type de graisse sur l’absorption.

4.5 Contrastes

On souhaite tester l’hypothèse que les matières grasses d’origine animale (groupes 1 et 2) sont absorbées de la même façon que celles d’origine végétale (groupes 3 et 4) :

\[\frac{\mu_1 + \mu_2}{2} = \frac{\mu_3 + \mu_4}{2} \iff \mu_1 + \mu_2 - \mu_3 - \mu_4 = 0\]

Les coefficients du contraste sont \(\lambda = (1, 1, -1, -1)\).

Code
lambda.i = c(1, 1, -1, -1)
xbar.i = tapply(X, Groupe, mean)
cat("Moyennes par groupe :\n")
print(xbar.i)
Moyennes par groupe :
 1  2  3  4 
72 85 76 62 
Code
n = length(X)
n.i = tapply(rep(1, n), Groupe, sum)
a = length(levels(Groupe))

# Somme des carrés des erreurs
Q.i = tapply(X, Groupe, var) * (n.i - 1)
SCE = sum(Q.i)
dl = n - a
CME = SCE / dl

# Contraste et test de Student
phi = sum(xbar.i * lambda.i)
s.phi = sqrt(CME * sum(lambda.i^2 / n.i))
tvalue = (phi - 0) / s.phi
pvaleur = pt(tvalue, dl)
pvaleur = 2 * min(pvaleur, 1 - pvaleur)

cat("Contraste estimé φ̂ =", round(phi, 2))
cat("\nÉcart-type s(φ̂) =", round(s.phi, 2))
cat("\nStatistique t =", round(tvalue, 4))
cat("\np-value =", round(pvaleur, 4))
cat("\nDegrés de liberté =", dl)
Contraste estimé φ̂ = 19
Écart-type s(φ̂) = 8.2
Statistique t = 2.3166
p-value = 0.0313
Degrés de liberté = 20

Conclusion : La nature de la matière grasse (animale vs végétale) a une influence significative sur l’absorption.

Fonction de test de contraste

On peut encapsuler ce test dans une fonction réutilisable :

Code
contrast.test = function(X, Groupe, lambda.i, phi0 = 0) {
  xbar.i = tapply(X, Groupe, mean)
  n = length(X)
  a = length(levels(Groupe))
  n.i = tapply(rep(1, n), Groupe, sum)
  Q.i = tapply(X, Groupe, var) * (n.i - 1)
  SCE = sum(Q.i)
  dl = n - a
  CME = SCE / dl
  phi = sum(xbar.i * lambda.i)
  s.phi = sqrt(CME * sum(lambda.i^2 / n.i))
  tvalue = (phi - phi0) / s.phi
  pvaleur = pt(tvalue, dl)
  pvaleur = 2 * min(pvaleur, 1 - pvaleur)
  return(list(c(phi.hat = phi, s.phi = s.phi),
              c(t = tvalue, p.value = pvaleur), dl = dl))
}

contrast.test(X, Groupe, c(1, 1, -1, -1), phi0 = 0)
[[1]]
phi.hat
19
s.phi
8.20162585507695
[[2]]
t
2.31661384410003
p.value
0.0312510602796205
$dl
20

4.6 Construction du tableau d’ANOVA à la main

Pour bien comprendre l’ANOVA, construisons le tableau pas à pas :

Code
a <- length(levels(Groupe))
n <- length(X)

# Tailles de chaque groupe
n.i <- tapply(rep(1, n), Groupe, sum)

# Moyennes de chaque groupe
xbar.i <- tapply(X, Groupe, mean)

# Variation dans chaque groupe
Q.i <- tapply(X, Groupe, var) * (n.i - 1)

# Sommes des carrés
SCT <- (n - 1) * var(X)
SCE <- sum(Q.i)
SCM <- SCT - SCE

# Degrés de liberté
dlE <- n - a
dlM <- a - 1

# Carrés moyens
CMM <- SCM / dlM
CME <- SCE / dlE

# Statistique de Fisher et p-value
F.obs <- CMM / CME
seuil.crit <- 1 - pf(F.obs, dlM, dlE)
Code
# Construction du tableau
Anova.tableau <- matrix(
  c(SCM, SCE, SCT, dlM, dlE, NA, CMM, CME, NA, F.obs, NA, NA, seuil.crit, NA, NA),
  nrow = 3,
  dimnames = list(c("Inter groupe", "Intra groupe", "Totale"),
                  c("SC", "dl", "CM", "F.obs", "Pr(F)"))
)
Anova.tableau
A matrix: 3 × 5 of type dbl
SC dl CM F.obs Pr(F)
Inter groupe 1636.5 3 545.5 5.406343 0.006875948
Intra groupe 2018.0 20 100.9 NA NA
Totale 3654.5 NA NA NA NA

Résumé

Concept Description Fonction R
Boxplot comparatif Comparer deux distributions boxplot(x ~ facteur)
ANOVA à 1 facteur Tester l’égalité des moyennes anova(lm()), aov()
SCM, SCE, SCT Décomposition de la variance Calcul manuel
Contrastes Tester une combinaison linéaire des moyennes Fonction personnalisée
tapply Appliquer une fonction par sous-groupe tapply(var, facteur, fun)

Points clés :

  • L’ANOVA teste si la différence entre les moyennes des groupes est significative.
  • Avant de faire une ANOVA, il faut vérifier la normalité dans chaque groupe.
  • L’ANOVA ne permet pas de conclure à un lien de cause à effet.
  • Les contrastes permettent de tester des hypothèses plus fines sur les moyennes.