Plans d'essais agronomiques

en langage R

L'essentiel de cette page

Cette page explique comment faire des plans d'expérience en agronomie.

Elle se focalise aussi (partie 2.2) pour voir comment afficher ses mesures sur ses parcelles après de son propre plan d'essai dessiné "manuellement".

1- Je souhaite mettre en place un plan d'expérience pour des essais agronomiques

Ma démarche pour faire un plan d'expérience pour des essais agronomiques est la question suivante :

Partons d'un exemple. Je souhaite faire des essais agronomiques avec 3 pesticides différents. Je compte combiner aussi deux méthodes d'arrosage. Je vais ensuite comptabiliser le nombre de feuilles par plants et la production de fruits (en kilogramme) par mètre carré. Je souhaite aussi suivre la température moyenne sur les blocs au cas où la température aurait un effet. 

Forcément, mes questions tombent : Combien de blocs ? Comment s'assurer qu'il n'y a pas un effet bord ? Quels types d'analyses ? Comment représenter les blocs ?

Il existe plusieurs techniques pour organiser ses blocs d'essai (randomisation totale, les blocs aléatoires, les carrés latins...). Le choix du dispositif dépend de la question de recherche, du nombre de traitements, du nombre de répétitions...

On peut approfondir différents exemples R avec ce site.

Nuançons d'abord un vocabulaire :

Typiquement, on parle de :

# Charger le package agricolae

library(agricolae)

# Charger magrittr pour pouvoir utiliser les pipes %>%

library(magrittr

# Charger tidyverse pour pouvoir retravailler la plan d'expérience

library(tidyverse

# Charger {desplot} pour afficher les plans d'expérience

library(desplot)

# Définir les traitements

Pesticide <- c("P1", "P2", "P3") ; n_Pesticide <- n_distinct(Pesticide) # 3 pesticides

Arrosage <- c("A1", "A2") ; n_Arrosage  <- n_distinct(Arrosage)# 2 méthodes d'arrosage

#TrtSoil <- paste(Pesticide, Arrosage, sep = "-") # 6 combinaisons

# Définir le nombre de répétitions

n_Reps <- 4

1.1- Choisir sa technique de randomisation

Plusieurs techniques permettent d'organiser un plan d'essai pour limiter les effets de bordures/sol/... qui auraient plus d'effet que les traitments. Pour citer ce résumé établi avec l'aide de Consensus :

Ces techniques sont appliquées respectivement avec les fonctions design.crd(), design.lsd() et design.rcbd() de {agricolae}, mais il existe d'autres fonction de design de plans d'essais agronomiques dans ce package. On peut citer par exemple design.ab() adaptée au design alpha lorsque le trop grand nombre de traitements rend la méthode RCBD inefficace (voir plus bas dans la page).

Déclarer l'essai avec un seul facteur (ici par la méthode RCBD, randomized complete block design)

trt <- Pesticide # obligatoire pour le moment car le vecteur d'entrée de plot_rcdb() est nécessairement trt

rcbd_out <- design.rcbd(trt = Pesticide, # Traitement

               r = n_Reps, # Nombre de blocs

               seed = 42) # Bloquer la structure (répétabilité)

library("agricolaeplotr")

plot_rcdb(rcbd_out) # attention, c'est bien plot_rcdb(), mais cela devrait certainement changer à l'avenir.

Ou en CRD...

trt <- Pesticide # nécessaire en mars 2024

crd_out <- design.crd(trt = trt, # Traitement

               r = n_Reps, # Nombre de répétitions

               seed = 42)

plot_design_crd(crd_out, nrows=3, ncols=4, reverse_y = TRUE)

1.2- Faire coller le design en lignes et colonne à sa surface disponible

Le problème qu'il y a à mettre en place un essai randomisé en agronomie, c'et qu'il va falloir concrètement le faire coller au terrain.

On a en général une parcelle, et l'on va organiser l'ensemble en lignes et colonnes, on a donc envie (et c'est logique) d'ajouter ces lignes et colonnes.

#  Ajouter les lignes et colonnes en CRD

num_rows <- 3

num_cols <- 4

crd_out$bookRowCol <- crd_out$book %>%

  mutate(Col = ((row_number() - 1) %% num_cols) + 1, # Assigner les numéros de colonne

         Row = ((row_number() - 1) %/% num_cols) + 1) %>% # Assigner les numéros de ligne

  ungroup()

print(crd_out$bookRowCol)

#  Ajouter les lignes et colonnes à un RCBD

rcbd_out$bookRowCol <- rcbd_out$book %>% # Récupérer les blocs

  mutate(Row= block %>% as.integer) %>%  # Créer les lignes

  group_by(Row) %>% 

  mutate(Col= 1:n()) %>% 

  ungroup()

print(rcbd_out$bookRowCol)

   plots block Pesticide   Row   Col 1   101 1     P1            1     1 2   102 1     P3            1     2 3   103 1     P2            1     3 4   201 2     P1            2     1 5   202 2     P2            2     2 6   203 2     P3            2     3 7   301 3     P1            3     1 8   302 3     P2            3     2 9   303 3     P3            3     310   401 4     P3            4     111   402 4     P2            4     212   403 4     P1            4     3

# Afficher le carrés

desplot(Pesticide ~ Col + Row,

flip = FALSE, # Transposer le graphique

        ticks = TRUE, # Afficher colonnes et lignes

        text = Pesticide, cex = 1,

        shorten = "no", # Abréger ou non le texte

        out1 = block, # Traits noir de séparation des blocs

        data = rcbd_out$bookRowCol,

        main = "randomized complete block design", 

        show.key = T, # Légende

        key.cex = 0.5)

1.3- Randomisation multifactorielle

Lorsque j'ai plusieurs facteurs, je vais utiliser la fonction design.ab() pour les combiner et afficher le résultat.

Prenons l'exemple d'un design RCBD où je combine 3 pesticides, 2 arrosages en 4 blocs.

fac2rcbd_out <- design.ab(trt = c(n_Pesticide, n_Arrosage), 

                          design = "rcbd",

                          r = n_Reps, 

                          seed = 42)

plot_design.factorial_rcbd() de {agricolaeplotr} pourrait permettre de visualiser ce résultat mais cette fonction est encore complètement chargée de bugs (2024) et ne découpe pas les blocs sans en préciser l'ordonnancement. Lien vers les fonctions de {agricolaeplotr}.

On va donc faire appel à la logique {dplyr} et aux visualisations de {desplot} pour voir les résultats.

Exemple 1 - Déclarer l'essai avec les 2 facteurs Pesticide et Arrosage (ici par la méthode RCBD, randomized complete block design) - organisation de chaque bloc sur 2 colonnes et 3 lignes

# Déclarons le bloc d'essai sur 8 colonnes

fac2rcbd_out$bookRowCol <- fac2rcbd_out$book %>%

  bind_cols(expand.grid(Row = 1:n_Pesticide,

                        Col = 1:(n_Arrosage*n_Reps))) %>% 

  mutate(Pesticide= paste0("P", A),

         Arrosage = paste0("A", B))

Exemple 2 - Reprendre l'exemple 1 pour organiser les blocs sur 4 colonnes et 6 lignes

fac2rcbd_out$bookRowCol$Row[fac2rcbd_out$bookRowCol$block>2] <- fac2rcbd_out$bookRowCol$Row[fac2rcbd_out$bookRowCol$block>2]+3

fac2rcbd_out$bookRowCol$Col[fac2rcbd_out$bookRowCol$block>2] <- fac2rcbd_out$bookRowCol$Col[fac2rcbd_out$bookRowCol$block>2]-4

Exemple 3 - Organisation sur 1 colonne et 6 lignes, soit 4 colonnes en tout pour 4 blocs.

fac2rcbd_out$bookRowCol <- fac2rcbd_out$book %>%

  bind_cols(expand.grid(Row = 1:(n_Pesticide*n_Arrosage),

                        Col = 1:n_Reps)) %>% 

  mutate(Pesticide= paste0("P", A),

         Arrosage = paste0("A", B))

# Affichage

library("desplot")

desplot(block ~ Col + Row | block, flip = TRUE,

        out1 = Row, out1.gpar = list(col = "grey", lty = 1),

        out2 = Col, out2.gpar = list(col = "grey", lty = 1), 

        text = Pesticide, cex = 1, shorten = "no", col=Arrosage,

        data = fac2rcbd_out$bookRowCol,

        main = "RCBD", 

        show.key = T, key.cex = 0.5)

On notera 2 blocs en haut et 2 blocs dessous.

Cette disposition n'est pas imposée par la fonction desplot() ou autres, mais par les dimensions de la fenêtre. Ici, desplot a mis sur 4 colonnes, en réalité, ce serait sur 8 colonnes, il faut bien dimensionner la fenêtre.

Les blocs sont simplement indépendants.

Affichages des blocs sur 3 lignes et 8 colonnes

Affichages de 4 blocs sur 4 colonnes

J'aimerais adapter le code pour un affichage verrouillé sur 6 lignes et 4 colonnes où chaque bloc occuperait 2 colonnes et 3 lignes... C'est ce qui a été fait partie 2.2. ci-après manuellement et pourrait être établi à partir d'un design.

Il serait pertinent de développer cette page dans le domaine de l'Evaluation du meilleur design :

Remarque importante : pour bien fonctionner, le Bloc doit être déclaré en facteur, cela peut en revanche faire planter certaines fonctions telles corrigraph() de {KefiR}.

2- Je souhaite visualiser mon plan d'expérience ainsi que ses mesures

2.1- Cas d'un plan d'expérience designé avec {agricolae}.

Si l'on prend l'exemple ci-dessus (partie 1), j'ai déjà mon plan d'expérience. Dans ce cas, je vais faire mes mesures et récolter les données de température, le nombre de feuilles par plants, et la production en fruits par mètre carré (en kg/m²).

Inventons quelques valeurs et essayons de les visualiser. On supposera que mes blocs sont les uns à côté des autres et on reprendre l'exemple à 2 facteurs de la partie précédente.

# Inventons des températures autour de 24°C qui augmentent si la ligne est élevée et/ou la colonne

# On notera qu'il y a 8 colonnes, 3 lignes, plus on est en bas ou à droite, plus il fait chaud

Temperature <- round(fac2rcbd_out$bookRowCol$Row/2+fac2rcbd_out$bookRowCol$Col/3+     

                                rnorm(length(fac2rcbd_out$bookRowCol$Row),24,1),1)

# Imaginons un nombre aléatoire de feuille qui augmente légèrement plus si le pesticide P2 a été utilisé

Nb_feuilles <- round( rnorm(length(fac2rcbd_out$bookRowCol$Row),16,4)+ifelse(fac2rcbd_out$bookRowCol$Pesticide=="P2",rnorm(1,4,2),rnorm(1,1,2)),0)

# Enfin, faisons une production qui augmente un peu avec la température, avec le nombre de feuille et surtout si on a utilisé l'arrosage A2

Prod_fruits <- round(abs( rnorm(length(fac2rcbd_out$bookRowCol$Row),1,0.2)+Temperature/7*rnorm(length(fac2rcbd_out$bookRowCol$Row),1,0.2)+Nb_feuilles/5*rnorm(length(fac2rcbd_out$bookRowCol$Row),1,0.2)),1)


# Ajouter les variables mesurées

fac2rcbd_out$bookRowCol <- cbind(fac2rcbd_out$bookRowCol, 

                                 Temperature =Temperature, 

                                 Nb_feuilles = Nb_feuilles, 

                                 Prod_fruits = Prod_fruits)

# Créer ma palette perso allant du bleu au rouge

liste_colors <- c("blue","red")

colfunc<-colorRampPalette(liste_colors,interpolate="spline")

colors <- colfunc(1000)

# Afficher le plan d'expérience

desplot(Temperature ~ Col + Row | block, flip = TRUE,

 out1 = Row, out1.gpar = list(col = "grey", lty = 1), out2 = Col, out2.gpar = list(col = "grey", lty = 1), 

 text = Pesticide, cex = 1, shorten = "no", col=Arrosage, data = fac2rcbd_out$bookRowCol, 

 main = "RCBD",  show.key = T, key.cex = 0.5,fill=Temperature,col.regions=colors

)

Cette coloration laisse craindre un effet bord pour les températures.

# Créer ma palette perso allant du marron au vert

liste_colors <- c("brown","green")

colfunc<-colorRampPalette(liste_colors,interpolate="spline")

colors <- colfunc(1000)

# Afficher le plan d'expérience

desplot(Prod_fruits~ Col + Row | block, flip = TRUE,

 out1 = Row, out1.gpar = list(col = "grey", lty = 1), out2 = Col, out2.gpar = list(col = "grey", lty = 1), 

 text = Pesticide, cex = 1, shorten = "no", col=Arrosage, data = fac2rcbd_out$bookRowCol,

 main = "RCBD",  show.key = T, key.cex = 0.5,fill=Prod_fruits, col.regions=colors

)

Je visualise bien ma production de fruits au mètre carré selon les zones d'essai.

2.2- Cas d'un plan d'expérience d'essai agronomique dessiné manuellement.

On va souvent avoir son propre plan d'essais agronomiques dessinés à convertir, basculer pour un affichage sous la forme de plan d'expérience.

Dans ce genre de cas, l'on va partir d'une data.frame qui doit indiquer les lignes et colonnes pour bien préciser où sont positionnés chaque carré.

Prenons un exemple.

J'ai 4 blocs où j'ai testé mes 3 pesticides et mes 2 arrosages. J'ai tracé mes blocs à raison de 2 au nord et 2 au sud.

J'ai ainsi 4 colonnes (2 par bloc) et 6 lignes (3 par bloc).

Bloc <- rep(1:4,each=6) ; 

Row <- rep(rep(1:6,each=2),2) ; 

Col <- c(rep(1:2,times=6),rep(3:4,times=6)) 

Pesticide_Arrosage <- rep(paste(rep(paste0("P",1:3),2),rep(paste0("A",1:2),each=3)),4)

Temperature <- round(rnorm(20,24)+Row/6+Col/4,1)

tableau <- data.frame(Bloc,Row,Col,Pesticide_Arrosage,Temperature) 

   Bloc Row Col Pesticide_Arrosage Temperature

1     1   1   1              P1 A1        24.9

2     1   1   2              P2 A1        24.8

3     1   2   1              P3 A1        23.3

4     1   2   2              P1 A2        24.1

5     1   3   1              P2 A2        24.2

6     1   3   2              P3 A2        25.6

7     2   4   1              P1 A1        25.1

8     2   4   2              P2 A1        26.7

9     2   5   1              P3 A1        24.6

10    2   5   2              P1 A2        24.7

11    2   6   1              P2 A2        24.5

# Créer ma palette perso allant du marron au vert

liste_colors <- c("brown","green")

colfunc<-colorRampPalette(liste_colors,interpolate="spline")

colors <- colfunc(1000)

library(desplot)

desplot(Temperature~ Col + Row | Bloc, flip = TRUE,

 ticks=TRUE,

 out1 = Row, out1.gpar = list(col = "grey", lty = 1),

 out2 = Col, out2.gpar = list(col = "grey", lty = 1), 

 text = Pesticide_Arrosage, # Text des carrés

 cex = 1, shorten = "no",

col=Pesticide_Arrosage, # Coloration du texte des carrés en fonction de la température

data = tableau,

main = "RCBD", fill=Temperature,

col.regions=colors,

show.key = T, key.cex = 0.5)

liste_colors <- c("red","yellow")

colfunc<-colorRampPalette(liste_colors,interpolate="spline")

colors <- colfunc(1000)

library(desplot)

desplot(Temperature~ Col + Row, flip = TRUE,

 ticks=TRUE,

 out1 = Row, out1.gpar = list(col = "grey", lty = 1),

 out2 = Col, out2.gpar = list(col = "grey", lty = 1), 

 cex = 1, shorten = "no",

data = tableau,

main = "Températures", fill=Temperature,

col.regions=colors,

show.key = T, key.cex = 0.5)

3- Je souhaite écarter l'éventualité d'un effet bord

L’effet bord est l’influence des conditions extérieures sur les unités expérimentales situées à la périphérie du champ d’expérience. Par exemple, les parcelles de gauche peuvent être plus exposées au vent ou à l’ombre que les parcelles de droite. Cet effet peut fausser les résultats de l’essai en introduisant une source de variation non contrôlée.

Pour savoir s’il y a un effet bord sur les blocs de gauche, il faut comparer les résultats des parcelles situées à gauche avec ceux des parcelles situées à droite ou au centre. Il existe plusieurs méthodes pour faire cette comparaison, comme l'analyse de variance (anova), l'analyse en composantes principales sur les données centrées-réduites (voir si les parcelles de gauche se distinguent), une analyse graphique  simple.

# En reprenant l'exemple de la partie 2.1. Voir si la production dépend du traitement où de la ligne ou la colonne

modele <- aov(Prod_fruits ~ Pesticide + Arrosage + Row + Col, data = fac2rcbd_out$bookRowCol)


# Afficher les résultats de l'analyse de variance

base::summary(modele) # car summary() de {agricolaeplotr} peut être en confli avec summary() de base.

On peut aussi évaluer les effets par :

modele <- aov(Prod_fruits ~ Pesticide + Arrosage, data = fac2rcbd_out$bookRowCol)


# Afficher les résultats de l'analyse de variance

mon_F <- base::summary(modele)[[1]]["Pesticide", "F value"]

# Effectuer la randomisation avec replication (équivalent de for)

n <- 1000  # Nombre de permutations

stats_perm <- replicate(n, {

  data_perm <- fac2rcbd_out$bookRowCol

  data_perm$Pesticide<- sample(fac2rcbd_out$bookRowCol$Pesticide)  # Permuter les traitements de Pesticide

  with(data_perm, base::summary(aov(Prod_fruits ~ Pesticide + Arrosage))[[1]]["Pesticide", "F value"])

})

table(ifelse(stats_perm >mon_F,1,0))

Il ne reste plus qu'à interpréter le nombre de fois où le F du hasard se retrouve au-dessus du F réel (idéalement moins de 5% des cas). On peut aussi faire ce test sur des simples différences de moyennes?.

4- Je souhaite exploiter mon plan d'expérience

# Aggréger la production de fruits par traitement

aggregate(Prod_fruits ~ Pesticide + Arrosage, data = fac2rcbd_out$bookRowCol, 

          FUN = function(x) c(mean = mean(x), sd = sd(x)))

Faire des tests de comparaison de moyennes avec des anova, ou encore en utilisant la fonction m.test() de {KefiR}.

library(KefiR) # cf. barre de recherche du site pour voir comment installer KefiR.

m.test(fac2rcbd_out$bookRowCol$Prod_fruits,paste(fac2rcbd_out$bookRowCol$Pesticide,fac2rcbd_out$bookRowCol$Arrosage))