Définir des variables


Travailler dans R consiste à définir des objets et à les manipuler pour obtenir des résultats qui nous intéressent. Dans cette page, nous allons voir comment définir des variables ainsi que les manipulations de base sur celles-ci. On verra successivement des variables scalaires, des variables vectorielles, des variables matricielles puis un autre type de variables appelés data frame. Il existe d'autres types de variables, dont nous verrons certains plus loin, mais ces quatre types de variables sont les plus importants et ceux que nous utiliserons de manière majoritaire dans cette introduction.


I. Les variables scalaires


Une variable de R peut être vue comme un emplacement de la mémoire de l'ordinateur auquel on a donné un nom pour y faire référence. Définir une variable dans R consiste donc à demander au logiciel de réserver un emplacement dans sa mémoire, de mémoriser de l'information dans cet emplacement, et de donner un nom - choisi assez librement - à cet emplacement pour pouvoir ultérieurement manipuler l'information qu'on y a stocké. Une manière très simple d'effectuer ces opérations est montré dans l'exemple qui suit:

mon_compteur<-10

Cette commande a pour effet de définir un emplacement en mémoire, de nommer cet emplacement "mon_compteur" et d'y stocker la valeur 10.

Un deuxième exemple pourrait être:

mon_texte<-"R est facile"

où cette fois la valeur stockée dans l'emplacement (nommé "mon_texte") est un texte.

Il est évidemment possible de faire effectuer des calculs à R et de stocker le résultat dans une variable, comme par exemple:

resultat<-20*10-7*(42-12)

Le résultat n'apparait pas à l'écran, mais est mémorisé dans la variable nommée "resultat".On peut obtenir la valeur stockée dans une variable en tapant simplement le nom de cette variable:

resultat
## [1] -10

Remarquons ici que R précède ses réponses d'un chiffre entre crochet, [1] dans cet exemple, qui ne fait pas vraiment partie de la réponse (voir plus loin).

On aurait obtenu le même résultat en tapant:

20*10-7*(42-12)
## [1] -10

La différence étant que dans ce dernier cas, le résultat n'est pas mémorisé par l'ordinateur et ne peut pas être réutilisé, comme par exemple ici:

resultat<-resultat+20
resultat
## [1] 10

L'instruction resultat<-resultat+20 a pour effet de prendre la valeur actuelle de "resultat" (soit, -10 au moment de l'exécution de cette commande), d'ajouter 20 à cette valeur (ce qui donne -10+20=10) et d'affecter cette dernière valeur à "resultat", qui vaut donc dorénavant 10, comme le montre l'exemple. Le terme à droite de "<-" est un exemple simple d'expression R. Ces expressions peuvent être beaucoup plus compliquées. Voici un autre exemple:

n<-10
r<-2
p<-0.6
combin<-factorial(n)/(factorial(r)*factorial(n-r)) #factorial(n) est une fonction fournissant n! en sortie
proba<-combin*(p**r)*((1-p)**(n-r))
proba
## [1] 0.01061683

Cet exemple montre le calcul de la probabilité d'obtenir 2 fois un événement lors de 10 essais s'il a une probabilité d'occurrence de 0.6 à chaque essai. Dans les expressions R, les opérateurs habituels sont utilisables: '+' pour l'addition, '-' pour la soustraction, '*' pour la multiplication et '/' pour la division. L'opérateur '**' est l'opérateur d'exponentiation: 'x**y' signifie qu'on élève x à la puissance y. Signalons également que l'ordre de priorité des opérateurs est le même que d'habitude, et peut être modifié en utilisant des parenthèses. L'expression 'factorial(n)' fait appel à une fonction interne au langage, la fonction factorial. R possède une riche collection de fonctions, dont la liste et l'utilisation peut se trouver dans les manuels d'aide de R. Nous en emploierons quelques unes dans le cadre de cette introduction. Signalons également qu'il est possible d'enrichir l'environnement de travail en ajoutant des fonctions définies par nous-mêmes, utilisateurs. Cette fonctionnalité est expliquée dans le chapitre suivant.


II. Les variables vectorielles


Si on voulait obtenir toute la distribution binomiale évoquée au paragraphe précédent, c'est-à-dire obtenir la probabilité associée à chaque valeur possible de r, on pourrait imaginer de créer une liste de valeurs de r possibles, et la liste des probabilitées associées. La manière la plus directe de créer la liste des valeurs de r est la suivante:

r<-c(0,1,2,3,4,5,6,7,8,9,10)

La fonction 'c(, , ...)' réserve 11 emplacements en mémoire et y stocke successivement les valeurs de 0 à 10. Ce résultat est ensuite nommé r, qui devient donc une variable contenant plusieurs valeurs, mémorisées de manière contigue en mémoire. Une telle variable est appelée variable vectorielle. On peut évidemment afficher le contenu de cette variable de la manière suivante:

r
##  [1]  0  1  2  3  4  5  6  7  8  9 10

Si on calcule une expression (scalaire) avec une variable vectorielle, le logiciel répète simplement l'expression pour chacune des composantes du vecteur. Ainsi, par exemple, on peut effectuer les opérations suivantes:

r2<-r**2
r2
##  [1]   0   1   4   9  16  25  36  49  64  81 100
combin<-factorial(n)/factorial(r)/factorial(n-r)
combin
##  [1]   1  10  45 120 210 252 210 120  45  10   1

Remarquez en particulier dans cette dernière expression comment R a géré ce mélange de fonctions agissant sur un scalaire (factorial(n)), sur un vecteur (factorial(r)) et sur une expression mélangeant scalaire et vecteur (factorial(n-r)): tout a été transformé en vecteurs et les fonctions ont été appliquées successivement à chacune des composantes du vecteur. En parlant des composantes du vecteur, elles sont donc accessibles collectivement, comme on vient de le voir, ou individuellement, en employant la syntaxe illustrée dans l'exemple qui suit:

distrib<-combin*(p**r)*((1-p)**(n-r)) # On obtient toute la distribution
distrib
##  [1] 0.0001048576 0.0015728640 0.0106168320 0.0424673280 0.1114767360
##  [6] 0.2006581248 0.2508226560 0.2149908480 0.1209323520 0.0403107840
## [11] 0.0060466176
proba_cum<-distrib[1]+distrib[2]+distrib[3] # Probabilité que r soit inférieur à 4

Cette dernière ligne illustre comment on peut utiliser individuellement les composantes d'un vecteur: dans l'exemple, les 3 premières composantes sont additionnées pour fournir la probabilité (cumulée) que r soit inférieur à 4. Remarquez également l'affichage du vecteur 'distrib': à chaque retour à la ligne, le logiciel affiche le numéro de la première composante de la ligne correspondante (ce qui explique la raison pour laquelle, quand le résultat obtenu est un scalaire, R précède ce résultat de [1]). Outre ces créations explicites (avec la fonction 'c()') ou implicites (par conversion, comme dans les exemples ci-dessus) de vecteurs, il existe d'autres possibilités pour créer des vecteurs. On verra dans un autre chapitre la manière de générer des séquences de nombres, par exemple.

Une autre méthode permet de créer des vecteurs:

v<-vector(mode="numeric",length=5)
v
## [1] 0 0 0 0 0

La fonction 'vector' permet donc de créer un vecteur de longueur donnée (length=5) et contenant un type de données spécifié (mode="numeric"). Les deux arguments de cette fonction ont un format particulier: le nom de l'argument est spécifié, suivi du symbole '=' et de la valeur désirée pour l'argument. On parle dans ce cas d'arguments nommés. L'intérêt de cette syntaxe est qu'il n'est plus nécessaire de se souvenir de l'ordre des différents arguments d'une focntion si on nomme les arguments. Par contre, si on ne les nomme pas, il est essentiel de respecter un ordre pré-déterminé, ce qui est parfois difficile. Les déclarations suivantes sont par exemple équivalentes:

w<-vector(mode="character", length=3)
w<-vector("character",3)
w<-vector(length=3, mode="character")
w<-vector("character", length=3)

mais, par contre, la syntaxe suivante est incorrecte:

w<-vector(3,"character")
## Warning in vector(3, "character"): NAs introduits lors de la conversion automatique
## Error in vector(3, "character"): la taille de vecteur ne peut être NA/NaN

L'argument 'mode' peut prendre plusieurs valeurs: 'numeric', 'character', mais aussi 'complex', 'integer', 'raw'... Nous utiliserons essentiellement les deux premiers. L'argument 'length' est évidemment nécessairement un nombre entier positif.
Dans certains cas, on ne désire que traiter des parties du vecteur dans une opération: on emploie alors la notation 'v[a:b]' pour accéder aux élements de 'v' allant de 'v[a]' à v[b]'. Un exemple d'une utilisation de ce type est le suivant: supposons que les deux vecteurs 'v' et 'w' contiennent les résultats obtenus pour deux groupes qu'on souhaite comparer avec le test de Mann-Whitney (voir cours de stats). Dans ce test, on met les données de 'v' et de 'w' ensemble, on calcule le rang (la position) de chaque donnée dans cette liste de toutes les observations, puis on somme séparément les rangs obtenus par les données provenant de 'v' et de 'w'. Une procédure simple pour effectuer cette opération est la suivante:

vw<-c(v,w) # Réunion de toutes les données dans un vecteur unique
r<-rank(vw,ties.method="average") # Attribution des rangs à toutes les données. Rang moyen en cas de valeurs égales
rv<-r[1:nv] # Rang des valeurs de v. nv est le nombre de valeurs dans ce vecteur
rw<-r[(nv+1):(nv+nw)] # Rang des valeurs de w. nw est le nombre de valeurs dans ce vecteur.


III. Les variables matricielles


Dans certaines circonstances, les données qu'on souhaite traiter peuvent se présenter sous la forme d'un tableau de nombres (ou de chaines de caractères). On parlera dans ce cas de variables matricielles. On peut créer ce type de variables de différentes manières, nous en verrons deux:

v<-c(5,10,15,20,25,30,35,40,45,50)
m<-array(data=v,dim=c(2,5))
m
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    5   15   25   35   45
## [2,]   10   20   30   40   50

Le vecteur 'v' a donc été ventilé dans un tableau dont les dimensions sont de 2 lignes et 5 colonnes. A signaler que les valeurs provenant du vecteur sont introduites de manière à remplir successivement les colonnes du tableau. Signalons également la manière dont R précède les lignes avec, entre crochets, l'indice de la ligne suivi d'une virgule, et précède les colonnes avec, entre crochets, l'indice de la colonne précédé d'une virgule. L'accès à un élément du tableau se fait en utilisant ces indices; ainsi, par exemple:

m[2,4]
## [1] 40

Une deuxième manière, très similaire mais un peu plus souple, de définir un tableau est la suivante:

n<-matrix(data=v,nr=2,nc=5)
n
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    5   15   25   35   45
## [2,]   10   20   30   40   50

On peut, avec cette dernière fonction, changer le mode de remplissage du tableau:

n2<-matrix(data=v,nr=2,nc=5,byrow=TRUE)
n2
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    5   10   15   20   25
## [2,]   30   35   40   45   50

Certaines expressions peuvent être étendues aux tableaux, comme c'était déjà le cas pour les vecteurs. Par exemple:

s<-n+n2+100
s
##      [,1] [,2] [,3] [,4] [,5]
## [1,]  110  125  140  155  170
## [2,]  140  155  170  185  200

Signalons encore, pour terminer ce paragraphe, que la syntaxe de la fonction 'matrix' donne un bel exemple de la souplesse de R pour l'appel à une fonction, en permettant d'omettre certains arguments (des valeurs par défaut sont prévues), et en utilisant les arguments nommés, comme discuté plus haut. Toutes les invocations qui suivent donnent des résultats corrects:

matrix() # Aucun argument...
##      [,1]
## [1,]   NA
# NA signifie 'Not Available', aucune valeur n'est disponible
matrix(10) # Un seul argument scalaire
##      [,1]
## [1,]   10
matrix(v) # Un seul argument vectoriel
##       [,1]
##  [1,]    5
##  [2,]   10
##  [3,]   15
##  [4,]   20
##  [5,]   25
##  [6,]   30
##  [7,]   35
##  [8,]   40
##  [9,]   45
## [10,]   50
# 1 colonne, par défaut


IV. Les data frames


Dans de nombreuses situations, on souhaitera traiter des données qui ont été collectées et stockées dans un fichier externe à R. Un format fréquent pour ces données est de présenter une ligne par individu de l'expérience, et les différentes mesures prises sur cet individu figure dans les colonnes successives. Le tableau ci-dessous donne un exemple de ce type de disposition de données.

IndividuNomRaceAgeSexePoids
1BillTeckel10M9
2TexChihuahua8M3
3DaisyBichon6F5
4MaxCaniche7M6
5ReineTeckel8F7
6JoyCaniche6F6
7MedorChihuahua9M4
8ColossusBichon5M4
9FluteTeckel6F7
10StickCaniche9M6


Si on excepte la première ligne, cette disposition ressemble à celle des variables matricielles vues au paragraphe précédent. Il existe toutefois une différence notable: si les données d'une même colonne sont bien de même type (comme pour les tableaux), les données de colonnes différentes sont de types différents, en général. Dans l'exemple, la colonne 'Age' contient des valeurs numériques alors que la colonne 'Race' contient des valeurs alphanumériques ('character', dans le jargon de R). Si on veut mémoriser ce type de structure de données dans R, on a donc besoin d'un nouveau type de variables: ce type est appelé data frame. Le plus souvent, une variable de ce type sera créée de manière implicite suite à la lecture d'un fichier externe de données (voir plus loin). Dans ce paragraphe, après avoir introduit le concept, nous allons maintenant montrer une manière explicite de créer ce type de structure:

Age<-c(10,8,6,7,8,6,9,5,6,9)
Race<-c("Teckel","Chihuahua","Bichon","Caniche","Teckel","Chihuahua","Caniche","Bichon","Teckel","Caniche")
d<-data.frame(Age,Race)
d
##    Age      Race
## 1   10    Teckel
## 2    8 Chihuahua
## 3    6    Bichon
## 4    7   Caniche
## 5    8    Teckel
## 6    6 Chihuahua
## 7    9   Caniche
## 8    5    Bichon
## 9    6    Teckel
## 10   9   Caniche

L'accès aux données se fait en utilisant un nouvel opérateur, l'opérateur '$'. Par exemple, pour accéder à la colonne (au vecteur...) des âges, il suffit de taper:

d$Age
##  [1] 10  8  6  7  8  6  9  5  6  9

Les manipulations sur ces structures se font aussi facilement que sur les tableaux. Pour illustrer ce fait, imaginons que l'on souhaite créer une nouvelle variable vectorielle qui contiendrait les âges corrigés pour leur moyenne (c'est-à-dire les âges auxquels on aurait soustrait la moyenne de tous les âges). On voudra en outre stocker ce nouveau vecteur dans la structure elle-même. Pour réaliser cela, il suffit de taper:

d$Agec<-d$Age-mean(d$Age)

Une nouvelle colonne, nommée 'Agec', aura été créée dans la data frame 'd', comme en témoigne le résultat suivant:

d
##    Age      Race Agec
## 1   10    Teckel  2.6
## 2    8 Chihuahua  0.6
## 3    6    Bichon -1.4
## 4    7   Caniche -0.4
## 5    8    Teckel  0.6
## 6    6 Chihuahua -1.4
## 7    9   Caniche  1.6
## 8    5    Bichon -2.4
## 9    6    Teckel -1.4
## 10   9   Caniche  1.6