Lors de cette séance nous allons apprendre:
A ajouter des résultats de tests statistiques à des graphiques
A ajouter des statistiques descriptives à des graphiques
A sélectionner des lignes avec la fonction filter
A représenter des données temporelles
Tout d’abord ouvrez le projet R, créez un nouveau script R et préparez votre environnement de travail:
# Chargez la librairie `tidyverse` (aide: utilisez la fonction `library()`)
# Importer `burghardt_et_al_2015_expt1.txt` et mettez le dans un objet appelé `expt1`
#(aide: utilisez la fonction `read_tsv()`)
Pour pouvoir rajouter des résultats de tests statistiques à des graphiques, nous allons utiliser des fonctions de la librairie ggpubr
.
library(ggpubr)
Afin de faire un test de comparaison de moyennes, nous utilisons la fonction stat_compare_means
.
ggplot(expt1, aes(genotype, days.to.flower, colour = fluctuation)) +
geom_boxplot() +
stat_compare_means(label = "p.format", method="t.test")
## Warning: Removed 83 rows containing non-finite values (stat_boxplot).
## Warning: Removed 83 rows containing non-finite values (stat_compare_means).
Comme nous avons deux groupes à comparer (température fluctuante vs température constante), la fonction fait la comparaison de ces deux groupes automatiquement pour chaque génotype et donne la p-value associée.
Plutôt que d’ajouter les valeurs des p-values, il est aussi possible d’utiliser un code à base d’étoiles pour indiquer le niveau de significativité du test:
ns: p > 0.05
*: p <= 0.05
**: p <= 0.01
***: p <= 0.001
****: p <= 0.0001
ggplot(expt1, aes(genotype, days.to.flower, colour = fluctuation)) +
geom_boxplot() +
stat_compare_means(label = "p.signif", method="t.test")
## Warning: Removed 83 rows containing non-finite values (stat_boxplot).
## Warning: Removed 83 rows containing non-finite values (stat_compare_means).
Par contre, s’il n’y a pas deux groupes clairement identifiés, tous les groupes vont être comparés, en utilisant par défaut avec un test de Kruskal–Wallis:
ggplot(expt1, aes(genotype, days.to.flower)) +
geom_boxplot() +
stat_compare_means(label = "p.format")
## Warning: Removed 83 rows containing non-finite values (stat_boxplot).
## Warning: Removed 83 rows containing non-finite values (stat_compare_means).
Voir ce site pour en savoir plus sur les différentes options de tests de comparaison de moyennes (comparaison de deux groupes et de multiples groupes).
Il est aussi possible d’ajouter le résultat d’un test de corrélation (spearman ou pearson) à un scatterplot en utilisant la fonction stat_cor()
:
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num)) +
geom_point()+
stat_cor()
## Warning: Removed 333 rows containing non-finite values (stat_cor).
## Warning: Removed 333 rows containing missing values (geom_point).
Par défaut, un test de pearson
est effectué. Pour utiliser un test de spearman
, il faut utiliser method="spearman"
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num)) +
geom_point()+
stat_cor(method="spearman")
## Warning: Removed 333 rows containing non-finite values (stat_cor).
## Warning: Removed 333 rows containing missing values (geom_point).
Si vous avez plusieurs groupes, stat_cor
va calculer la corrélation pour chaque groupe.
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = fluctuation)) +
geom_point()+
stat_cor()
## Warning: Removed 333 rows containing non-finite values (stat_cor).
## Warning: Removed 333 rows containing missing values (geom_point).
Il est aussi possible d’ajouter une ligne de régression (linéaire ou non), incluant un intervalle de confiance, au graphique avec geom_smooth()
. Notez cependant, comme on peut le voir ci-dessous, que cette fonction fonctionne mieux pour des régressions linéaires et non complexes.
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = fluctuation)) +
geom_point()+
stat_cor() +
geom_smooth()
## Warning: Removed 333 rows containing non-finite values (stat_cor).
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
## Warning: Removed 333 rows containing non-finite values (stat_smooth).
## Warning: Removed 333 rows containing missing values (geom_point).
Comme vous pouvez le voir, la ligne de régression ne représente pas bien les données. Il faudrait ici utiliser une méthode plus compliquée pour pouvoir extrapoler une ligne de régression. Ce n’est pas parce qu’il est possible de faire quelque chose avec R, que cela est forcément judicieux. Soyez critique et assurez vous que ce que vous faites soit pertinent!
Exercice 1:
Ajouter le test statistique le plus approprié aux deux graphiques suivant:
ggplot(expt1, aes(fluctuation, rosette.leaf.num)) +
geom_violin()
## Warning: Removed 95 rows containing non-finite values (stat_ydensity).
ggplot(expt1, aes(days.to.bolt, days.to.flower)) +
geom_point()
## Warning: Removed 83 rows containing missing values (geom_point).
BONUS: Ajoutez une ligne de régression (pour le graphique où cela est possible). Déplacez le résultat du test statistique pour qu’il soit au meilleur endroit sur le graphique. Qu’est ce que vous pouvez conclure pour chacun de ces graphiques?
%>%
Les “pipes” (%>%
) permettent de faire une séquence d’opération sur des données, sans avoir besoin de créer des objets intermédiaires (ou de faire des commandes imbriquées très compliquées)
Grace au symbole %>%
pipe, nous pouvons créer une chaîne de commandes. Pour cela nous devons d’abord faire une commande et ajouter %>%
à la fin de la ligne qui va utiliser le résultat de cette commande comme input pour la fonction à la ligne suivante. Nous allons utiliser les pipes à partir de maintenant pour créer des chaines de commandes.
group_by()
et summarise()
Parfois nous voulons résumer nos données dans une table plus petite et en extraire des statistiques descriptives (moyenne, médiane, nombre d’observations …).
Ce type d’opération peut être fait avec la combinaison de deux fonctions: group_by()
et summarise()
.
Notez que group_by()
ne change pas le format de la table de données. Cette fonction liste des lignes qui doivent être groupées. Nous pouvons ensuite utiliser summarise()
pour extraire des statistiques descriptives de chaque groupe.
Par exemple, nous pouvons extraire la moyenne pour le temps de floraison de chaque génotype:
group_by(expt1, genotype) %>%
summarise(mean.days.to.flower = mean(days.to.flower, na.rm = TRUE))
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 10 x 2
## genotype mean.days.to.flower
## <chr> <dbl>
## 1 Col Ama 55.8
## 2 Col FRI 70.2
## 3 fca-6 70.4
## 4 flc-3 FRI 53.1
## 5 flk-1 77.6
## 6 fve-3 81.2
## 7 ld-1 89.8
## 8 Ler-1 55.6
## 9 prmt5 FRI 88.7
## 10 vin3-4 FRI 95.7
L’output contient deux colonnes:
genotype
qui est la colonne qui a servi à grouper les données
mean.days.to.flower
qui est la colonne crée par la fonction summarise
Il n’y a que 10 lignes dans cette table, une par génotype.
Il est possible de grouper les données par plus d’une variable.
Par exemple nous pouvons mesurer la moyenne, la médiane et l’écart type pour chaque génotype aux différentes températures:
group_by(expt1, genotype, temperature) %>%
summarise(mean.days.flower = mean(days.to.flower, na.rm = TRUE),
sd.days.flower = sd(days.to.flower, na.rm = TRUE),
median.days.flower = median(days.to.flower, na.rm = TRUE))
## `summarise()` regrouping output by 'genotype' (override with `.groups` argument)
## # A tibble: 20 x 5
## # Groups: genotype [10]
## genotype temperature mean.days.flower sd.days.flower median.days.flower
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 Col Ama 12 66.6 25.5 62
## 2 Col Ama 22 46.0 20.5 51
## 3 Col FRI 12 81.1 26.2 80
## 4 Col FRI 22 57.8 25.9 59.5
## 5 fca-6 12 76.2 20.9 72.5
## 6 fca-6 22 63.9 34.6 40
## 7 flc-3 FRI 12 64.7 25.2 46
## 8 flc-3 FRI 22 43 17.5 50
## 9 flk-1 12 87.8 27.9 87
## 10 flk-1 22 61.2 29.2 47
## 11 fve-3 12 96.1 26.3 92
## 12 fve-3 22 51.5 5.96 49
## 13 ld-1 12 99.8 27.8 100
## 14 ld-1 22 72.9 36.5 60
## 15 Ler-1 12 67.4 22.7 64
## 16 Ler-1 22 45.2 18.3 51.5
## 17 prmt5 FRI 12 103. 22.0 102
## 18 prmt5 FRI 22 67.2 26.6 62
## 19 vin3-4 FRI 12 111. 40.0 83
## 20 vin3-4 FRI 22 72.5 29.7 69
Il y a maintenant 20 lignes dans la table, car chaque génotype apparaît deux fois (12 et 22 degrés)
Une autre information utile que nous pouvons extraire est le nombre d’observation pour chaque groupe. Pour cela nous devons utiliser la fonction n()
, dans summarise()
qui compte le nombre de ligne pour chaque groupe.
Par exemple, pour connaitre le nombre d’observations pour chaque génotype:
group_by(expt1, genotype) %>%
summarise(n.obs = n())
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 10 x 2
## genotype n.obs
## <chr> <int>
## 1 Col Ama 135
## 2 Col FRI 128
## 3 fca-6 64
## 4 flc-3 FRI 136
## 5 flk-1 60
## 6 fve-3 60
## 7 ld-1 60
## 8 Ler-1 68
## 9 prmt5 FRI 125
## 10 vin3-4 FRI 121
Attention: Quand vous utilisez la fonction group_by()
, les lignes du tableau restent groupées en fonction de la variable utilisée. Les opérations suivantes vont utiliser ces groupes, ce qui peut poser problème. Pensez à utiliser la fonction ungroup()
pour enlever les groupes quand vous avez fini avec group_by()
et summarise()
Exercice 2:
Calculez la médiane et l’écart-type de
blade.length.mm
ettotal.leaf.length.mm
pour chaquegenotype
aux différentesday.length
. Calculez aussi le nombre d’observations de chaque groupe.
Une autre possibilité est d’ajouter les statistiques descriptives à un graphique contenant les données.
Pour cela, nous devons:
Utiliser group_by()
et summarise()
pour extraire les statistiques descriptives
Utiliser une fonction de la famille *_join()
pour les combiner avec nos données
Nous pouvons maintenant faire un graphique contenant les données et les statistiques descriptives.
Par exemple, prenons ce boxplot:
ggplot( expt1, aes(genotype, rosette.leaf.num)) +
geom_boxplot()
## Warning: Removed 95 rows containing non-finite values (stat_boxplot).
Si nous voulons y ajouter le nombre d’observations pour chaque groupe, nous utilisons le script suivant:
group_by(expt1, genotype) %>%
summarise(n.obs=n()) %>%
mutate(n.obs=paste("n =",n.obs)) %>%
full_join(expt1, by="genotype") %>%
ggplot( aes(genotype, rosette.leaf.num)) +
geom_boxplot() +
geom_text(aes(label=n.obs, x=genotype, y=0))
## `summarise()` ungrouping output (override with `.groups` argument)
## Warning: Removed 95 rows containing non-finite values (stat_boxplot).
Exercice 3
Faites un voilin plot de
total.leaf.length.mm
pour chaque génotype et ajoutez la médiane pour chaque groupe (avec un point coloré) ainsi que le nombre d’observation de chaque groupeBONUS: Modifiez le graphique que vous venez de créer pour ajouter les comparaison de moyenne de chaque génotype avec
Col Ama
(comme sur le graphique ci-dessous)
Avec cette même méthode il est aussi possible d’ajouter des informations pour des groupes formés à partir de deux variables.
Par exemple, si nous voulons faire un boxplot rosette.leaf.num
pour chaque génotype en fonction de la température et y ajouter le nombre d’observations des différents groupes, nous utilisons le script suivant:
group_by(expt1, genotype, fluctuation) %>%
summarise(n.obs=n()) %>%
mutate(n.obs=paste("n =",n.obs)) %>%
full_join(expt1, by=c("genotype", "fluctuation")) %>%
ggplot( aes(genotype, rosette.leaf.num, fill=fluctuation)) +
geom_boxplot() +
geom_text(aes(label=n.obs, x=genotype, y=-2),position=position_dodge(0.8), angle=45)
## `summarise()` regrouping output by 'genotype' (override with `.groups` argument)
## Warning: Removed 95 rows containing non-finite values (stat_boxplot).
filter
Avec la fonction filter()
, nous pouvons garder toutes les lignes de notre table qui correspondent à des plantes qui ont subies une vernalisation.
Tout d’abord, nous devons connaitre les différentes valeurs de la colonne vernalization
. Comme nous pouvons voir, il y a deux options: ‘NV’ et ‘V’.
unique(expt1$vernalization)
## [1] "NV" "V"
(note: $
permet de sélectionner une colonne en particulier de la table)
Comme nous voulons garder les plantes qui ont subies une vernalisation, nous devons filtrer les données pour garder les lignes pour lesquelles il y a “V” dans la colonne vernalization
:
filter(expt1, vernalization == "V")
## # A tibble: 330 x 15
## plant_nb genotype background temperature fluctuation day.length vernalization
## <dbl> <chr> <chr> <dbl> <chr> <dbl> <chr>
## 1 1 Col Ama Col 12 Con 16 V
## 2 2 Col Ama Col 12 Con 16 V
## 3 3 Col Ama Col 12 Con 16 V
## 4 4 Col Ama Col 12 Con 16 V
## 5 5 Col Ama Col 12 Con 16 V
## 6 6 Col Ama Col 12 Con 16 V
## 7 7 Col Ama Col 12 Con 16 V
## 8 8 Col Ama Col 12 Con 16 V
## 9 1 Col Ama Col 12 Con 8 V
## 10 2 Col Ama Col 12 Con 8 V
## # … with 320 more rows, and 8 more variables: survival.bolt <chr>, bolt <chr>,
## # days.to.bolt <dbl>, days.to.flower <dbl>, rosette.leaf.num <dbl>,
## # cauline.leaf.num <dbl>, blade.length.mm <dbl>, total.leaf.length.mm <dbl>
Nous pouvons utiliser les opérateurs suivant pour définir les conditions pour filtrer les données:
Opérateur | Condition de sélection | Exemple |
---|---|---|
< |
inférieur à | filter(expt1, days.to.bolt < 20) |
<= |
inférieur ou égal à | filter(expt1, days.to.bolt <= 20) |
> |
supérieur à | filter(expt1, days.to.bolt > 20) |
>= |
supérieur ou égal à | filter(expt1, days.to.bolt >= 20) |
== |
égal à | filter(expt1, days.to.bolt == 20) |
!= |
différent de | filter(expt1, days.to.bolt != 20) |
%in% |
est contenu dans | filter(expt1, genotype %in% c("Col FRI", "Ler-1")) |
Il est aussi possible de combiner plusieurs conditions de sélection avec les opérateurs suivant:
Opérateur | Signification | Exemple |
---|---|---|
& |
ET | filter(expt1, days.to.bolt == 20 & genotype == "Ler-1") |
| |
OU | filter(expt1, rosette.leaf.num < 8 | rosette.leaf.num > 100) |
Nous pouvons aussi identifier les données manquantes (NA
) avec la fonction is.na()
ou sa négation (en utilisant !
):
Opérateur | Signification | Exemple |
---|---|---|
is.na() |
données manquante | filter(expt1, is.na(rosette.leaf.num)) |
!is.na() |
donnée non manquante | filter(expt1, !is.na(rosette.leaf.num)) |
Par exemple, nous pouvons sélectionner les plantes qui ont été vernalisées ET qui ont poussées avec une température fluctuante:
filter(expt1, vernalization == "V" & fluctuation == "Var")
Il est aussi possible de sélectionner les plantes qui ont poussées avec 8h de jours OU qui fleurissent tardivement:
filter(expt1, day.length == "8" | days.to.bolt > 85)
Exercice 4: Filtrez les données pour garder les plantes selon les 3 cas de figures suivant (indépendants les uns des autres):
- Plantes qui ne sont pas du background Ler et qui ont été traitées avec une température fluctuante.
- Plantes qui ont fleuries (bolt) en moins de 57 jours et qui ont moins de 40 feuilles de rosette
- Plantes du génotype fca-6 pour qui le blade.length.mm n’est pas manquant
Pour représenter des données temporelles avec une ligne par individu mesuré, nous allons utiliser un autre jeu de données qui contient des transcriptomes effectués sur des plantes d’Arabidopsis thaliana ayant été traités pour 15 minutes, 1 heure ou 4 heures avec une température de 17°C ou 27°C.
Tout d’abord, chargeons les données et gardons les dans l’objet RNAseq
:
RNAseq <- read_tsv("../data/data_expression_cortijo2017.txt")
##
## ── Column specification ────────────────────────────────────────────────────────
## cols(
## gene = col_character(),
## Log2FoldToZero_17c_15min = col_double(),
## TLog2FoldToZero_17c_1hr = col_double(),
## Log2FoldToZero_17c_4hr = col_double(),
## Log2FoldToZero_27c_15min = col_double(),
## Log2FoldToZero_27c_1hr = col_double(),
## Log2FoldToZero_27c_4hr = col_double(),
## cluster = col_character()
## )
L’objectif est d’obtenir un graphique de ce type:
Pour cela nous devons d’abord filtrer les données pour ne garder que l’expression pour les gènes AT1G04520
et AT3G12580
filter(RNAseq, gene%in%c("AT1G04520","AT3G12580"))
## # A tibble: 2 x 8
## gene Log2FoldToZero_… TLog2FoldToZero… Log2FoldToZero_… Log2FoldToZero_…
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 AT1G… -0.0924 -0.595 -1.44 -0.447
## 2 AT3G… 1.46 0.951 -0.421 6.22
## # … with 3 more variables: Log2FoldToZero_27c_1hr <dbl>,
## # Log2FoldToZero_27c_4hr <dbl>, cluster <chr>
Nous obtenons une table avec deux lignes, une par gène; et 8 colonnes, une par condition, plus une avec le cluster. Afin de pouvoir faire le graphique voulu, il nous faut un tableau avec une colonne contenant la durée de traitement, une autre avec la température de traitement et une troisième avec le niveau d’expression des gènes. Comme le tableau ci-dessous:
## # A tibble: 12 x 6
## gene cluster normalisation temperature temps expression
## <chr> <chr> <chr> <chr> <chr> <dbl>
## 1 AT1G04520 cluster1 Log2FoldToZero 17c 15min -0.0924
## 2 AT3G12580 cluster6A Log2FoldToZero 17c 15min 1.46
## 3 AT1G04520 cluster1 TLog2FoldToZero 17c 1hr -0.595
## 4 AT3G12580 cluster6A TLog2FoldToZero 17c 1hr 0.951
## 5 AT1G04520 cluster1 Log2FoldToZero 17c 4hr -1.44
## 6 AT3G12580 cluster6A Log2FoldToZero 17c 4hr -0.421
## 7 AT1G04520 cluster1 Log2FoldToZero 27c 15min -0.447
## 8 AT3G12580 cluster6A Log2FoldToZero 27c 15min 6.22
## 9 AT1G04520 cluster1 Log2FoldToZero 27c 1hr -1.78
## 10 AT3G12580 cluster6A Log2FoldToZero 27c 1hr 6.24
## 11 AT1G04520 cluster1 Log2FoldToZero 27c 4hr -1.93
## 12 AT3G12580 cluster6A Log2FoldToZero 27c 4hr 2.95
Or ces informations sont dans le titre des colonnes de notre tableau. Pour changer le format et créer une colonne qui contient l’information du titre des colonnes, nous devons utiliser la fonction gather()
Voici comment la fonction gather()
marche:
Essayons sur nos données:
filter(RNAseq, gene%in%c("AT1G04520","AT3G12580")) %>%
gather(key="condition", value="expression", Log2FoldToZero_17c_15min:Log2FoldToZero_27c_4hr)
## # A tibble: 12 x 4
## gene cluster condition expression
## <chr> <chr> <chr> <dbl>
## 1 AT1G04520 cluster1 Log2FoldToZero_17c_15min -0.0924
## 2 AT3G12580 cluster6A Log2FoldToZero_17c_15min 1.46
## 3 AT1G04520 cluster1 TLog2FoldToZero_17c_1hr -0.595
## 4 AT3G12580 cluster6A TLog2FoldToZero_17c_1hr 0.951
## 5 AT1G04520 cluster1 Log2FoldToZero_17c_4hr -1.44
## 6 AT3G12580 cluster6A Log2FoldToZero_17c_4hr -0.421
## 7 AT1G04520 cluster1 Log2FoldToZero_27c_15min -0.447
## 8 AT3G12580 cluster6A Log2FoldToZero_27c_15min 6.22
## 9 AT1G04520 cluster1 Log2FoldToZero_27c_1hr -1.78
## 10 AT3G12580 cluster6A Log2FoldToZero_27c_1hr 6.24
## 11 AT1G04520 cluster1 Log2FoldToZero_27c_4hr -1.93
## 12 AT3G12580 cluster6A Log2FoldToZero_27c_4hr 2.95
Nous avons maintenant une colonne condition
, qui contient le nom des colonnes que nous avons regroupées et une colonne expression
qui contient les valeurs des colonnes (le niveau d’expression des gènes). Ce n’est pas encore exactement ce dont nous avons besoin car la colonne condition
contient toutes les informations de l’expérience, mais nous voulons une colonne pour chacune de ces informations: méthode de normalisation, température, durée de traitement.
Pour cela nous allons utiliser la fonction separate
, qui permet de séparer une colonne en plusieurs colonnes:
## # A tibble: 12 x 6
## gene cluster normalisation temperature temps expression
## <chr> <chr> <chr> <chr> <chr> <dbl>
## 1 AT1G04520 cluster1 Log2FoldToZero 17c 15min -0.0924
## 2 AT3G12580 cluster6A Log2FoldToZero 17c 15min 1.46
## 3 AT1G04520 cluster1 TLog2FoldToZero 17c 1hr -0.595
## 4 AT3G12580 cluster6A TLog2FoldToZero 17c 1hr 0.951
## 5 AT1G04520 cluster1 Log2FoldToZero 17c 4hr -1.44
## 6 AT3G12580 cluster6A Log2FoldToZero 17c 4hr -0.421
## 7 AT1G04520 cluster1 Log2FoldToZero 27c 15min -0.447
## 8 AT3G12580 cluster6A Log2FoldToZero 27c 15min 6.22
## 9 AT1G04520 cluster1 Log2FoldToZero 27c 1hr -1.78
## 10 AT3G12580 cluster6A Log2FoldToZero 27c 1hr 6.24
## 11 AT1G04520 cluster1 Log2FoldToZero 27c 4hr -1.93
## 12 AT3G12580 cluster6A Log2FoldToZero 27c 4hr 2.95
Nous avons maintenant le format de tableau qui nous permet de faire le graphique voulu, montrant le changement d’expression au cours du temps en réponse au traitement de température pour nos deux gènes d’intérêt.
filter(RNAseq, gene%in%c("AT1G04520","AT3G12580")) %>%
gather(key="condition", value="expression", Log2FoldToZero_17c_15min:Log2FoldToZero_27c_4hr) %>%
separate(condition, into=c("normalisation", "temperature", "temps")) %>%
ggplot(aes(x=temps, y=expression,color=temperature, group=temperature)) +
geom_line() +
facet_wrap(~gene, scales="free_y")
Attention: quand nous utilisons geom_line()
, l’argument group=
est essentiel pour savoir comment relier les points ensemble en une (ou plusieurs) ligne(s).
Vous avez vu aujourd’hui, qu’il est possible de rajouter de nombreuses informations à des graphiques, et de réorganiser vos données pour pouvoir faire la représentation la plus appropriée de vos données