Vous travaillerez de 9h15 à 12h30 au plus tard. Vous aurez accès au cours, à l’internet… La seule exigence est que vous n’utilisiez pas d’aide extérieure.
Vous devrez répondre aux questions dans ce fichier en ajoutant du texte et du code. Les questions sont sous la forme de puces :
Utilisez le format “Blockquote” pour le texte de vos réponses (en mode visuel, utilisez le menu “Format” ; en mode source, commencez chaque paragraphe par
>et sautez une ligne avant vos réponses). Ajoutez le code dans des bouts de code standard:
# Code
library("tidyverse")
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.0 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.2 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ dplyr::select() masks MASS::select()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Tricotez souvent pour ne pas accumuler des erreurs de code.
Vous devez répondre aussi clairement que possible, en rédigeant vos réponses, mais soyez factuels et concis : inutile de montrer que vous connaissez le cours puisque vous avez accès aux documents. Votre code doit être facile à lire, avec des commentaires quand c’est utile, formaté correctement (attention aux espaces, aux indentations, à la clarté des noms des objets, etc.).
A la fin de votre travail, envoyez le fichier examen.Rmd
à eric.marcon@agroparistech.fr
Les données nécessaires se trouvent dans le sous-dossier
data.
L’examen comporte plusieurs questions à traiter :
Les brochets sont des prédateurs supérieurs qui cumulent des pesticides au cours de leur vie. Vous devez quantifier le lien entre la concentration en DDT et l’âge des poissons.
Les données (âge et concentration en DDT) de 15 brochets sont dans le
fichier data/Brochets.csv au format anglo-saxon.
library("readr") # Déjà chargé par library("tidiverse")
read_csv("data/Brochet.csv") %>%
print() ->
brochets
## Rows: 15 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (3): Obs, Age, TxDDT
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 15 × 3
## Obs Age TxDDT
## <dbl> <dbl> <dbl>
## 1 1 2 0.2
## 2 2 2 0.25
## 3 3 2 0.18
## 4 4 3 0.19
## 5 5 3 0.29
## 6 6 3 0.28
## 7 7 4 0.31
## 8 8 4 0.33
## 9 9 4 0.36
## 10 10 5 0.71
## 11 11 5 0.38
## 12 12 5 0.47
## 13 13 6 1.1
## 14 14 6 0.87
## 15 15 6 0.83
La colonne
Obsn’a aucune utilité.
Représentation grahphique :
brochets %>%
ggplot(aes(x = Age, y = TxDDT)) +
geom_point() +
geom_smooth(method = "lm")
## `geom_smooth()` using formula = 'y ~ x'
Estimation du modèle :
brochets_lm <- lm(TxDDT ~ Age, data = brochets)
summary(brochets_lm)
##
## Call:
## lm(formula = TxDDT ~ Age, data = brochets)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.24133 -0.10500 0.01133 0.08300 0.30733
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.23533 0.11269 -2.088 0.057 .
## Age 0.17133 0.02656 6.450 2.16e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1455 on 13 degrees of freedom
## Multiple R-squared: 0.7619, Adjusted R-squared: 0.7436
## F-statistic: 41.61 on 1 and 13 DF, p-value: 2.165e-05
Le modèle explique 76% de la variabilité. La variable age est significative. Comme la constante n’est pas différente de 0, le modèle pourrait être estimé sans elle, d’autant plus que la concentration en DDT à la naissance des brochets devrait être nulle.
Vérification de la distribution des résidus :
plot(brochets_lm, which = 1)
Les résidus ont une forme de cuvette : les points centraux ont des résidus négatifs, les extrêmes des valeurs positives.
Vérification de la normalité des résidus :
plot(brochets_lm, which = 2)
Le test de Shapiro ne rejette pas la normalité des résidus. Remarque : le nombre de points est très petit, la puissance du test est donc faible.
brochets_lm %>% residuals() %>% shapiro.test()
##
## Shapiro-Wilk normality test
##
## data: .
## W = 0.96554, p-value = 0.7874
Effet de levier : le point 13 (6 ans, taux = 1,1), tout en haut à droite du nuage de points, a un fort effet de levier positif, compensé par celui, négatif du point 11 (le plus bas des brochets de 5 ans). Ces deux points apparaissent dans toutes les figures de diagnostic.
plot(brochets_lm, which = 5)
Conclusion : la régression ne viole pas les hypothèses de façon flagrante mais souffre de plusieurs problèmes : les résidus ne sont pas répartis de façon homogène, deux points ont un effet de levier excessif. Le fait que les ages soient des valeurs discrètes limite la pertinence du modèle.
Représentation graphique :
brochets %>%
ggplot(aes(x = Age, y = TxDDT)) +
geom_point() +
scale_y_log10() +
geom_smooth(method = "lm")
## `geom_smooth()` using formula = 'y ~ x'
Estimation du modèle :
brochets_lm_log <- lm(log(TxDDT) ~ Age, data = brochets)
summary(brochets_lm_log)
##
## Call:
## lm(formula = log(TxDDT) ~ Age, data = brochets)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.37122 -0.15103 0.04114 0.09496 0.32278
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.44086 0.17135 -14.245 2.61e-09 ***
## Age 0.36890 0.04039 9.134 5.09e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2212 on 13 degrees of freedom
## Multiple R-squared: 0.8652, Adjusted R-squared: 0.8548
## F-statistic: 83.43 on 1 and 13 DF, p-value: 5.092e-07
Le modèle linéaire s’ajuste mieux après la transformation de la variable expliquée avec un R² de 87%. La constante est maintenant significative à cause de la transformation logarithmique.
Vérification de la distribution des résidus : sans amélioration par rapport au modèle original.
plot(brochets_lm_log, which = 1)
Vérification de la normalité des résidus : similaire au précédent.
plot(brochets_lm_log, which = 2)
Le test de Shapiro ne rejette pas la normalité des résidus.
brochets_lm_log %>% residuals() %>% shapiro.test()
##
## Shapiro-Wilk normality test
##
## data: .
## W = 0.95807, p-value = 0.6589
Effet de levier : les point 13 et 11 ont toujours les mêmes effets, mais apparemment moins importants que dans le modèle original.
plot(brochets_lm_log, which = 5)
Conclusion : la régression du logarithme de la concentration en DDT fonctionne mieux que le modèle original.
Estimation du modèle :
brochets_lm_log_poly <- lm(log(TxDDT) ~ poly(Age, degree = 2), data = brochets)
summary(brochets_lm_log_poly)
##
## Call:
## lm(formula = log(TxDDT) ~ poly(Age, degree = 2), data = brochets)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.30079 -0.09397 -0.04723 0.14917 0.32431
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.96527 0.04875 -19.800 1.57e-10 ***
## poly(Age, degree = 2)1 2.02054 0.18881 10.701 1.71e-07 ***
## poly(Age, degree = 2)2 0.45644 0.18881 2.417 0.0325 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1888 on 12 degrees of freedom
## Multiple R-squared: 0.9093, Adjusted R-squared: 0.8942
## F-statistic: 60.18 on 2 and 12 DF, p-value: 5.553e-07
Le coefficient de l’âge au carré est significatif (p-value = 3%) et le R² est amélioré. Le R² ajusté, qui prend en compte l’ajout d’un paramètre, est aussi amélioré. Le modèle est donc meilleur que le précédent.
Vérification de la distribution des résidus : La distribution des résidus est beaucoup plus homogène.
plot(brochets_lm_log_poly, which = 1)
Vérification de la normalité des résidus : similaire au précédent.
plot(brochets_lm_log_poly, which = 2)
Le test de Shapiro ne rejette pas la normalité des résidus.
brochets_lm_log_poly %>% residuals() %>% shapiro.test()
##
## Shapiro-Wilk normality test
##
## data: .
## W = 0.96903, p-value = 0.8434
Effet de levier : le point 11 ne pose plus de problème.
plot(brochets_lm_log_poly, which = 5)
Conclusion : la régression du logarithme de la concentration en DDT par l’age et l’age au carré est le meilleur modèle. Le polynôme de degré 2 a réglé le problème d’hétérogénéité de la distribution des résidus (négatifs pour les valeurs intermédiaires) en permettant d’ajuster la courbe de régression.
Les abondances de 6 espèces d’oiseaux dans 4 régions ont été tirées
de Blondel et Farré (1988). Elles se trouvent dans le fichier
data/Oiseaux.csv. Les fonctions que vous allez utiliser ont
besoin d’un tableau avec des noms de ligne. Vous devez donc préparer les
données :
read_csv2("data/Oiseaux.csv") %>%
print() ->
oiseaux
## ℹ Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 6 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ";"
## chr (1): Espèce
## dbl (4): Pologne, Bourgogne, Provence, Corse
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 6 × 5
## Espèce Pologne Bourgogne Provence Corse
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 S1 33 507 77 278
## 2 S2 157 512 164 288
## 3 S3 751 444 194 406
## 4 S4 621 221 357 494
## 5 S5 776 375 347 512
## 6 S6 854 536 580 598
especes <- oiseaux[, 1]
oiseaux <- oiseaux[, -1]
rownames(nom_du_tableau) <- vecteur_des_noms.row.names(oiseaux) <- especes$Espèce
## Warning: Setting row names on a tibble is deprecated.
Vous pouvez maintenant analyser les données.
oiseaux
## # A tibble: 6 × 4
## Pologne Bourgogne Provence Corse
## * <dbl> <dbl> <dbl> <dbl>
## 1 33 507 77 278
## 2 157 512 164 288
## 3 751 444 194 406
## 4 621 221 357 494
## 5 776 375 347 512
## 6 854 536 580 598
$ Le test du \(\chi^2\) est approprié:
chisq.test(oiseaux)
##
## Pearson's Chi-squared test
##
## data: oiseaux
## X-squared = 1286.4, df = 15, p-value < 2.2e-16
La probabilité de se tromper en rejetant l’hypothèse d’indépendance des lignes et des colonnes est très proche de 0 : les oiseaux ne sont pas distribués indépendamment des régions.
Attention : les données sont présentées sous la forme d’un tableau de contingence dont la première colonne (l’espèce) doit être retirée parce qu’elle ne contient pas de données utiles.
mosaicplot(oiseaux, shade = TRUE, main = "")
Des patrons de distribution sont visibles :
- Les espèces 1 et 2 se trouvent en Bourgogne et pas en Pologne,
- Les espèces 3 et 5 sont en Pologne,
- Les espèces 4 et 6 sont en Provence.
library("FactoMineR")
oiseaux %>%
CA() ->
oiseaux_CA
Aide : vous utiliserez la fonction CA() du package
FactoMineR.
Valeurs propres :
library("factoextra")
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
oiseaux_CA %>%
fviz_eig()
## Warning in geom_bar(stat = "identity", fill = barfill, color = barcolor, :
## Ignoring empty aesthetic: `width`.
Le premier axe représente presque toute la variabilité. On en affichera deux pour la lisibilité.
oiseaux_CA %>%
fviz_ca_biplot()
L’axe 1 associe la Bourgogne aux espèces 1 et 2. Les espèces 3 à 6 sont à l’opposé. L’axe 2 montre l’opposition entre la Pologne (espèces 3 et 5) et la Provence (espèces 4 et 6). La Corse se trouve au centre de gravité du graphique, sans espèce caractéristique claire. Ces résultats sont cohérents avec ceux du graphique en mosaïque, même si les espèces 1 et 4 présentes en Corse ne sont pas visibles dans l’AFC.
On pourrait s’attendre à un gradient géographique (nord-sud, plus ou moins continental, est-ouest) mais l’AFC ne montre rien de tel. La distribution des espèces d’oiseau répond à d’autres facteurs, inexplicables avec ces données.
Faites une carte de la parcelle 6 de Paracou
(data/Paracou6.csv) qui montrera l’emplacement du plus gros
arbre de chaque famille botanique.
Aide: vous devrez d’abord trouver la taille du plus gros arbre de chaque famille, mais vous perdrez toutes les informations individuelles sur les arbres. Vous devrez donc utiliser une jointure pour retrouver les coordonnées de l’arbre. Réfléchissez bien aux colonnes qui vont servir à la jointure et nommez la bonne colonne avec le bon nom au bon moment.
Lecture des données
read_csv2("data/Paracou6.csv") ->
paracou6
## ℹ Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 3541 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ";"
## chr (3): Family, Genus, Species
## dbl (5): SubPlot, idTree, Xfield, Yfield, CircCorr
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Sélection de l’arbre le plus gros
paracou6 %>%
group_by(Family) %>%
# Circonférence max.
# La colonne calculée garde le nom de la variable pour la jointure à suivre
summarise(CircCorr = max(CircCorr)) ->
paracou6_max
Jointure avec la table des arbres et carte
paracou6 %>%
inner_join(paracou6_max) %>%
ggplot(aes(x = Xfield, y = Yfield, color = Family, size = CircCorr)) +
geom_point() +
coord_fixed() +
labs(x = "", y = "", color = "Famille", size = "Circonférence") +
# Suppression de la légende, trop grande
theme(legend.position = "none")
## Joining with `by = join_by(Family, CircCorr)`
Le graphique contient trop d’entrées de légende pour que cette information soit lisible : on peut supprimer la légende. L’information sur les familles ne peut pas être représentée de cette façon.
La méthode de Monte-Carlo permet de tester des hypothèses ou d’estimer des valeurs par simulation. Vous allez l’appliquer ici.
Le graphique suivant montre un quart de cercle de rayon 1 dans le carré de côté 1, les deux tracés à partir de l’origine du repère :
# La fonction circle calcule les coordonnées d'un point du cercle de rayon 1
# theta est l'angle de la coordonnée polaire du point
circle <- function(theta) {
z <- exp(1i * theta)
return(c(Re(z), Im(z)))
}
# Pour tous les angles de 0 à pi/2, par valeur de 0,01
seq(from = 0, to = pi / 2, by = 1/100) %>%
# Calculer les coordonnées du point sur le cercle
sapply(FUN = circle) %>%
# Transposer la matrice obtenue (ligne 1 = x, ligne 2 = y)
t() %>%
# La transformer en tibble, nommer les colonnes
as_tibble(.name_repair = ~c("x", "y")) %>%
# Tracer le cercle
ggplot(aes(x = x, y = y)) +
geom_line() +
coord_fixed() +
# Ajouter le carré
geom_hline(yintercept = 0:1) +
geom_vline(xintercept = 0:1) ->
# Enregistrer le graphique
gg_circle
# Afficher le graphique
gg_circle
Ne modifiez pas le code ci-dessus : il produit la figure de base sur laquelle vous allez ajoutez des éléments.
Aide : créez un tibble appelé mes_points dont les
colonnes s’appellent x et y, dont le contenu
sera un vecteur de nombres aléatoires tirés dans la loi uniforme.
Ajoutez une géométrie au graphique gg_circle en précisant
ses données (data = mes_points) et son esthétique
(, mapping = aes(...)).
# Nombre de simulations
simulations_n <- 10
# Tirage des points
mes_points <- tibble(
x = runif(simulations_n),
y = runif(simulations_n)
)
# Ajout au graphique
gg_circle +
geom_point(
data = mes_points,
aes(x = x, y = y)
)
Les points sont tirés dans le carré de surface 1. La surface du quart de cercle de rayon 1 est \(\pi / 4\). La probabilité est le rapport des surfaces.
Aide : le rayon du cercle est 1.
Le nombre de succès suit une loi binomiale d’espérance \(n_{sim} * \pi / 4\).
Le nombre de succès observé est l’estimateur de l’espérance : \(n_{sim} * \pi / 4\), ce qui équivaut à \(\pi = 4 * n_+ / n_{sim}\).
mes_points, ajoutez une colonne qui
indique si chaque tirage est un succès.mes_points %>%
mutate(
# Distance du point à l'origine
distance = sqrt(x^2 + y^2),
# Le point est dans le cercle si distance < rayon
succes = (distance <= 1)
) ->
mes_points
Aide : calculez d’abord la distance du point à l’origine puis testez si elle est inférieure au rayon du cercle (il vous faudra deux colonnes).
# Estimation de pi
4 * sum(mes_points$succes) / simulations_n
## [1] 3.6
La première décimale de \(\pi\) est correcte.
Code complet:
# Nombre de simulations
simulations_n <- 1E6
# Tirage des points
tibble(
x = runif(simulations_n),
y = runif(simulations_n)
) %>%
mutate(
# Distance du point à l'origine
distance = sqrt(x^2 + y^2),
# Le point est dans le cercle si distance < rayon
succes = (distance <= 1)
) ->
mes_points
# Estimation de pi
4 * sum(mes_points$succes) / simulations_n
## [1] 3.139592
L’estimation est plus proche de la vraie valeur mais reste assez grossière. Il faut augmenter encore le nombre de tirages, mais le temps de calcul et le besoin en mémoire pour stocker
mes_pointsva rapidement poser problème.
J. Blondel & H. Farré. The convergent trajectories of bird communities along ecological successions in european forests. Öcologia (Berlin), 75 :83–93, 1988.