# QCBS R Workshop Series ## ## ggplot2 // tidyr // dplyr ## ## Auteur(e)s: Quebec Center for Biodiversity Science ## Materials Generated & Amalgamated by: ## Xavier Giroux-Bougard, Monica Granados, ## Maxwell Farrell, Etienne Low-Decarie ## Dernière mise à jour: 8 novembre 2016 ## R version 3.1.3 #### 0. On passe le ménage #### # Effacez le contenu du répertoire de travail rm(list=ls()) # Installez et charger les paquets requis if(!require(ggplot2)){install.packages("ggplot2")} library(ggplot2) if(!require(tidyr)){install.packages("tidyr")} library(tidyr) if(!require(dplyr)){install.packages("dplyr")} library(dplyr) if(!require(magrittr)){install.packages("magrittr")} library(magrittr) if(!require(gridExtra)){install.packages("gridExtra")} library(gridExtra) if(!require(viridis)){install.packages("viridis")} library(viridis) if(!require(devtools)){install.packages("devtools")} library(devtools) #--------------------------------------------------------------------------------# #### 1. Tracer des graphiques avec R en utilisant la grammaire des graphiques #### #--------------------------------------------------------------------------------# #### 1.1 Intro - ggplot2 #### #### 1.2 Graphiques simples avec la fonction ''qplot()'' #### # Explorez le fichier d'aide de qplot ?qplot # Chargez et inspectez le jeu de données "iris" data(iris) ?iris head(iris) str(iris) names(iris) # Diagramme de dispersion de base qplot(data = iris, x = Sepal.Length, y = Sepal.Width) # Diagramme de dispersion avec variable catégorique qplot(data = iris, x = Species, y = Sepal.Width) # Ajouter des étiquettes d'axes et un titre qplot(data = iris, x = Sepal.Length, xlab = "Longueur (mm)", y = Sepal.Width, ylab = "Largeur (mm)", main = "Dimensions de sépales d'iris") #------------------------------------------------------------------------------# #------------------------# #### ggplot2 - Défi 1 #### #------------------------# # À l'aide de la fonction qplot(), tracez un graphique de type “nuage de points” # avec un titre et des étiquettes d'axes. Utilisez le jeu données CO2 # ou BOD qui sont déjà inclus dans R. Vous pouvez les charger et explorer # leur structure avec ces commandes : ?CO2 data(CO2) ?BOD data(BOD) # SOLUTION: qplot(data = CO2, x = conc, xlab = "Concentration de CO2 (mL/L)", y = uptake, ylab = "Absorption de CO2 (umol/m^2 sec)", main = "Absorption de CO2 chez une espèce de graminée") #------------------------------------------------------------------------------# #### 1.3 La grammaire des graphiques #### #### 1.4 Graphiques avancés avec la fonction ''ggplot()'' #### # en utilisant qplot() qplot(data = iris, x = Sepal.Length, xlab = "Longueur (mm)", y = Sepal.Width, ylab = "Largeur (mm)", main = "Dimensions de sépales d'iris") # code équivalent en utilisant ggplot() ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + xlab("Longueur (mm)") + ylab("Largeur (mm)") + ggtitle("Dimensions de sépales d'iris") # Assign ggplot to object graph.base <- ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + xlab("Longueur (mm)") + ylab("Largeur (mm)") + ggtitle("Dimensions de sépales d'iris") #### 1.5 Ajouter des couleurs et des formes #### graph.base <- graph.base + aes(colour = Species, shape = Species) graph.base #### 1.6 Ajouter des objets géométriques #### graph.ligne <- graph.base + geom_smooth(method="lm", se = FALSE) graph.ligne ### BONUS #### # objets géometriques émoticône avec le paquet emoGG devtools::install_github("dill/emoGG") library(emoGG) # vous devez faire une recherche emoji_search("bear") ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) + geom_emoji(emoji="1f337") #------------------------# #### ggplot2 - Défi 2 #### #------------------------# # Créez un graphique coloré avec une droite de régression (ou autres smoother) # à partir des jeux de données CO2 ou msleep de R: # Chargez et explorez les jeux de données CO2 et msleep data(CO2) ?CO2 head(CO2) str(CO2) names(CO2) # Solution avec loess CO2.graph <- ggplot(data = CO2, aes(x = conc, y = uptake, colour = Treatment)) + geom_point() + xlab("Concentration en CO2 (mL/L)") + ylab("Absorption de CO2 (umol/m^2 sec)") + ggtitle("Absorption de CO2 par une espèce de graminée") + geom_smooth(method = "loess") CO2.graph # On pourrait aussi tracer des courbes accompagnées de l'erreur type avec couleurs CO2.graph <- ggplot(data = CO2, aes(x = conc, y = uptake, colour = factor(Treatment))) + geom_point() + xlab("Concentration en CO2 (mL/L)") + ylab("Absorption de CO2 (umol/m^2 sec)") + ggtitle("Absorption de CO2 par une espèce de graminée") + geom_smooth(method = "loess", aes(fill = factor(Treatment))) CO2.graph #### 1.7 Séparer les graphiques en panneaux #### # graphique de base pour CO2 data(CO2) CO2.graph <- ggplot(data = CO2, aes(x = conc, y = uptake, colour = Treatment)) + geom_point() + xlab("Concentration en CO2 (mL/L)") + ylab("Absorption de CO2 (umol/m^2 sec)") + ggtitle("Absorption de CO2 par une espèce de graminée") CO2.graph # Adding facets CO2.graph <- CO2.graph + facet_grid(. ~ Type) CO2.graph #### 1.8 Adding groups #### # Adding line geoms CO2.graph + geom_line() # Specifying groups CO2.graph <- CO2.graph + geom_line(aes(group = Plant)) CO2.graph #------------------------------------------------------------------------------# #------------------------# #### ggplot2 - Défi 3 #### #------------------------# # Familiarisez-vous avec un nouvel objet géométrique et d'autres éléments # graphiques. Utilisez votre propre jeu de données ou un jeu de données déjà # inclus dans R (Tapez ''data()'' pour la liste des jeux de données disponibles) data(msleep) data(OrchardSprays) # SOLUTION data(OrchardSprays) box.graph <- ggplot(data = OrchardSprays, aes(x = treatment, y = decrease)) + geom_boxplot() box.graph #------------------------------------------------------------------------------# #### 1.9 Saving plots #### # Enregistrer un graphique avec un script pdf("./graph_du_jour.pdf") print(graph.base) print(graph.ligne) print(CO2.graph) graphics.off() # avec la fonction ggsave() ggsave("CO2graph.pdf", CO2.graph, height = 8.5, width = 11, units = "in") #### 1.10 Fine tuning - colours #### # manuellement CO2.graph + scale_colour_manual(values = c("nonchilled" = "red","chilled" = "blue")) # avec des codes hexadécimaux CO2.graph + scale_colour_manual(values = c("#FF0000", "#1111e5")) # en utilisant la palette de couleur viridis CO2.graph + scale_colour_manual(values = viridis(2, option = "D")) # Bonus!!! Palette de couleur RColorBrewer if(!require(RcolorBrewer)) {install.packages("RColorBrewer")} library(RColorBrewer) graph.base + scale_color_brewer(palette = "Dark2") # Bonus!!! Palette de couleur Wes Anderson if(!require(devtools)) {install.packages("devtools")} library(devtools) devtools::install_github("wesanderson", "karthik") library(wesanderson) graph.base + scale_color_manual(values = wes_palette("GrandBudapest", 3)) #### Ajustement de précision - axes et échelles #### CO2.graph + scale_y_continuous(name = "Taux d'absorption de CO2", breaks = seq(5,50, by = 10), labels = seq(5,50, by = 10), trans = "log10") #### 1.12 Fine tuning themes #### # black and white ggplot2 theme CO2.graph + theme_bw() # building your own theme mon.theme <- theme_bw() + theme(plot.title = element_text(colour = "red")) + theme(legend.position = c(0.9, 0.9)) CO2.graph + mon.theme # BONUS: ggtheme package if(!require(ggthemes)) {install.packages("ggthemes")} library(ggthemes) # un theme minimaliste data(OrchardSprays) tufte.box <- ggplot(data = OrchardSprays, aes(x = treatment, y = decrease)) + geom_tufteboxplot() + theme_tufte() tufte.box # la fonction plot() de R est tout de même puissante # en parallèle avec nos analyse plot(iris) lm <- lm(Sepal.Length~Petal.Width, data = iris) x11() plot(lm) # Bonus! - Ecologists who may become vegan users # install_github("ggvegan", "gavinsimpson") library(ggvegan) data(dune) data(dune.env) sol <- cca(dune ~ A1 + Management, data = dune.env) autoplot(sol) data(mite) data(mite.env) mite.hel = decostand(mite, "hel") rda <- rda(mite.hel ~ WatrCont + Shrub, mite.env) # Model with all explanatory variables x11() ggvegan.plot <- autoplot(rda) + theme_bw() normal.plot <- plot(rda) #------------------------------------------------------------------------------# #--------------------------------------------------------# #### 2. Utiliser "tidyr" pour réorganiser ses données #### #--------------------------------------------------------# # Matériel d'inspiration: # tidyr #https://blog.rstudio.org/2014/07/22/introducing-tidyr/ #### 2.1 Pourquoi réorganiser ses données ? #### # Chargez et explorez le jeux de données "airquality" ?airquality str(airquality) head(airquality) names(airquality) #### 2.2 Données en format long vs large #### # On peut utiliser "tidyr" comme suit : # 1. "gather()" --> "ramasser" nos données (large --> long) # 2."spread()" --> "éparpiller" nos données (long --> large) # Exemple : vous envoyez votre assistant de terrain pour faire la collecte # de données des dimensions de plusieurs arbres sur un site de recherche, # sois le diamètre à la hauteur de la poitrine (DHP) et la hauteur. # Il ou elle vous revient avec le format “dégât” (format en large) suivant : degat <- data.frame(Species = c("Chêne", "Orme", "Frêne"), DHP = c(12, 20, 13), Haut = c(56, 85, 55)) degat #### 2.3 Gather: Ramasser les données en format long #### ?gather # On peut empiler les colonnes DHP et Haut degat.long <- gather(degat, dimension, cm, DHP, Haut) degat.long # Exemple : Essayons également avec le jeu de données CO2, soit en # "ramassant" les colonnes contant la concentration de CO2 "conc" # et le CO2 absorbé "uptake" CO2.long <- gather(CO2, response, value, conc, uptake) head(CO2) head(CO2.long) tail(CO2.long) #### 2.4 Spread: Making your data wide #### ?spread # la fonction spread() utilise la meme syntaxe que gather degat.large <- spread(degat.long, dimension, cm) degat.large #------------------------------------------------------------------------------# #--------------------# #### tidyr Défi 4 #### #--------------------# # Réorganisez le jeu de données airquality en format long (en ramassant tous les # colonnes sauf Month et Day). Ensuite, remettez-le en format large pour # retrouver le format original de airquality: # SOLUTION: ?airquality names(airquality) air.long <- gather(airquality, variable, value, -Month, -Day) # Notez que la syntaxe ici indique qu'on veut "ramasser" toutes # les colonnes sauf "Month" et "Day" head(air.long) air.wide <- spread(air.long , variable, value) head(air.wide) #------------------------------------------------------------------------------# #### 2.5 Separate : séparer une colonne en plusieurs colonnes #### # À titre d'exemple, créons un jeu de données fictif # sur les poissons et le zooplancton set.seed(8) gros.degat <- data.frame(id = 1:4, trt = sample(rep(c('controle', 'culture'), each = 2)), zooplancton.T1 = runif(4), poisson.T1 = runif(4), zooplancton.T2 = runif(4), poisson.T2 = runif(4)) gros.degat # la première étape consisterais à transformer le jeu de données en format long gros.degat.long <- gather(gros.degat, taxa, count, -id, -trt) head(gros.degat.long) # ensuite on peut separer la colonne taxa pour créer une colonne de variable du temps (T1 et T2) # Ici, notons que la syntaxe \\. est requise parce qu'un point seul (.) est # une carte frime (un joker) pour plusieurs fonctions dans le langage R. # Les \\ servent donc à indiquer qu'on fait belle et bien référence au point gros.degat.long.sep <- separate(gros.degat.long, taxa, into = c("especes", "temps"), sep = "\\.") head(gros.degat.long.sep) #### 2.6 Combiner ggplot2 avec tidyr #### # Exemple utilisant les formats long et large avec le jeu de données airquality head(airquality) # Le jeu de données est en format large où chaque variable mesurée # (ozone, solar.r, wind and temp) ont leur propre colonne. # Diagrammes de diagnostic avec un format large et ggplot2 # 1: Visualisez l'étendue de chaque variable mesurée en fonction du mois. fMonth <- factor(airquality$Month) # Convertit la variable "Month" en facteur. ozone.box <- ggplot(airquality, aes(x = fMonth, y = Ozone)) + geom_boxplot() solar.box <- ggplot(airquality, aes(x = fMonth, y = Solar.R)) + geom_boxplot() temp.box <- ggplot(airquality, aes(x = fMonth, y = Temp)) + geom_boxplot() wind.box <- ggplot(airquality, aes(x = fMonth, y = Wind)) + geom_boxplot() # Vous pouvez utiliser la fonction grid.arrange() du paquet gridExtra pour # regrouper tous ces diagrammes en une seule figure : combo.box <- grid.arrange(ozone.box, solar.box, temp.box, wind.box, nrow = 2) # nrow = argument spécifiant le nombre de lignes sur lesquelles # les diagrammes seront affichés. # 2: On peut faire la même chose, mais en représentant les variables en # fonction du jour pour chaque mois séparément ozone.graph <- ggplot(airquality, aes(x = Day, y = Ozone)) + geom_point() + geom_smooth() + facet_wrap(~ Month, nrow = 2) solar.graph <- ggplot(airquality, aes(x = Day, y = Solar.R)) + geom_point() + geom_smooth() + facet_wrap(~ Month, nrow = 2) wind.graph <- ggplot(airquality, aes(x = Day, y = Wind)) + geom_point() + geom_smooth() + facet_wrap(~ Month, nrow = 2) temp.graph <- ggplot(airquality, aes(x = Day, y = Temp)) + geom_point() + geom_smooth() + facet_wrap(~ Month, nrow = 2) # On peut également regrouper tous ces diagrammes # (même si ce n'est pas très joli pour le moment !) : combo.facets <- grid.arrange(ozone.graph, solar.graph, wind.graph, temp.graph, nrow = 4) # MAIS, comment faire pour utiliser la fonction ''facet_wrap()'' avec des # variables mesurées au lieu des variables "Month" et "Day" air.long <- gather(airquality, variable, value, -Month, -Day) head(air.long) air.large <- spread(air.long , variable, value) head(air.large) # Utilisez air.long fMonth.long <- factor(air.long$Month) meteo <- ggplot(air.long, aes(x = fMonth.long, y = value)) + geom_boxplot() + facet_wrap(~ variable, nrow = 2) meteo # Comparez les diagrammes ''meteo'' avec ceux de ''combo.box'' # On peut aussi ajuster les échelles de l'axe y sur chaque panneau meteo <- meteo + facet_wrap(~ variable, nrow = 2, scales = "free") meteo # On peut aussi utiliser le format long pour créer un graphique qui # inclut toutes les variables sur un seul graphique : meteo2 <- ggplot(air.long, aes(x = Day, y = value, colour = variable)) + geom_point() + facet_wrap(~ Month, nrow = 1) meteo2 #------------------------------------------------------------------------------# #---------------------------------------------# #### 3. Manipulation de données avec dplyr #### #---------------------------------------------# ## MANIPULATION EXTRÊME DE JEUX DE DONNÉES ## #### 3.1 Intro - la mission de dplyr #### #### 3.2 Fonctions dplyr de base #### # Sélection de colonnes avec ''select()'' ozone <- select(airquality, Ozone, Month, Day) head(ozone) # Sélection d'un sous-ensemble de rangées avec ''filter()'' aout <- filter(airquality, Month == 8, Temp >= 90) head(aout) # Triage avec ''arrange()'' air_degat <- sample_frac(airquality, 1) head(air_degat) air_chron <- arrange(air_degat, Month, Day) head(air_chron) # Créer des nouvelles variables avec ''mutate()'' airquality_C <- mutate(airquality, Temp_C = (Temp-32)*(5/9)) head(airquality_C) #### 3.3 dplyr et magrittr, don du ciel #### # on peut accomplir 2 étapes en enveloppant les fonctions juin_C <- mutate(filter(airquality, Month == 6), Temp_C = (Temp-32)*(5/9)) # ou on peut les lier avec un "pipe" juin_C <- airquality %>% filter(Month == 6) %>% mutate(Temp_C = (Temp-32)*(5/9)) #### 3.4 dplyr - Opérations de regroupement et d'aggrégation #### # calculer la température moyenne et l'écart type pour chaque mois mois_moy <- airquality %>% group_by(Month) %>% summarise(mean_temp = mean(Temp), sd_temp = sd(Temp)) mois_moy #------------------------------------------------------------------------------# #### dplyr Défi #5 #### # En utilisant le jeu de données ChickWeight, créez un tableau sommaire # dans lequel on retrouve la différence de masse entre le maximum et le # minimum de la masse enregistré pour chaque poussin dans l'étude. Utilisez # les verbes dplyr et le “pipe” %>%. mass_diff <- ChickWeight %>% group_by(Chick) %>% summarise(weight_gain = max(weight) - min(weight)) mass_diff # Notez qu'ici on mesure simplement la différence de masse entre le maximum # et le minimum. Il ne s'agit donc pas de la différence de masse entre le début # et la fin de l'étude (différence en ordre chronologique de la masse). # Inspectez de plus près les observations liées au poussin # 18 : poussin_18 <- ChickWeight %>% filter(Chick == 18) poussin_18 # au lieu de max() et min() on pourrait utiliser last() et first(), qui nous # informerait de la masse d'un poussin à la fin vs au début de l'étude #------------------------------------------------------------------------------# #### dplyr NINJA Défi #6 #### # En utilisant le jeu de données ChickWeight, créez un tableau sommaire # nous indiquant, pour chaque Diet, la moyenne de la différence de masse # entre la fin et le début de l'étude pour chaque poussin. Utilisez les # verbes dplyr et le pipe %>%. (Indice : les fonctions first() et last() # pourrait s'avérer utile) diet_mass <- ChickWeight %>% group_by(Diet, Chick) %>% summarise(gain_masse = last(weight) - first(weight)) %>% summarise(gain_moyen = mean(gain_masse)) diet_mass #------------------------------------------------------------------------------#