Objets et vecteurs
Pour l'instant, nous avons utilisé R pour faire la même chose que les calculatrices de nos ordinateurs et téléphones. Alors, c'est super, mais franchement pas passionnant. Avançons un peu.
Objets
Les objets sont globalement n'importe quoi (on pourrait presque les appeler "trucs") que l'on crée afin de stocker quelque chose (une ou plusieurs valeurs, mais aussi plein d'autres machins que nous verrons plus tard). Pour en créer un, on tape une ou des lettres, suivies de <- (chevron orienté vers l'objet et tiret), puis une valeur ou un calcul :
x <- 2
x
#[1] 2
x <- 46 + 2
x
#[1] 48
youpi <- 33 * 2
youpi
#[1] 66
Pour dupliquer un objet, on peut simplement le mettre dans un autre objet (en clair, stocker la valeur d'un objet dans un autre). Ça nous permet d'appliquer des calculs ou modifier la valeur de notre objet initial tout en conservant cet objet.
x <- 42
y <- x
y
#[1] 42
y <- y + 3
# oui, j'ai additionné un objet avec lui-même, c'est tout à fait faisable
y
#[1] 45 ==> y a une nouvelle valeur
x
#[1] 42 ==> x n'a pas été modifié
Règles de nommage
On est assez libres pour nommer nos objets, mais il y a tout de même quelques règles à respecter:
- Ne pas commencer par un chiffre
- Les seuls caractères spéciaux autorisés sont
.et_. - Il faut respecter la casse (les majuscules, tout ça). Je conseille donc de les éviter de façon générale.
Vecteurs
Les objets que nous venons de voir ne contiennent qu'une seule valeur, c'est pas d'une utilité folle. Alors on peut créer plein d'objets que l'on peut utiliser :
a <- 1
b <- 3
c <- 6
d <- 2
a+b+c+d
#[1] 12
Ou même :
e <- a+b+c+d
e
#[1] 12
Mais considérons que nous mesurons des diamètres de lèvres d'amphores (on va dire Dressel 43261) en centimètres. Ça nous donne :
diam1 <- 34
diam2 <- 42
diam3 <- 38
diam4 <- 12
On peut faire la somme totale des diamètres (même si c'est un peu débile).
diam1 + diam2 + diam3 + diam4
#[1] 126
Si on veut la moyenne, sans utiliser de fonction spécifique, on additionne toutes nos valeurs et puis on divise par le nombre d'observations.
(diam1 + diam2 + diam3 + diam4)/2
#[1] 63
On va pas se mentir, c'est fastidieux. Il y a évidemment moyen de mettre plusieurs valeurs dans un objet et un objet composé d'une suite de valeurs est un vecteur.
Pour créer un vecteur, il faut ajouter quelques éléments lors de la création d'un objet, c(). Le c est pour combine. Pour l'utiliser :
diametres <- c(34.2,42.6,38.66,13.8)
diametres
#[1] 34.20 42.60 38.66 13.80
1 : Oui, je sais ça n'existe pas, on se calme les antiquisant.e.s et les protohistorien.ne.s ! Et moi, mon humour me convient
Opérations sur les vecteurs
Et maintenant, si on fait une opération sur un vecteur, que se passe-t-il ? Eh bien testons :
diametres/4
#[1] 8.550 10.650 9.665 3.450
L'opération (ici une division par 4), a été appliquée pour chaque élément de notre vecteur. C'est quelque chose qui peut être assez pratique, par exemple pour changer d'échelle :
# Nos diamètres sont en cm et nous allons les convertir en mm, donc les multiplier par 10
diametres*10
#[1] 342.0 426.0 386.6 138.0
Maintenant, on va tester le croisement de deux vecteurs, en divisant les hauteurs par les diamètres :
hauteurs <- c(98,95,87,102)
hauteurs / diametres
#[1] 2.865497 2.230047 2.250388 7.391304
Les éléments de chaque vecteur ont été divisés l'un par l'autre et un à un. En clair, le premier élément du premier vecteur a été divisé par le premier élément du deuxième vecteur, le deuxième élément du premier vecteur par le deuxième du deuxième vecteur, etc. Cela implique plusieurs choses, déjà, comment faire une opération globale sur un vecteur ? (genre, on veut la moyenne des éléments qui le composent). Ensuite, si chaque élément de chaque vecteur est associé un à un, c'est que leur position est importante (et indexée).
Modulo
Le modulo, c'est souvent peu considéré par les gens qui débutent en exploration de données en SHS, alors qu'il s'agit d'une fonction bien pratique. Le modulo donne le reste d'une division pour garder le résultat entier. Par exemple, le modulo de 5/2, c'est 1; on divise 5 par 2, mais comme le résultat doit être un entier, le calcul complet est 5/2 = 4/2+1 (puisque 4/2 c'est 2, donc un entier, on est bons !). Le signe du modulo dans R, c'est %%.
5 %% 2
#[1] 1
On va tester si nos hauteurs sont des nombres pairs :
hauteurs %% 2
#[1] 0 1 1 0
Seules la première et la dernière hauteur sont paires avec un modulo 2 = 0.
Position des éléments d'un vecteur
La position d'un élément est donnée par quelque chose que nous voyons depuis le début sans forcément comprendre à quoi il correspond. À chaque fois qu'un résultat est affiché, il y a un chiffre entre [] (crochets) qui apparaît. On va créer un vecteur un peu long et voir ce que ça donne :
x <- c(23,14,543,34,5,63,66,4,76,5,67,9,7,80,32,32,24,79,7,64,32,14,52,6,784,74,543,78)
x
#[1] 23 14 543 34 5 63 66 4 76 5 67 9 7 80 32 32 24 79 7 64 32 14
#[23] 52 6 784 74 543 78
On pourrait s'amuser à compter, mais je vous donne tout de suite la réponse, il s'agit de la position dans le vecteur du premier élément de la ligne. Ce même [], vous permet donc d'identifier une position. À l'inverse, si on tape le nom d'un vecteur suivi de [] (avec un numéro de position dedans), on peut obtenir directement la valeur située à cette position.
x[3]
#[1] 543
Et on peut tester si 52 est bien à la position 23 :
x[23]
#[1] 52
Il existe beaucoup de subtilités et de méthodes pour chercher et jouer sur les positions des valeurs, nous verrons cela plus tard, lors de nos traitements de données.
Les fonctions
On se demandait, un peu plus haut, comment appliquer des opérations à un vecteur dans son ensemble et pas valeur par valeur (genre, la moyenne de notre vecteur x). Pour ça, R est rempli de fonctions. Une fonction, globalement, c'est une commande qui fait plein de trucs avec des options. Dans R, sa forme est le nom de la fonction suivi des paramètres et options entre parenthèse. Les fonctions bénéficient de l'autocomplétion, dans RStudio, si vous en tapez les premières lettres, celles qui commencent pareil sont proposées. La première proposition est sélectionnée par défaut (mais vous pouvez déplacer la sélection avec les flèches du clavier) et hop, on tape sur entrée; la fonction sélectionnée est insérée avec ses parenthèses et le curseur est placé entre les parenthèses, il n'y a donc plus qu'à taper vos paramètres si vous en avez, sinon directement Entrée (enfin il faut très souvent au moins indiquer les données sur lesquelles appliquer la fonction). Quelques exemples de fonctions :
La valeur minimale :
min(x)
#[1] 4
La valeur maximale (oui, ce monde est fou):
max(x)
#[1] 784
La valeur la plus basse et la plus haute (Les deux précédentes commandes à la fois quoi) :
range(x)
#[1] 4 784
La moyenne (surprenant, non ?):
mean(x)
#[1] 100.6071
La médiane (bon, tout le monde distingue bien moyenne et médiane ?) :
median(x)
#[1] 33
La variance et l'écart-type (standard deviation en anglais)
var(x)
#[1] 36168.17
sd(x)
#[1] 190.1793
La somme totale des éléments du vecteur:
sum(x)
#[1] 2817
Les valeurs uniques:
unique(x)
#[1] 23 14 543 34 5 63 66 4 76 67 9 7 80 32 24 79 64 52 6 784 74 78
Le nombre de valeurs:
length(x)
#[1] 28
Le type d'objet et une description (vraiment) très rapide:
str(x)
# num [1:28] 23 14 543 34 5 63 66 4 76 5 ...
Là, on a plein de trucs bien pratiques (les noms sont explicites, je vous laisse deviner):
summary(x)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 4.00 12.75 33.00 100.61 74.50 784.00
La différence entre chaque valeur successive :
diff(x)
#[1] -9 529 -509 -29 58 3 -62 72 -71 62 -58 -2 73 -48 0 -8 55 -72
#[19] 57 -32 -18 38 -46 778 -710 469 -465
La somme cumulative, élément après élément :
cumsum(x)
#[1] 23 37 580 614 619 682 748 752 828 833 900 909 916 996 1028 1060 1084 1163
#[19] 1170 1234 1266 1280 1332 1338 2122 2196 2739 2817
Et, bien sûr, on peut imbriquer des fonctions :
length(unique(x))
#[1] 22
Il y a 22 valeurs uniques dans notre vecteur.
La fonction help
La fonction la plus importante est probablement help(). Je me doute bien que vous n'allez pas apprendre toutes les fonctions et leurs paramètres par cœur, c'est là que la fonction help vient vous sauver dans la nuit sombre de vos errances de tapage de commandes. Son utilisation est très simple, help(*nom de la commande sur laquelle vous avez besoin d'un petit coup de main*):
help(median)
La commande vous affiche l'explication de la commande dans RStudio avec une description de la commande, des paramètres, des valeurs liées, une petite citation bibliographique parce que c'est toujours bien et, pour finir, des exemples (vous avez vraiment tout là).
Alternativement, vous pouvez taper :
?median
qui fonctionne à peu près pareil.
Les arguments
Je n'ai pas développé dessus plus haut, mais les fonctions peuvent prendre plusieurs arguments qui sont séparés par des virgules, c'est par exemple de cette façon que nous avons utilisé c() en lui donnant plusieurs arguments.
La fonction help() nous donne l'ensemble des arguments possibles pour une fonction. Par exemple :
help(mean)
Nous retourne :
mean(x, ...)
Default S3 method:
mean(x, trim = 0, na.rm = FALSE, ...)
Ici, trois arguments indiqués (même si plus sont disponibles). x indique les données, les arguments moins évidents sont explicités en plus bas dans l'aide (même x d'ailleurs). Un argument est donné sous la forme argument =. L'ordre indiqué dans l'aide est celui par défaut. Soit vous indiquez vos arguments dans l'ordre et vous n'avez pas besoin de les nommer, soit vous devez les indiquer avec argument =.
Générer des suites de valeurs
Dans certains cas, il peut être utile de générer des suites de nombres, en créant des données ou même en les interrogeant (on verra tout ça un peu plus dans les pages suivantes).
Les : vont générer une suite incrémentée de 1, depuis la valeur qui précède les : à celle qui leur succède :
x <- 1:28
Pour contrôler un peu plus la génération des suites, la fonction seq() est très pratique dans son usage basique, seq(min, max, pas) :
seq(2,35,4)
#[1] 2 6 10 14 18 22 26 30 34
#La plus haute valeur est 34 car par pas de 4, on ne pouvait pas tomber pile sur 35 et que cette valeur ne peut pas non plus être dépassée.
Un type de génération que j'aime bien, c'est celle qui crée des distributions normales aléatoires. Il s'agit de rnorm(nombre de valeurs, moyenne, écart-type) :
rnorm(60,14,10)
# [1] 1.3233514 -4.6967920 16.5148504 14.7976726 16.7438506 5.4070413
# [7] 9.8316416 29.1486864 11.6293148 27.2316077 10.0895463 9.7892557
# Bon là, je n'ai mis que les deux premières lignes, 60 valeurs quand même...
Il existe évidemment plein d'autres types de générateurs de suites de plein de types différents, je vous laisse explorer.
Les principaux types de données
Pour l'instant, nous n'avons joué qu'avec des chiffres, or, R (imprononçable ce truc) peut utiliser trois principaux types de données : numériques, textuelles et logiques. Les fonctions str() ou class() peuvent nous indiquer le type de données qui compose un objet.
Données numériques
Je ne vais pas revenir dessus, ce sont des nombres entiers ou réels, seule précision, R distingue entier et réels.
x <- seq(3,68,8)
str(x)
#num [1:9] 3 11 19 27 35 43 51 59 67
class(x)
#[1] "numeric"
Pour forcer le type entier ("integer"), il faut rajouter un L.
x <- c(3L,5L,2L,6L)
class(x)
#[1] "integer"
Données textuelles
Ce sont des chaînes de caractères, donc du texte. Pour utiliser du texte, il faut le placer entre guillemets ".
x <- c("chat", "chien", "tortue", "souris")
x
#[1] "chat" "chien" "tortue" "souris"
class(x)
#[1] "character"
Données logiques
Ce sont des booléens de type OUI/NON, PRÉSENT/ABSENT, VRAI/FAUX, etc. Dans R, ces données prennent les formes TRUE et FALSE (tout en majuscule). Pour l'instant, ça ne vous paraît peut-être pas très intéressant de prime abord, mais je vous assure que pour pas mal de traitements de données, c'est vite très puissant (on commencera à voir ça avec les tests conditionnels).
x <- c(TRUE,FALSE,FALSE,FALSE,TRUE,TRUE)
class(x)
#[1] "logical"
D'ailleurs un truc bien pratique, c'est qu'on peut facilement compter les TRUE. Si vous avez vu des tableaux de données un peu anciens, on a souvent l'habitude de coder la présence/absence en 1,0. En réalité, c'est un peu ce système qui est utilisé dans R, avec les TRUE qui valent 1 et les FALSE qui valent 0. Donc on peut, par exemple, compter le nombre de positifs de notre vecteur x avec la fonction sum() :
sum(x)
#[1] 3
Types de vecteurs
Il faut garder à l'esprit qu'un vecteur ne peut être que d'un type. Si vous mélangez des types en créant un vecteur, vous verrez que la fonction class() ne retourne qu'un seul argument. Une idée à considérer, c'est qu'en mélangeant des types, le vecteur prendra le type le plus complexe (complexité : logique < numérique < texte) :
| Types de données en entrée | Type de vecteur en sortie |
|---|---|
| Logique + numérique | numérique |
| Logique + texte | texte |
| numérique + texte | texte |
| Logique + numérique + texte | texte |
Et démontration dans R :
x <- c(TRUE,2)
class(x)
#[1] "numeric"
Oui, TRUE est converti en 1 et FALSE en 0. C'est logique avec ce qu'on a vu juste avant.
x <- c(TRUE,"chat")
class(x)
#[1] "character"
Si on regarde notre vecteur, TRUE est maintenant indiqué "TRUE", c'est donc bien du texte.
x <- c(2,"chat")
class(x)
#[1] "character"
Comme pour le précédent exemple, 2 est maintenant affiché "2".
x <- c(2,"chat","TRUE")
class(x)
#[1] "character"
Plus que du texte, c'est un peu triste, non ?
Les listes
Un vecteur ne peut être que d'un type, mais il peut arriver (très rarement dans les analyses statistiques, mais ça peut arriver dans le nettoyage et la gestion de données) de vouloir utiliser un genre de vecteur qui possède plusieurs types de données. Il existe un type d'objet dans ce cas, la liste, associée à la fonction list() :
x <- list(2,"chat","TRUE")
class(x)
#[1] "list"
Et si vous l'observez, avec la fonction View() par exemple, vous verrez que chaque valeur a conservé son type.
Les valeurs manquantes
Nous traitons toutes et tous des données parfois un peu lacunaires pour plein de raisons, données anciennes, origines multiples, changements de direction, projets un peu en bazar, trop de pluie ou de moustiques sur le terrain, inondation dans la réserve, enfin plein de trucs qui vous évoqueront probablement des souvenirs... Ces aléas aboutissent à des données dans lesquelles certaines valeurs sont manquantes. Dans R, les valeurs manquantes sont indiquées "NA", pour les bases SQL, elles sont "null" (NULL existe aussi dans R, mais on ne va pas l'aborder maintenant), enfin en fonction des logiciels ou des environnements, elles sont indiquées de façon différente.
Nous allons concrètement voir ce que donne la présence de données manquantes dans un vecteur :
x <- c(3,56,84,34,NA,42)
class(x)
#[1] "numeric"
#Même avec un NA, R identifie ce vecteur comme numérique
length(x)
#[1] 6
#Le NA est bien compté dans le nombre de valeurs
mean(x)
#[1] NA
La présence d'un NA rend l'opération impossible, il s'agirait de diviser 5 valeurs (les présentes) par 6 (le nombre de valeurs).
Un moyen de contourner ce problème, ajouter une option pour dire à R qu'il peut omettre les NA, na.rm = TRUE :
mean(x, na.rm=TRUE)
#[1] 43.8
Les opérateurs logiques
Les opérateurs logiques peuvent sembler un peu relous, a priori, mais je vous assure que dès que vous commencerez à automatiser des choses (oui, oui, vous le ferez bien un jour !), c'est fort utile. Les opérateurs logiques, comme vous pouvez vous en douter, retournent un vecteur logique. L'idée est de comparer des vecteurs et le résultat qui nous est retourné prend la forme d'un vecteur logique.
Les principaux opérateurs logiques :
| Opérateur | Signification | ||
|---|---|---|---|
| == | égal à | ||
| != | différent de | ||
| > | strictement supérieur à | ||
| >= | supérieur ou égal à | ||
| < | strictement inférieur à | ||
| <= | inférieur ou égal à | ||
| Association d'arguments | |||
| \ | union (OU) | ||
| \ | \ | OU (sur un argument de longueur 1) | |
| & | intersect (ET) | ||
| && | ET (sur un argument de longueur 1) | ||
| ! | inversion |
La principale différence entre & / && et | / || est que le double caractère fonctionne sur un argument unique et correspond à des caractères de programmation standard, d'où le fait qu'on le retrouve très souvent utilisé. Sur des vecteurs, on aura tendance à utiliser les caractères simples, mais cela demande un peu de pratique (sauf si vous avez l'habitude du SQL ou ce genre de pratiques et là, ça devrait le faire).
Quelques exemples :
x <- c(1,2,3,5,7,8,9)
y <- c(3,2,4,6,6,8,3)
y == 6
#[1] FALSE FALSE FALSE TRUE TRUE FALSE FALSE
x == y
#[1] FALSE TRUE FALSE FALSE FALSE TRUE FALSE
x != y
#[1] TRUE FALSE TRUE TRUE TRUE FALSE TRUE
y < x
#[1] FALSE FALSE FALSE FALSE TRUE FALSE TRUE
x <= y
#[1] TRUE TRUE TRUE TRUE FALSE TRUE FALSE
x == y | y < x
#[1] FALSE TRUE FALSE FALSE TRUE TRUE TRUE
x == y & y < x
#[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE