Ce cours est visible gratuitement en ligne.

J'ai tout compris !
Effectuez vos études statistiques avec R

Effectuez vos études statistiques avec R

Mis à jour le vendredi 11 juillet 2014
  • Moyenne
Le langage R

Les listes

Avant d'attaquer plus en détails les manipulations avancées des matrices et tableaux de données que vous venez de découvrir lors des précédents chapitres, nous allons découvrir une dernière structure de données qui nous sera utile par la suite: les listes.

Un vrai fourre-tout...

Les listes sont peut être les objets qui peuvent paraître les moins faciles à appréhender pour certains débutants, mais, lorsque l'on a discerné leur fonctionnement et leur utilité elles se révèlent être d'une grande aide!
En effet, comme le suggère le titre de cette section, il est possible de mettre à peu près tout ce que l'on souhaite au sein d'une liste.

De manière plus formelle, une liste est un ensemble d'éléments ordonnés les uns à la suite des autres dans une même structure indexée. Détail important : ces éléments peuvent être de types différents.
L'utilité d'une telle structure est ainsi de regrouper dans un même objet une série d'autres objets appartenant par exemple à une même expérience ou observation. L'utilité réelle est alors de pouvoir traiter tous les éléments d'une même liste en une seule fois grace à des fonctions adaptées, que vous découvrirez d'ici la fin de ce chapitre!

Comme souvent, le plus simple reste d'illustrer cette partie très théorique avec un petit exemple. Imaginons que je suis un entraineur de saut en longueur et que j'ai 10 athlètes du même age sous mes ordres. Au cours d'un mois ils effectuent plusieurs sauts mesurés et je souhaite enregistrer ces valeurs pour y effectuer quelques analyses dessus par la suite.
On pourrait facilement stocker tout ça dans un tableau de données me direz vous. Oui, mais le problème est le suivant: chaque athlète n'a pas le même nombre de sauts mesurés (hors les lignes et les colonnes d'un tableau de données doivent être respectivement de la même longueur) et je veux pouvoir facilement modifier les données.
Une solution est alors de créer un vecteur par athlète, ce vecteur pouvant être modifié facilement pour rajouter de nouvelles mesures par exemple.
Maintenant, si je veux pouvoir traiter les données des différents athlètes de manière rapide et sans avoir à répéter 10 fois la même procédure, il me faut regrouper ces données dans une même structure : une liste!

Afin d'avoir une idée plus précise de ce à quoi ressemble une liste, voici comme elles sont représentées sous R.

Image utilisateur

Aperçu de la structure d'une liste sous R.

Vous pouvez donc voir que la liste appelée athletes contient différents vecteurs de type numérique et que ces vecteurs sont associés à un index dont le nom commence par un dollar ($). Nos mesures relatives à nos sauteurs en longueur sont donc bien présentes. Il nous reste donc à savoir comment créer et manipuler une telle structure.

Création et manipulation de listes

Création de liste

La fonction servant à créer des listes est assez facile à retenir étant donnée qu'elle s'appelle... list(). Cette fonction prend autant d'arguments que d'objets à insérer dans la liste. Vous avez donc plusieurs approches pour créer notre liste appelée athletes :

  • soit créer une liste vide grace à la simple commande athletes <- list() et la remplir par la suite avec les éléments que l'on souhaite y stocker (on apprendra comment faire sous peu).

  • soit, préciser chaque nouvel objet à insérer comme nouvel argument. Si l'on souhaite stocker 10 vecteurs, on spécifiera alors 10 arguments, chacun étant l'un de nos vecteurs de données. A la manière de ce que l'on a vu précédemment avec les tableaux de données, il est possible de directement donner un nom à l'élément de la liste en le précisant lors de sa création grace à la syntaxe nom_de_l_element=objet. Le code ci-dessous illustre comment créer notre liste athletes en y insérant les 10 vecteurs correspondant aux performances de nos 10 athlètes dont les noms sont spécifiés lors de la création de l'objet.

> # Création de la liste
>athletes <- list(Didier=c(630, 625, 628, 599, 635, 633, 622), Jules=c(610, 590, 595, 582, 601, 603), Pierre=c(644, 638, 639, 627, 642, 633, 639), Matthieu=c(622, 625, 633, 641, 610), Georges=c(561, 572, 555, 569, 653, 549, 558, 561), Khaled=c(611, 621, 619, 618, 623, 614, 623), Guillaume=c(599, 601, 612, 609, 607, 608, 594), Hermann=c(624, 630, 631, 629, 634, 618, 622), Carlos=c(528, 531, 519, 533, 521), Keith=c(513))
> # Et affichage
> athletes
$Didier
[1] 630 625 628 599 635 633 622

$Jules
[1] 610 590 595 582 601 603

$Pierre
[1] 644 638 639 627 642 633 639

$Matthieu
[1] 622 625 633 641 610

$Georges
[1] 561 572 555 569 653 549 558 561

$Khaled
[1] 611 621 619 618 623 614 623

$Guillaume
[1] 599 601 612 609 607 608 594

$Hermann
[1] 624 630 631 629 634 618 622

$Carlos
[1] 528 531 519 533 521

$Keith
[1] 513

Nous avons donc maintenant une liste contenant nos dix vecteurs recensant les performances respectives de nos 10 athlètes, et chacun de ces objets est associé au nom que nous avons défini lors de la création de la liste.

Maintenant que nous avons créé notre liste, commençons à voir comment nous pouvons la manipuler.

Noms et longueur

A l'instar de ce que l'on a vu pour les vecteurs par exemple, il est possible d'interroger mais aussi de mettre à jour les attributs noms et longueur d'une liste grace aux fonctions names() et length().

> # On interroge l'attribut noms de notre liste
> names(athletes)
 [1] "Didier"    "Jules"     "Pierre"    "Matthieu"  "Georges"   "Khaled"    "Guillaume" "Hermann"  
 [9] "Carlos"    "Keith"    
> # Ainsi que l'attribut longueur 
> length(athletes)
[1] 10
Indexation

Le principe d'indexation d'une liste reste similaire à celui rencontré précédemment avec les vecteurs. Cependant, afin notamment de distinguer ces deux structures, leur syntaxe diffère légèrement. En effet, là où les vecteurs demandent un paire de crochets pour accéder à un élément précis, les listes utilisent des doubles crochets comme illustré ici : liste[[element]].
Les index spécifiés peuvent alors être les noms associés aux éléments de la liste, ou alors leur index numérique comme le montre les quelques exemples ci dessous.

> # On sélectionne les données relatives à Hermann
> athletes[["Hermann"]]
[1] 624 630 631 629 634 618 622
> # ou alors les données du premier athlète de la liste
> athletes[[1]]
[1] 630 625 628 599 635 633 622

Il est possible de sélectionner plusieurs éléments d'une liste en même temps mais il faut pour cela utiliser une syntaxe avec des crochets simples et non des crochets doubles. Pour sélectionner les deux premiers éléments de notre liste il faudra donc employer la commande athlete[1:2].

Comme nous l'avons déjà vu, les éléments d'une liste sont affichés précédés d'un symbole $ par R. Cela vient du fait qu'il est aussi possible d'accéder aux éléments d'une liste en utilisant la syntaxe liste$element. Dans ce cas, il faut spécifier le nom de l'élément et non son identifiant numérique.

> # Cette syntaxe permet d'extraire l'élément de la liste associé au nom "Khaled" 
> athletes$Khaled
[1] 611 621 619 618 623 614 623

Il est important de savoir que, lorsque l'on accède à un élément d'une liste, l'objet qui nous est alors retourné est du type de l'objet contenu par cet élément de la liste. Dans notre exemple, tous les éléments stockés sont des vecteurs. Ainsi, si l'on extrait n'importe lequel de ces éléments, cela nous renverra un vecteur et cet objet pourra donc être traité comme un vecteur.

> # On extrait l'élément "Guillaume" ce qui nous renvoie le vecteur contenu dans cet élément de la liste
> athletes[["Guillaume"]]
[1] 599 601 612 609 607 608 594
>
> # Comme il s'agit d'un vecteur, on peut directement indexer cet objet comme pour n'importe quel autre vecteur
> athletes[["Guillaume"]][1:3]
[1] 599 601 612
>
> # Dans ce cas, on aura sélectionné les 3 premiers éléments du vecteur contenu dans l'élément de la liste "Guillaume".

Traitement en série des éléments d'une liste

Nous l'avons vu plus tôt en introduction, l'un des intérêts de stocker des objets dans une liste est de pouvoir regrouper une série de données dans une même structure mais, surtout, de pouvoir les traiter tous ensemble de manière très rapide.

Continuons avec notre exemple de performances enregistrées pour différents athlètes et imaginons que nous souhaitions, pour chacun des athlètes, extraire rapidement plusieurs informations telles que :

  • la meilleure et la pire performance

  • le nombre de sauts effectués,

  • et la longueur moyenne sautée.

La fonction lapply()

Il serait possible de le faire individuellement pour chacun des éléments de la liste mais ceci serait à la fois long et peu pratique. Heureusement, R fournit une fonction permettant d'appliquer automatiquement et indépendamment une même fonction aux différents éléments d'une liste. Il s'agit de la fonction lapply(x, FUN, ...).

De prime abord sa syntaxe peut paraître barbare mais elle est en fait assez simple:

  • l'argument x indique le nom de la liste à traiter. Dans notre cas il s'agit de la liste contenue dans notre variable athletes.

  • l'argument FUN indique le nom de la fonction que l'on souhaite appliquer à tous les éléments de la liste. Si l'on souhaite par exemple appliquer la fonction min(), il suffira d'indiquer le nom de la fonction sans les parenthèses, c'est à dire min.

  • comme nous l'avons déjà vu précédemment les trois petits points présents dans certaines définition de fonction signifient que l'on peut spécifier autant d'arguments qu'on le souhaite. Dans ce cas précis, ces arguments optionnels seront les arguments que prendra la fonction que nous avons choisie d'appliquer aux éléments de notre liste. Si l'on souhaite par exemple spécifier l'argument na.rm de la fonction min(), il faudra le préciser comme argument optionnel à la fonction lapply().

Pour illustrer cette partie théorique voici la ligne de code qui permet d'extraire la pire performance (la valeur minimale) de chacun des athlètes de notre liste.

> lapply(athletes, min, na.rm=T)
$Didier
[1] 599

$Jules
[1] 582

$Pierre
[1] 627

$Matthieu
[1] 610

$Georges
[1] 549

$Khaled
[1] 611

$Guillaume
[1] 594

$Hermann
[1] 618

$Carlos
[1] 519

$Keith
[1] 513

Une seule ligne! En une seule ligne nous avons effectué cette opération qui aurait pu sembler assez répétitive au départ. Pour ceux qui n'auraient pas encore saisi le fonctionnement précis de la fonction lapply() voici comment R procède. La fonction va prendre la liste donnée en argument (dans notre cas la liste appelée athletes) et va en extraire les éléments un par un. Dans notre cas, ces éléments seront donc les vecteurs que nous y avons stockés. Pour chacun de ces vecteurs, R va alors lancer la commande min(x, na.rm=T) car nous lui avons dit que nous souhaitions appliquer la fonction min() avec l'argument na.rm=T.

En gardant la même logique, à vous de vous exercer et d'extraire les informations restantes, à savoir, pour chacun des athlètes :

  • 1) la meilleure performance

  • 2) le nombre de sauts effectués,

  • et 3) la longueur moyenne sautée.

Les réponses se trouvent ci dessous.

> # 1)
> lapply(athletes, max)
$Didier
[1] 635

$Jules
[1] 610

$Pierre
[1] 644

$Matthieu
[1] 641

$Georges
[1] 653

$Khaled
[1] 623

$Guillaume
[1] 612

$Hermann
[1] 634

$Carlos
[1] 533

$Keith
[1] 513

> # 2)
> lapply(athletes, length)
$Didier
[1] 7

$Jules
[1] 6

$Pierre
[1] 7

$Matthieu
[1] 5

$Georges
[1] 8

$Khaled
[1] 7

$Guillaume
[1] 7

$Hermann
[1] 7

$Carlos
[1] 5

$Keith
[1] 1

> # 3)
> lapply(athletes, mean)
$Didier
[1] 624.5714

$Jules
[1] 596.8333

$Pierre
[1] 637.4286

$Matthieu
[1] 626.2

$Georges
[1] 572.25

$Khaled
[1] 618.4286

$Guillaume
[1] 604.2857

$Hermann
[1] 626.8571

$Carlos
[1] 526.4

$Keith
[1] 513
La fonction sapply()

Nous avons donc extrait les informations que nous souhaitions! Et ce de manière réellement rapide. Cependant, certains d'entre vous l'auront peut être remarqué, les objets renvoyés sont des listes. Ceci peut s'avérer problématique car certaines fonctions n'acceptent pas les listes comme argument d'entrée.
Imaginons par exemple que je souhaite maintenant effectuer d'autres analyses comme :

  • classer automatiquement les athlètes selon leur meilleure performance moyenne

  • extraire les informations sur la distribution des valeurs enregistrées pour chaque athlètes grâce à la fonction summary().

Deux problèmes se poseront alors à nous. Tout d'abord, comme nous venons de le voir, de nombreuses fonctions comme la fonction sort() ne prennent pas de liste en entrée. Mais aussi, dans le second cas, les résultats seront difficilement visibles et réutilisables par la suite.

Pour pallier ces problèmes il existe une autre fonction au fonctionnement similaire à celui de lapply(). Il s'agit de sapply(). L'unique différence est que cette dernière essaiera, dans la mesure du possible, de retourner les résultats sous la forme d'un objet plus facilement réutilisables tels que des vecteurs, matrices ou tableaux de données. Si l'on extrait à nouveau les moyennes des performances de chaque athlète à l'aide de cette nouvelle fonction nous obtiendrons par exemple un vecteur comme illustré ci dessous.

> sapply(athletes, mean)
   Didier     Jules    Pierre  Matthieu   Georges    Khaled Guillaume   Hermann    Carlos     Keith 
 624.5714  596.8333  637.4286  626.2000  572.2500  618.4286  604.2857  626.8571  526.4000  513.0000

Le résultat, retourné sous la forme d'un vecteur, sera alors plus simple à lire mais aussi à réutiliser. Par exemple, cet objet sera facilement traité par la fonction sort(), ce qui n'aurait pas été possible avec une liste.

> sort(sapply(athletes, mean))
    Keith    Carlos   Georges     Jules Guillaume    Khaled    Didier  Matthieu   Hermann    Pierre 
 513.0000  526.4000  572.2500  596.8333  604.2857  618.4286  624.5714  626.2000  626.8571  637.4286

Dans notre second exemple, l'utilisation de la fonction summary() sur nos données, le problème était donc plus celui de la lisibilité mais aussi potentiellement celui d'une réutilisation future des résultats. La fonction sapply() pourra alors retourner un résultat plus propre comme illustré ci dessous.

> sapply(athletes, summary)
        Didier Jules Pierre Matthieu Georges Khaled Guillaume Hermann Carlos Keith
Min.     599.0 582.0  627.0    610.0   549.0  611.0     594.0   618.0  519.0   513
1st Qu.  623.5 591.2  635.5    622.0   557.2  616.0     600.0   623.0  521.0   513
Median   628.0 598.0  639.0    625.0   561.0  619.0     607.0   629.0  528.0   513
Mean     624.6 596.8  637.4    626.2   572.2  618.4     604.3   626.9  526.4   513
3rd Qu.  631.5 602.5  640.5    633.0   569.8  622.0     608.5   630.5  531.0   513
Max.     635.0 610.0  644.0    641.0   653.0  623.0     612.0   634.0  533.0   513

Dans ce cas, R nous aura retourné une jolie matrice qui sera alors bien plus facile à lire et manipuler que la liste renvoyée par la fonction lapply().

Les listes hétérogènes

Dans le cas que nous avons étudié tout au long de ce chapitre notre liste était homogène : elle contenait seulement des vecteurs. Mais ce n'est pas tout le temps le cas. Comme nous l'avons vu en introduction, les listes peuvent permettre de stocker tout type d'objet et ainsi être hétérogènes.

Cette propriété des listes est souvent utilisée par le langage R lui même. En effet, lorsque l'on fait appel à une fonction elle ne peut vous retourner qu'un seul objet. Mais, parfois, certaines fonctions (souvent des fonctions permettant d'effectuer des tests statistiques) ont besoin de renvoyer beaucoup d'informations. Les listes entrent alors en jeu et vous en avez déjà vues sans le savoir!

Par exemple, lorsque nous avons précédemment effectué des tests de corrélation, R nous retournait différentes informations comme le nombre d'observation, le coefficient de corrélation, les intervalles de confiance,...

Pour vous rafraichir la mémoire, vous pouvez par exemple calculer la corrélation entre les performances de deux athlètes ayant le même nombre de mesures : Didier et Pierre. En faisant appel à votre mémoire vous devriez être capables de retrouver le code ci dessous.

> cor.test(athletes$Didier, athletes$Pierre)

        Pearson's product-moment correlation

data:  athletes$Didier and athletes$Pierre 
t = 2.5567, df = 5, p-value = 0.05085
alternative hypothesis: true correlation is not equal to 0 
95 percent confidence interval:
 -0.0007476893  0.9610299842 
sample estimates:
      cor 
0.7527342

Le retour de fonction affiché par R n'y parait pas mais il s'agit en fait d'une liste. La seule différence est que R fait appel de manière invisible à d'autres fonctions pour rendre le résultat plus facile à lire. Mais si l'on veut stocker ce résultat dans une variable on peut alors s'apercevoir qu'il s'agit bien d'une liste comme en témoigne le code suivant.

> test <- cor.test(athletes$Didier, athletes$Pierre)
> is.list(test)
[1] TRUE

Je ne vous avais pas menti. ^^

Par contre, si vous essayez d'afficher cette variable vous vous rendrez alors compte qu'il ne sera toujours pas affiché comme une liste. Ceci peut être problématique si l'on veut avoir un aperçu de sa structure et que l'on souhaite extraire une information particulière de cette liste, comme par exemple la p-value associée au coefficient de corrélation.

Pour pallier ce problème nous pouvons utiliser deux fonctions. Une que vous connaissez déjà (names()) et une nouvelle : str() (structure en abrégé). La fonction names() vous retournera donc les noms des différents éléments de la liste. La fonction str() aussi, mais elle donne aussi un aperçu rapide de ce qui y est contenu.

> # On affiche seulement les noms
> names(test)
[1] "statistic"   "parameter"   "p.value"     "estimate"    "null.value"  "alternative" "method"     
[8] "data.name"   "conf.int"   
>
> # Et la structure de l'objet
> str(test)
List of 9
 $ statistic  : Named num 2.56
  ..- attr(*, "names")= chr "t"
 $ parameter  : Named num 5
  ..- attr(*, "names")= chr "df"
 $ p.value    : num 0.0508
 $ estimate   : Named num 0.753
  ..- attr(*, "names")= chr "cor"
 $ null.value : Named num 0
  ..- attr(*, "names")= chr "correlation"
 $ alternative: chr "two.sided"
 $ method     : chr "Pearson's product-moment correlation"
 $ data.name  : chr "athletes$Didier and athletes$Pierre"
 $ conf.int   : atomic [1:2] -0.000748 0.96103
  ..- attr(*, "conf.level")= num 0.95
 - attr(*, "class")= chr "htest"

Le retour de la fonction str() peut vous paraître un peu barbare. Nous n'allons pas nous y attarder mais, comme vous pouvez le constater, elle liste les éléments de notre structure et, après les deux points, en donne le type et éventuellement un rapide aperçu. Cela permet de savoir à quoi correspondent ces éléments. Par exemple, la p-value est contenue dans l'élément p.value dont nous pouvons avoir un aperçu des valeurs.

Connaissant ceci on peut alors facilement extraire cette information précise.

> test$p.value
[1] 0.0508476

Vous venez de découvrir une nouvelle structure de données mais aussi la puissance de R pour traiter des données en série grâce à des fonctions telles que lapply() ou sapply().

La bonne nouvelle est que des fonctions de la sorte existent aussi pour d'autres objets telles que les tableau de données ou les matrices, ce que l'on découvrira dès le prochaine chapitre!

Ce tutoriel est en cours de rédaction. D'autres chapitres abordant la création de différents graphiques ou présentant des notions plus avancées viendront voir le jour. Soyez patient et n'hésitez pas à relire les chapitres publiés tout comme à faire part de vos remarques ou suggestions à l'aide des commentaires.

Découvrez aussi ce cours en...

Exemple de certificat de réussite
Exemple de certificat de réussite