Dans ce chapitre, nous reviendrons brièvement sur l’histoire de R et la philosophie qui entoure le logiciel. Nous donnerons quelques conseils pour son installation (dataframe, vecteur, matrice, etc.) et comment les manipuler avec des exemples appliqués. Si vous maîtrisez déjà R, nullement besoin de lire ce chapitre !
::: {.bloc_package data-latex=""} Dans ce chapitre, nous utiliserons principalement les packages suivants :
Je me rajoute une phrase ici ok ?
R est à la fois un langage de programmation et un logiciel libre (sous la licence publique générale GNU) dédié à l'analyse statistique et soutenu par une fondation : R foundation for Statistical computing. Il est principalement écrit en C et Fortran.
R a été créé par Ross Ihaka et Robert Gentleman à l'Université d'Auckland en Nouvelle-Zélande. Si vous avez un jour l'occasion de passer dans le coin, une plaque est affichée dans le département de statistique de l'université, ça mérite le détour (figure \@ref(fig:fig01)). Une version expérimentale a été publiée en 1996, mais la première version stable ne date que de 2000, il s'agit donc d'un logiciel relativement récent si on le compare à ses concurrents SPSS (1968), SAS (1976) et Stata (1984).
library(dplyr) knitr::include_graphics('images/introduction/plaque.jpg', dpi = NA)
R a cependant réussi à s'imposer tant dans le milieu de la recherche que dans le secteur privé. Pour s'en convaincre, il suffit de lire l'excellent article concernant la popularité des logiciels d'analyse de données tiré du site r4stats.com{target="_blank"} (figure \@ref(fig:fig02)).
knitr::include_graphics('images/introduction/r_citations.jpg', dpi = NA)
Les nombreux atouts de R justifient largement sa popularité sans cesse croissante :
Un des principaux attrait de R est la quantité astronomique de packages actuellement disponibles. Un package est un ensemble de nouvelles fonctionnalités développées par un·e ou plusieurs utilisateurs·trices de R et mises à disposition de l'ensemble de la communauté. Par exemple, le package ggplot2 est dédié à la réalisation de graphiques; les packages data.table et plyr permettent de manipuler des tableaux de données; le package car apporte de nombreux outils pour faciliter l'analyse de modèles de régressions, etc. Ce partage des packages rend accessible à tous des méthodes d'analyses complexes et récentes et favorise grandement la reproductibilité de la recherche. Cependant, ce fonctionnement implique quelques désavantages :
Il nous semble important de relativiser d'emblée la portée du dernier point. Il est rarement nécessaire de lire et analyser le code source d'un package pour s'assurer de sa fiabilité. Nous ne sommes pas des spécialistes de tous les sujets et il peut être extrêmement ardu de comprendre la logique d'un code écrit par une autre personne. Nous vous recommandons donc de privilégier l'utilisation de packages qui :
Toujours pour nuancer notre propos, il convient de distinguer package de package! Certains d'entre eux sont des ensembles très complexes de fonctions permettant de réaliser des analyses poussées alors que d'autres sont des projets plus modestes dont l'objectif principal est de simplifier le travail des utilisateurs·trices. Ces derniers ressemblent à des petites boites à outils et font généralement moins l'objet d'une vérification intensive.
Pour conclure cette section, l'illustration partagée sur Twitter par Darren L Dahly résume avec humour la force du logiciel R et de sa communauté (figure \@ref(fig:fig03)) : R apparait clairement comme une communauté hétéroclyte, mais diversifiée et adaptable.
knitr::include_graphics('images/introduction/softwares_and_cars.jpeg', dpi = NA)
Dans ce livre, nous détaillerons les packages utilisés dans chaque section avec un encadré spécifique, accompagné de l'icône présenté à la figure \@ref(fig:fig04).
knitr::include_graphics('css/images/package.png', dpi = NA)
Dans cette section, nous vous proposons une visite de l'environnement de travail classique R.
La première étape pour travailler avec R est bien sûr de l'installer. Pour ce faire, il suffit de visiter le site web de CRAN{target="_blank"} et de télécharger la dernière version de R en fonction de votre système d'exploitation : Windows, Linux ou Mac. Une fois installé, si vous démarrez R immédiatement, vous aurez alors accès à une console, plutôt rudimentaire, attendant sagement vos instructions (figure \@ref(fig:fig05)).
knitr::include_graphics('images/introduction/r_console.jpeg', dpi = NA)
Notez que vous pouvez aussi télécharger des version plus anciennes de R en allant sur ce lien{target="_blank"}. Ceci peut être intéressant lorsque vous voulez reproduire des résultats d'une autre étude ou que certains packages ne sont plus disponibles dans les nouvelles versions.
Rares sont les utilisateurs·trices de R qui préfèrent travailler directement avec la console classique. Nous vous recommandons vivement d'utiliser RStudio, soit un environnement de développement dédié à R, offrant une intégration très intéressante d'une console, d'un éditeur de texte, d'une fenêtre de visualisation des données, d'une autre pour les graphiques, d'un accès à la documentation, etc. En d'autres termes, si R est un vélo minimaliste, RStudio permet d'y rajouter des freins, des vitesses, un porte-bagage, des gardes-boues et une selle confortable. Vous pouvez télécharger{target="_blank"} et installer RStudio sur Windows, Linux et Mac. La version de base est gratuite, mais l'entreprise qui développe ce logiciel propose aussi des versions commerciales du logiciel qui assurent essentiellement un support technique. Il existe d'autres environnements de développement pour travailler avec R (VisualStudio, Jupyter, Tinn-R, Radiant, RIDE, etc.), mais RStudio offre à ce jour la meilleure option en terme de facilité d'installation, de prise en main et de fonctionnalités proposées (voir l'interface de RStudio à la figure \@ref(fig:fig06)).
knitr::include_graphics('images/introduction/r_studio_01.jpeg', dpi = NA)
Avant d'aller plus loin, notez que :
knitr::include_graphics('images/introduction/r_studio_02.jpeg', dpi = NA)
Une fois ces détails réglés, vous pouvez ouvrir votre première feuille de code en allant dans l'onglet File / New File / R Script. Votre environnement est maintenant découpé en quatre fenêtres (figure \@ref(fig:fig08)) :
knitr::include_graphics('images/introduction/r_studio_03.jpeg', dpi = NA)
Prenons un bref exemple, tapez la syntaxe suivante dans l'éditeur de code (fenêtre 1 à la figure \@ref(fig:fig08)) :
ma_somme <- 4+4
Sélectionnez ensuite cette syntaxe (mettre en surbrillance avec la souris), quand vous utilisez le raccourci Ctrl+Enter ou cliquez sur le bouton Run (avec la flèche verte), cette syntaxe est envoyée à la console qui l'exécute immédiatement. Notez que rien ne se passe tant que le code n'est pas envoyé à la console. Il s'agit donc de deux étapes distinctes : écrire son code, puis l'envoyer à la console. Vous constaterez également qu'un objet ma_somme est apparu dans votre environnement et que sa valeur est bien 8. Votre console se "souvient" de cette valeur, elle est actuellement stockée dans votre mémoire vive sous le nom de ma_somme (figure \@ref(fig:fig09)).
knitr::include_graphics('images/introduction/r_studio_04.jpeg', dpi = NA)
Pour conclure cette section, nous vous invitons à enregistrer votre première syntaxe R (File / Save As) dans un fichier .R que vous pouvez appeler par exemple "mon_premier_script.R". Fermez ensuite RStudio, redémarrez le et ouvrez (File / Open File) votre fichier "mon_premier_script.R". Vous pouvez constater que votre code est toujours présent, mais que votre environnement est vide tant que vous n'exécutez pas votre syntaxe. En effet, lorsque vous fermez RStudio, l'environnement est vidé pour libérer de la mémoire vive. Ceci peut poser problème lorsque certains codes sont très longs à exécuter, nous verrons donc plus tard comment enregistrer l'environnement en cours pour le recharger par la suite.
Dans la section sur la Philosophie de R, nous avons souligné la place centrale jouée par les packages. Notez que les termes paquet et plus rarement librarie sont parfois utilisés en français. Voyons ensemble comment installer un package intitulé lubridate, qui nous permettra plus tard de manipuler des données temporelles.
Pour installer un package, vous devez être connecté à Internet puisque R va accéder au répertoire de packages CRAN pour télécharger le package et l'installer sur votre machine. Cette opération est réalisée avec la fonction install.packages
.
install.packages("lubridate")
Notez qu'une fois que le package est installé, vous n'aurez plus besoin de le refaire. Le package est disponible localement sur votre ordinateur, à moins de le désinstaller explicitement avec la fonction remove.packages
.
CRAN est le répertoire officiel des packages de R. Vous pouvez cependant télécharger des packages provenant d'autres sources. Très souvent, les packages sont disponibles sur le site web GitHub{target="_blank"} et l'on peut même y trouver des versions en développement avec des fonctionnalités encore non intégrées dans la version sur CRAN. Reprenons le cas de lubridate, sur GitHub, il est disponible à la page suivante{target="_blank"}. Pour l'installer nous devons d'abord installer un autre package appelé remotes (depuis CRAN).
install.packages("remotes")
Maintenant que nous disposons de remotes, nous pouvons utiliser la fonction d'installation remotes::install_github
pour directement télécharger lubridate depuis GitHub.
remotes::install_github("tidyverse/lubridate")
Maintenant que lubridate est installé, nous pouvons le charger dans notre session actuelle de R et accéder aux fonctions qu'il propose. Pour cela, suffit d'utiliser la fonction library
. Conventionnellement, l'appel des packages se fait au tout début du script que vous rédigez. Rien ne vous empêche de le faire au fur et à mesure de votre code, mais ce dernier perd alors en lisibilité. Notez qu'à chaque nouvelle session (redémarrage de R), il faudra recharger les packages dont vous avez besoin.
library(lubridate)
Si vous obtenez un message d'erreur du type :
Cela signifie que le package que vous tentez de charger n'est pas encore installé sur votre ordinateur. Dans ce cas, réessayer de l'installer avec la fonction install.packages
. Si le problème persiste, vérifiez que vous n'avez pas fait une faute de frappe dans le nom du package. Vous pouvez également redémarrer RStudio et réessayer d'installer le package.
Lorsque vous installez des packages dans R, vous téléchargez aussi leur documentation. Tous les packages de CRAN disposent d'une documentation, ce n'est pas forcément vrai pour GitHub. Dans RStudio, vous pouvez accéder à la documentation des packages dans l'onglet Packages (figure \@ref(fig:fig010)). Vous pouvez utiliser la barre de recherche pour retrouver rapidement un package installé. Si vous cliquez sur le nom du package, vous accédez directement à sa documentation dans cette fenêtre.
knitr::include_graphics('images/introduction/rstudio_packages.jpeg', dpi = NA)
Vous pouvez également accéder à ces informations en utilisant la syntaxe suivante dans votre console :
help(package = 'lubridate')
Souvent, vous aurez besoin d'accéder à la documentation d'une fonction spécifique d'un package. Affichons la documentation de la fonction now
de lubridate :
help(now, package = 'lubridate')
ou plus simplement :
?lubridate::now
Vous pouvez aussi utiliser le raccourci suivant :
?now
Si vous connaissez le nom d'une fonction, mais vous ne vous souvenez plus à quel package elle appartient, lancez une recherche en utilisant un double point d'interrogation :
??now
Vous découvrirez ainsi que la fonction now
n'existe pas que dans lubridate, ce qui souligne l'importance de bien connaître les packages que l'on installe et que l'on charge dans notre session !
Maintenant que nous avons fait le tour de l'environnement de travail, nous pouvons passer aux choses sérieuses, soit les bases du langage R.
R est un langage de programmation. Il vous permet de communiquer avec votre ordinateur pour lui donner des tâches à accomplir. Dans cette section, nous aborderons les bases du langage. Ce type de section introductive à R est présente dans tous les manuels sur R ; elle est donc incontournable. À la première lecture, elle vous semblera probablement aride, et ce, d'autant plus que nous ne réalisons pas d'analyse à proprement parler. Gardez en tête que l'analyse de données requiert au préalable une phase de structuration de ces dernières, opération qui nécessite la maîtrise des notions abordées dans cette section. Nous vous recommandons une première lecture de ce chapitre pour comprendre quelles manipulations que vous pouvez effectuer avec R, la lecture des chapitres suivants dédiés aux analyses statistiques, puis de consulter à nouveau cette section au besoin. Notez aussi que la maîtrise des différents objets et opérations de base de R ne s’acquiert qu'en pratiquant. Vous gagnerez cette expertise au fil de vos prochains codes R, période durant laquelle vous pourrez consulter ce chapitre tel un guide de référence des différents objets et notions fondamentales de R.
Une introduction à un langage de programmation se doit de commencer par le rite de passage Hello World. Il s'agit d'une forme de tradition consistant à montrer aux nouveaux utilisateurs·trices comment afficher le message "Hello World" à l'écran avec le langage en question.
print("Hello World")
Bravo ! Vous venez officiellement de faire votre premier pas dans R !
Dans R, nous passons notre temps à manipuler des objets à l'aide d'expressions. Prenons un exemple concret, si vous tapez la syntaxe 4 + 3
, vous manipulez deux objets (4 et 3) au travers d'une expression indiquant que vous souhaitez obtenir la somme des deux objets.
4 + 3
Cette expression est correcte, R comprend vos indications et effectue le calcul.
Il est possible d'enregistrer le résultat d'une expression et de la conserver dans un nouvel objet. On appelle cette opération déclarer une variable.
ma_somme <- 4 + 3
Concrètement, nous venons de demander à R d'enregistrer le résultat de 4 + 3
dans un espace spécifique de notre mémoire vive. Si vous regardez dans votre fenêtre Environment, vous verrez en effet qu'un objet appelé ma_somme est actuellement en mémoire et a pour valeur 7.
Notez ici que le nom des variables ne peut être composé que de lettres, de chiffres, de points (.) et de tiret bas (_) et doit commencer par une lettre. R est sensible à la casse; en d'autre termes, les variables Ma_somme
, ma_sommE
, ma_SOMME
, et MA_SOMME
renvoient toutes à un objet différent. Attention donc aux fautes de frappe. Si vous déclarez une variable en utilisant le nom d'une variable existante, la première est écrasée par la seconde :
age <- 35 age age <- 45 age
Portez alors attention aux noms de variables que vous utilisez et réutilisez. Réutilisons notre objet ma_somme
dans une nouvelle expression :
ma_somme2 <- ma_somme + ma_somme
Avec cette nouvelle expression, nous indiquons à R que nous souhaitons déclarer une nouvelle variable appelée ma_somme2
, et que cette variable aura pour valeur ma_somme + ma_somme
, soit 7 + 7
. Sans surprise, ma_somme2
a pour valeur 14.
Notez que la mémoire vive (l'environnement) est vidée lorsque vous fermez R. Autrement dit, R perd complètement la mémoire lorsque vous le fermez. Vous pouvez bien sûr recréer vos objets en relançant les mêmes syntaxes. C'est pourquoi vous devez conserver vos feuilles de codes et ne pas seulement travailler dans la console. La console ne garde aucune trace de votre travail. Pensez donc à bien enregistrer votre code !
Nous verrons dans un autre chapitre comment sauvegarder des objets et les recharger dans une session ultérieure de R (LIEN SECTION). Ce type d'opération est pertinent quand le temps de calcul nécessaire à la production de certains objets est très long.
Dans R, nous manipulons le plus souvent nos objets avec des fonctions. Une fonction est elle-même un objet, mais qui a la particularité de pouvoir effectuer des opérations sur d'autres objets. Par exemple, déclarons l'objet taille
avec une valeur de 175.897 :
taille <- 175.897
Nous allons utiliser la fonction round
dont l'objectif est d'arrondir un nombre à virgule pour obtenir un nombre entier.
round(taille)
Pour effectuer leurs opérations, les fonctions ont généralement besoin d'arguments. Ici, taille
est un argument passé à la fonction round
. Si nous regardons la documentation de round
avec help(round)
, nous constatons que cette fonction prend en réalité deux argments : x et digits. Le premier est le nombre que nous souhaitons arrondir et le second le nombre de décimales à conserver. On peut lire dans la documentation que la valeur par défaut de digits est 0, ce qui explique que round(taille)
a produit le résultat de 176.
knitr::include_graphics('images/introduction/help_round.jpeg', dpi = NA)
Réutilisons maintenant la fonction round
mais en gardant une décimale :
round(taille, digits = 1)
Il est aussi possible que certaines fonctions ne requièrent pas d'arguments. Par exemple, la fonction now
va indiquer la date précise (avec l'heure) et n'a besoin d'aucun argument pour le faire :
now()
Par contre, si nous essayons de lancer la fonction round
sans argument, nous obtiendrons une erreur :
round()
Le message est très clair, round
a besoin d'au moins un argument pour fonctionner. Si au lieu d'un nombre, nous avions donné du texte à la fonction round
, nous aurions aussi obtenu une erreur :
round("Hello World")
À nouveau le message est très explicite : nous avons passé un argument non-numérique à une fonction mathématique. Lisez toujours vos messages d'erreurs qui vous permettront de repérer les coquilles et de corriger votre code !
Une fonction essentielle est la fonction print
qui permet d'afficher la valeur d'une variable.
print(ma_somme)
Depuis le début de ce chapitre, nous avons déclaré plusieurs variables et essentiellement des données numériques. Dans R, il existe trois principaux types de données de base :
15
et 15.3
."abcdefg"
.TRUE
) ou faux (FALSE
).Déclarons une variable pour chacun de ces types :
age <- 35 taille <- 175.5 adresse <- '4225 rue de la gauchetiere' proprietaire <- TRUE
Si vous avez un doute sur le type de données stockées dans une variable, vous pouvez utiliser la fonction typeof
. Par exemple, cela permet de repérer si des données qui sont supposées être numériques sont en fait stockées sous forme de texte comme dans l'exemple ci-dessous.
typeof(age) typeof(taille) tailletxt <- "175.5" typeof(tailletxt)
Notez également qu'il existe des types pour représenter l'absence de données :
NULL
,NA
,""
.age2 <- NULL taille2 <- NA adresse2 <- ''
Nous avons vu que les fonctions permettent de manipuler des objets. Nous pouvons également effectuer un grand nombre d'opérations avec des opérateurs.
Les opérateurs mathématiques permettent d'effectuer du calcul avec des données de type numérique.
df <- data.frame( Operateur = c("`+`","`-`","`*`","`/`", "`^`", "`**`", "`%%`", "`%/%`"), Description = c("Addition", 'Soustraction', 'Multiplication', 'Division', 'Exponentiel', 'Exponentiel', 'Reste de division', 'Division entière'), Syntaxe = c("`4 + 4`", "`4 - 3`", "`4 * 3`", "`12 / 4`", "`4 ^ 3`", '`4 ** 3`', "`15.5 %% 2`", "`15.5 %/% 2`"), Resultat = c(8,1,12,3,64,64,1.5,7)) show_table(df, col.names = c("Opérateur","Description","Syntaxe","Résultat"), caption = 'Opérateurs mathématiques' )
Les opérateurs relationnels permettent de vérifier des conditions dans R. Ils renvoient un booléen, TRUE
si la condition est vérifiée et FALSE
si ce n'est pas le cas.
df <- data.frame( Operateur = c("`==`","`!=`","`>`","`<`", "`>=`", "`<=`"), Description = c("Égalité", 'Différence', 'Est supérieur ', 'Est inférieur', 'Est supérieur ou égal', 'Est inférieur ou égal'), Syntaxe = c("`4 == 4`", "`4 != 4`", "`5 > 4`", "`5 < 4`", "`5 >= 4`", '`5 <= 4`'), Resultat = c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)) show_table(df, col.names = c("Opérateur","Description","Syntaxe","Résultat"), caption = 'Opérateurs relationnels' )
Les opérateurs logiques permettent de combiner plusieurs conditions :
L'opérateur ET permet de vérifier que deux conditions (l'une ET l'autre) sont TRUE. Si l'une des deux est FALSE, il renvoie FALSE.
L'opérateur OU permet de vérifier que l'une des deux conditions est TRUE (l'une OU l'autre). Si les deux sont FALSE, alors il renvoit FALSE.
L'opérateur NOT permet d'inverser une condition. Ainsi NOT TRUE est FALSE et NOT FALSE est TRUE.
df <- data.frame( Operateur = c("`&`","`|`","`!`"), Description = c("ET", "OU", "NOT"), Syntaxe = c("`TRUE & FALSE`", "`TRUE | FALSE`", "`! TRUE`"), Resultat = c(FALSE, TRUE, FALSE)) show_table(df, col.names = c("Opérateur","Description","Syntaxe","Résultat"), caption = 'Opérateurs logiques' )
Prenons le temps pour un rapide exemple :
A <- 4 B <- 10 C <- -5 # produit TRUE car A est bien plus petit que B et C est bien plus petit que A A < B & C < A # produit FALSE car si A est bien plus petit que B, # B est en revanche plus grand que c A < B & B < C # produit TRUE car la seconde condition est inversée A < B & ! B < C # produit TRUE car au moins une des deux conditions est juste A < B | B < C
Notez que l'opérateur ET est prioritaire sur l'opérateur OU et que les parenthèses sont prioritaires sur tous les opérateurs :
# produit TRUE car on commence par tester A < B puis B < C ce qui donne FALSE # on obtient ensuite # FALSE | A > C # enfin, A est bien supérieur à C, donc l'une des deux conditions est vraie A < B & B < C | A > C
Notez qu'en arrière-plan, les opérateurs sont en réalité des fonctions déguisées. Il est donc possible de définir de nouveau comportements pour les opérateurs. Il est par exemple possible d'additionner ou comparer des objets spéciaux comme des dates, des géométries, des graphes, etc.
Jusqu'à présent, nous avons utilisé des objets ne comprenant qu'une seule valeur. Or, des analyses statistiques nécessitent de travailler à des volumes de données bien plus grands. Pour stocker plusieurs valeurs, nous allons travailler avec plusieurs structures de données : les vecteurs, les matrices, les tableaux de données et les listes.
Les vecteurs sont la brique élémentaire de R. Ils permettent de stocker une série de valeurs du même type dans une seule variable. Pour déclarer un vecteur, on utilise la fonction c() :
ages <- c(35,45,72,56,62) tailles <- c(175.5,180.3,168.2,172.8,167.6) adresses <- c('4225 rue de la gauchetiere', '4223 rue de la gauchetiere', '4221 rue de la gauchetiere', '4219 rue de la gauchetiere', '4217 rue de la gauchetiere') proprietaires <- c(TRUE,TRUE,FALSE,TRUE,TRUE)
Nous venons ainsi de déclarer quatre nouvelles variables étant chacune un vecteur de longueur cinq (comprenant chacun cinq valeurs). Ces vecteurs représentent, par exemple, les réponses de plusieurs répondants à un questionnaire.
::: {.bloc_attention data-latex=""} Il existe dans R une subtilité à l'origine de nombreux malentendus : la distinction entre un vecteur de type texte et un vecteur de type facteur. Dans l'exemple précédent, le vecteur adresses est un vecteur de type texte. Chaque nouvelle valeur ajoutée dans le vecteur peut être n'importe quelle nouvelle adresse. Déclarons un nouveau vecteur qui contiendrait cette fois-ci la couleur des yeux de personnes ayant répondu au questionnaire.
couleurs_yeux <- c('marron','marron','bleu','bleu','marron','vert')
Contrairement aux adresses, il y a un nombre limité de couleurs que nous pouvons mettre dans ce vecteur. Il serait intéressant de fixer les valeurs possibles du vecteur pour s'assurer que de nouvelles ne soient pas ajoutées par erreur. Pour cela, nous pouvons convertir ce vecteur texte en vecteur de type facteur avec la fonction as.factor
.
couleurs_yeux_facteur <- as.factor(couleurs_yeux)
Notez qu'à présent, nous pouvons ajouter une nouvelle couleur dans le premier vecteur, mais pas dans le second.
couleurs_yeux[7] <- "rouge" couleurs_yeux_facteur[7] <- "rouge"
Le message d'erreur nous informe que nous avons tenté d'introduire une valeur invalide dans le facteur.
Les facteurs peuvent sembler restrictifs et très régulièrement, on préfère travailler avec de simples vecteurs de type texte plutôt que des facteurs. Cependant, de nombreuses fonctions d'analyse nécessitent d'utiliser des facteurs car ils assurent une certaine cohérence dans les données. Il est donc essentiel de savoir passer du texte au facteur avec la fonction as.factor
. À l'inverse, il est parfois nécessaire de revenir à une variable de type texte avec la fonction as.character
.
Notez que des vecteurs numériques peuvent aussi être convertis en facteurs :
tailles_facteur <- as.factor(tailles)
Cependant, si vous souhaitez reconvertir ce facteur en format numérique, il faudra passer dans un premier temps par le format texte :
as.numeric(tailles_facteur)
Comme vous pouvez le voir, convertir un facteur en valeur numérique renvoie des nombres entiers. Ceci est dû au fait que les valeurs dans un facteur sont recodées sous forme de nombres entiers, chaque nombre correspondant à une des valeurs originales (appelées niveaux). Si on convertit un facteur en valeurs numériques, on obtient donc ces nombres entiers.
as.numeric(as.character(tailles_facteur))
Morale de l'histoire, ne confondez pas les données de type texte et de type facteur. Dans le doute, vous pouvez demander à R quel est le type d'un vecteur avec la fonction class
.
class(tailles) class(tailles_facteur) class(couleurs_yeux) class(couleurs_yeux_facteur)
:::
Quasiment toutes les fonctions utilisent des vecteurs. Par exemple, on pourrait calculer la moyenne du vecteur ages en utilisant la fonction mean présente de base dans R.
mean(ages)
Cela démontre bien que le vecteur est la brique élémentaire de R ! Toutes les variables que nous avons déclarées dans les sections précédentes sont aussi des vecteurs, mais de longueur 1 !
Il est possible de combiner des vecteurs pour former des matrices. Une matrice est un tableau en deux dimensions (colonnes et lignes) généralement utilisé pour représenter certaines structures de données comme des images (pixels), effectuer du calcul matriciel ou plus simplement présenter des matrices de corrélations. Vous aurez rarement à travailler directement avec des matrices, mais il est bon de savoir ce qu'elles sont. Créons deux matrices à partir de nos précédents vecteurs.
matrice1 <- cbind(ages,tailles) # afficher la matrice 1 print(matrice1) # afficher les dimensions de la matrice 1 (1er chiffre : lignes; 2e chiffre : colonnes) print(dim(matrice1)) matrice2 <- rbind(ages, tailles) # afficher la matrice 2 print(matrice2) # afficher les dimensions de la matrice 2 print(dim(matrice2))
Comme vous pouvez le constater, la fonction cbind
permet de concaténer des vecteurs comme s'ils étaient les colonnes d'une matrice, alors que rbind
les combine comme s'ils étaient des lignes d'une matrice. La figure \@ref(fig:fig012) présente graphiquement le passage du vecteur à la matrice.
knitr::include_graphics('images/introduction/vecteur_to_matrix.png', dpi = NA)
Notez que vous pouvez transposer une matrice avec la fonction t
. Si nous essayons maintenant de comparer la matrice 1 et la matrice 2 nous allons avoir une erreur car elles n'ont pas les mêmes dimensions.
matrice1 == matrice2
En revanche, on pourrait transposer la matrice 1 et refaire cette comparaison :
t(matrice1) == matrice2
Le résultat souligne bien que l'on a les mêmes valeurs dans les deux matrices. Il est aussi possible de construire des matrices directement avec la fonction matrix
, ce que nous montrons dans la prochaine section.
S'il est rare de travailler avec des matrices, il est encore plus rare de manipuler des arrays. Un array est une matrice spéciale qui peut avoir plus que deux dimensions. Un cas simple serait un array en trois dimensions : lignes, colonnes, profondeur, que l'on pourrait se représenter comme un cube divisé en sous cubes. Au delà de trois dimensions, il devient difficile de se les représenter. Cette structure de données peut être utilisée pour représenter les différentes bandes spectrales d'une image satellitaire. Les lignes et les colonnes délimiteraient les pixels de l'image, la profondeur quant à elle délimiterait les différents bandes composant l'image (figure \@ref(fig:fig012)).
knitr::include_graphics('images/introduction/array.png', dpi = NA)
Créons un array en combinant trois matrices avec la fonction array
. Chacune de ces matrices sera composée respectivement de 1, de 2 et de 3 et aura une dimension de 5 x 5. L'array final aura donc des dimensions de 5 x 5 x 3.
mat1 <- matrix(1, nrow = 5, ncol = 5) mat2 <- matrix(2, nrow = 5, ncol = 5) mat3 <- matrix(3, nrow = 5, ncol = 5) mon_array <- array(c(mat1, mat2, mat3), dim = c(5,5,3)) print(mon_array)
S'il est rare de manipuler des matrices et des arrays, le DataFrame (tableau de données en français) est la structure de données la plus souvent utilisée. Dans cette structure, chaque ligne du tableau représente un individu et chaque colonne représente une caractéristique de ces individus. Ces colonnes ont des noms, ce qui permet facilement d'accéder à leurs valeurs. Créons un DataFrame à partir de nos quatres vecteurs et de la fonction data.frame
.
df <- data.frame( "age" = ages, "taille" = tailles, "adresse" = adresses, "proprietaire" = proprietaires )
show_table(df, caption = 'Un premier DataFrame')
Dans Rstudio, vous pouvez visualiser votre tableau de données avec la fonction View(df)
. Comme vous pouvez le constater, chaque vecteur est devenu une colonne de votre tableau de données df. La figure \@ref(fig:fig014) résume ce passage d'une simple donnée à un DataFrame en passant par un vecteur.
knitr::include_graphics('images/introduction/vecteur_to_dataframe.png', dpi = NA)
Plusieurs fonctions de base de R fournissent des informations importantes sur un DataFrame :
names
renvoie les noms des colonnes du DataFrame;nrow
renvoie le nombre de lignes;ncol
renvoie le nombre de colonnes.names(df) nrow(df) ncol(df)
Vous pouvez accéder à chaque colonne de df en utilisant le symbole $
ou [["nom_de_la_colonne"]]
. Recalculons ainsi la moyenne des âges :
mean(df$age) mean(df[["age"]])
La dernière structure de données à connaître est la liste. Elle ressemble à un vecteur, au sens où elle permet de stocker un ensemble d'objets les uns à la suite des autres. Cependant, une liste peut contenir n'importe quel type d'objets. Vous pouvez ainsi construire des listes de matrices, des listes d'arrays, des listes mixant des vecteurs, des graphiques, des DataFrames, des listes de listes...
Créons ensemble une liste qui va contenir des vecteurs et des matrices à l'aide de la fonction list.
ma_liste <- list(c(1,2,3,4), matrix(1, ncol = 3, nrow = 5), matrix(5, ncol = 3, nrow = 7), 'A' )
Il est possible d'accéder aux éléments de la liste par leur position dans cette dernière en utilisant les doubles crochets [[ ]]
:
print(ma_liste[[1]]) print(ma_liste[[4]])
Il est aussi possible de donner des noms aux éléments de la liste et d'utiliser le symbole $
pour y accéder. Créons une nouvelle liste de vecteurs et donnons leurs des noms avec la fonction names
.
liste2 <- list(c(35,45,72,56,62), c(175.5,180.3,168.2,172.8,167.6), c(TRUE,TRUE,FALSE,TRUE,TRUE) ) names(liste2) <- c("age",'taille','proprietaire') print(liste2$age)
Si vous avez bien suivi, vous devez avoir compris qu'un DataFrame n'est en fait rien d'autre qu'une liste de vecteurs avec des noms !
Bravo ! Vous venez de faire le tour des bases du langage R. Vous allez apprendre désormais à manipuler des données dans des DataFrames !
Dans cette section, vous apprendrez à charger et manipuler des DataFrames en vue d'effectuer des opérations classiques de gestion de données.
Il sera rarement nécessaire de créer vos DataFrames manuellement comme signalé dans la section précédente. Le plus souvent, vous disposerez de fichiers contenant vos données et utiliserez des fonctions pour les importer dans R sous forme d'un DataFrame. Les formats à importer les plus répandus sont :
Plus rarement, il se peut que vous ayez à charger des fichiers provenant de logiciels propriétaires :
Pour lire la plupart de ces fichiers, nous allons utiliser le package foreign dédié à l'importation d'une multitude de formats. Commencez donc par l'installer (install.packages("foreign")
). Nous allons charger cinq fois le même jeu de données enregistré dans des formats différents (csv, dbf, dta, sas7bdat et xlsx). Aussi, nous mesurerons le temps nécessaire pour importer chacun de ces fichiers avec la fonction Sys.time
.
Pour le format csv, il n'y a pas besoin d'utiliser un package puisque R dispose d'une fonction de base pour lire ce format.
t1 <- Sys.time() df1 <- read.csv("data/priseenmain/SR_MTL_2016.csv", header = TRUE, sep = ",", dec = ".", stringsAsFactors = FALSE) t2 <- Sys.time() d1 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df1 a ",nrow(df1),' observations', 'et ',ncol(df1),"colonnes\n")
Rien de bien compliqué ! Notez tout de même que :
sep = ","
), mais il pourrait tout aussi bien être un point virgule (sep = ";"
) une tabulation (sep = " "
), etc.dec = "."
), mais certains logiciels avec des paramètres régionaux de langue française (notamment Excel) exportent des fichiers csv avec des virgules comme séparateur de décimales (utilisez alors dec = ","
).header
indique si la première ligne (l'entête) du fichier comprend ou non les noms des colonnes du jeu de données (avec les valeurs TRUE
ou FALSE
). Il arrive que certains fichiers csv soient fournis sans entête et que les noms et descriptions des colonnes soient fournis dans un autre fichier.Pour lire un fichier dbase (.dbf), nous utilisons la fonction read.dbf
du package foreign installé précédemment :
library(foreign) t1 <- Sys.time() df2 <- read.dbf("data/priseenmain/SR_MTL_2016.dbf") t2 <- Sys.time() d2 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df2 a ",nrow(df2)," observations", "et ",ncol(df2),"colonnes\n")
Comme vous pouvez le constater, nous obtenons les mêmes résultats qu'avec le fichier csv.
Si vous travaillez avec des collègues utilisant le logiciel Stata, il se peut que ces derniers vous partagent des fichiers dta. Toujours en utilisant le package foreign, vous serez en mesure de les charger directement dans R.
t1 <- Sys.time() df3 <- read.dta("data/priseenmain/SR_MTL_2016.dta") t2 <- Sys.time() d3 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df3 a ",nrow(df3)," observations", "et ",ncol(df3),"colonnes\n", sep = "")
Pour importer un fichier sav provenant du logiciel statistique SPSS, utilisez la fonction read.spss
du package foreign.
t1 <- Sys.time() df4 <- as.data.frame(read.spss("data/priseenmain/SR_MTL_2016.sav")) t2 <- Sys.time() d4 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df4 a ",nrow(df4)," observations", "et ",ncol(df4),"colonnes\n", sep = "")
Pour importer un fichier sas7bdat provenant du logiciel statistique SAS, utilisez la fonction read.sas7bdat
du package sas7bdat. Installez préalablement le package (install.packages("sas7bdat")
) et chargez le (library(sas7bdat)
).
library(sas7bdat) t1 <- Sys.time() df5 <- read.sas7bdat("data/priseenmain/SR_MTL_2016.sas7bdat") t2 <- Sys.time() d5 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df5 a ",nrow(df5)," observations", "et ",ncol(df5),"colonnes\n", sep ="")
Lire un fichier Excel dans R n'est pas toujours une tâche facile. Généralement, nous recommandons d'exporter les fichiers en question au format csv dans un premier temps, puis de le lire avec la fonction read.csv
dans un second temps (section \@ref(sect01411)).
Il est néanmoins possible de lire directement un fichier xlsx avec le package xlsx. Ce dernier requiert que le logiciel JAVA soit installé sur votre ordinateur (Windows, Mac ou Linux). Si vous utilisez la version 64 bit de R, vous devrez télécharger et installer la version 64 bit de JAVA. Une fois que ce logiciel tiers est installé, il ne vous restera plus qu'à installer (install.packages("xlsx")
) et charger (library(xlsx)
) le package xlsx.
library(xlsx) t1 <- Sys.time() df6 <- read.xlsx(file="data/priseenmain/SR_MTL_2016.xlsx", sheetIndex = 1, as.data.frame = TRUE) t2 <- Sys.time() d6 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df6 a ",nrow(df6)," observations", "et ",ncol(df6),"colonnes\n", sep = "")
Il est possible d'accélérer significativement la vitesse de lecture d'un fichier xlsx en utilisant la fonction read.xlsx2
. Il faut cependant indiquer à cette dernière le type de données de chaque colonne. Dans le cas présent, les cinq premières colonnes contiennent des données de type texte (character
), alors que les 43 autres sont des données numériques (numeric
). Nous utilisons la fonction rep
afin de ne pas avoir à écrire plusieurs fois character et numeric.
library(xlsx) t1 <- Sys.time() df7 <- read.xlsx2(file="data/priseenmain/SR_MTL_2016.xlsx", sheetIndex = 1, as.data.frame = TRUE, colClasses = c(rep("character",5),rep("numeric",43)) ) t2 <- Sys.time() d7 <- as.numeric(difftime(t2,t1,units="secs")) cat("le dataframe df6 a ",nrow(df7)," observations", "et ",ncol(df7),"colonnes\n", sep = "")
Si l'on compare les temps d'exécution (tableau \@ref(tab:tableduration)), on constate que la lecture des fichiers xlsx peut être extrêmement longue si l'on ne spécifie pas le type des colonnes, ce qui peut devenir problématique pour des fichiers volumineux. Notez également que la lecture des fichiers csv devient de plus en plus laborieuse à mesure que la taille du fichier csv augmente. Si vous devez un jour charger des fichiers csv de plusieurs gigaoctets, nous vous recommandons vivement d'utiliser la fonction fread
du package data.table qui est beaucoup plus rapide.
DureeImportation <- data.frame( "duree" = c(d1,d2,d3,d4,d5,d6,d7), "fonction" = c("read.csv","read.dbf","read.spss","read.dta", 'read.sas7bdat',"read.xlsx","read.xlsx2") ) show_table(DureeImportation, digits = 2, col.names = c("Durée (s)","fonction"), caption = 'Temps nécessaire pour lire les données en fonction du type de fichiers')
Une fois le DataFrame chargé, voyons comment il est possible de le manipuler.
Tidyverse est un ensemble de packages conçus pour faciliter la structuration et la manipulation des données dans R. Avant d'aller plus loin, il est important d'aborder brièvement un débat actuel dans la Communauté R. Entre 2010 et 2020, l'utilisation du tidyverse s'est peu à peu répandue. Développé et maintenu par Hadley Wickham, tidyverse introduit une philosophie et une grammaire spécifiques qui diffèrent du langage R traditionnel. Une partie de la communauté a pour ainsi dire complètement embrassé le tidyverse et de nombreux packages en dehors du tidyverse ont adopté sa grammaire et sa philosophie. À l'inverse, une autre partie de la communauté est contre cette évolution (voir l'article du blogue suivant{target="_blank"}). Les arguments pour et contre tidyverse sont résumés dans le tableau suivant.
df <- data.frame( Pour = c("Simplicité d'écriture et d'apprentissage", "Ajout de l'opérateur `%>%` permettant d'enchaîner les traitements", "La meilleure librairie pour réaliser des graphiques : **ggplot2**", "Crée un écosystème cohérent", "Package en développement et de plus en plus utilisé"), Contre = c("Nouvelle syntaxe à apprendre", "Perte de lisibilité avec l'opérateur `->`", "Certaines fonctions de base sont remplacées par **tidyverse** lors de son chargement, pouvant créer des erreurs.", "Ajoute une dépendance dans le code", "Philosophie d'évolution agressive, aucune assurance de rétro-compatibilité") ) show_table(df, col.names = c("Avantage du tidyverse", "Problème posé par le tidyverse"), caption = 'Avantages et inconvénients du tidyverse', col.to.resize = c(1,2), col.width = "7cm")
Le dernier point est probablement le plus problématique. Dans sa volonté d'évoluer au mieux et sans restriction, le package tidyverse n'offre aucune garantie de rétro-comptatibilité. En d'autre termes, des changements importants peuvent être introduits d'une version à l'autre rendant potentiellement obsolète votre propre code. Nous n'avons pas d'opinion tranchée sur le sujet : tidyverse est un outil très intéressant dans de nombreux cas; nous évitons simplement de l'utiliser systématiquement et préférons charger directement des sous-packages (comme dplyr ou ggplot2) du tidyverse. Notez que le package data.table offre une alternative au tidyverse dans la manipulation de données. Au prix d'une syntaxe généralement un peu plus complexe, le package data.table offre une vitesse de calcul bien supérieure au tidyverse et assure une bonne rétro-compatibilité.
Repartons du DataFrame que nous avions chargé précédemment en important un fichier csv.
df <- read.csv(file="data/priseenmain/SR_MTL_2016.csv", header = TRUE, sep = ",", dec = ".", stringsAsFactors = FALSE)
Pour rappel, il est possible d'accéder aux colonnes dans ce DataFrame en utilisant le symbole dollar $ma_colonne
ou les doubles crochets [["ma_colonne"]]
.
# Calcul de la superficie totale de l'Île de Montréal sum(df$KM2) sum(df[["KM2"]])
Il est possible de sélectionner plusieurs colonnes d'un DataFrame et filtrer ainsi les colonnes inutiles. Pour cela, on peut utiliser un vecteur contenant soit les positions des colonnes (1 pour la première colonne, 2 pour la seconde et ainsi de suite), soit les noms des colonnes.
# Conserver les 5 premières colonnes df2 <- df[1:5] # Conserver les colonnes 1, 5, 10 et 15 df3 <- df[c(1,5,10,15)] # Cela peut aussi être utilisé pour changer l'ordre des champs df3 <- df[c(10,15,1,5)] # Conserver les colonnes 1 à 5, 7 à 12, 17 et 22 df4 <- df[c(1:5,7:12,17,22)] # Conserver les colonnes avec leurs noms df5 <- df[c("SRIDU","KM2","Pop2016","MaisonIndi","LoyerMed")]
Il est parfois plus intéressant et rapide de supprimer directement des colonnes plutôt que de recréer un nouveau DataFrame. Pour ce faire, on attribue la valeur NULL
à ces colonnes.
# Supprimer le colonnes 2, 3 et 5 df3[c(2,3,5)] <- list(NULL) # Supprimer une colonne avec son nom df4$OID <- NULL # Supprimer plusieurs colonnes par leur nom df5[c("SRIDU","LoyerMed")] <- list(NULL)
Notez que si vous supprimez une colonne, vous ne pouvez pas revenir en arrière. Il faudra recharger votre jeu de données ou éventuellement relancer les calculs qui avaient produit cette colonne.
Il est possible de changer le nom d'une colonne. Cette opération est importante pour faciliter la lecture du DataFrame ou encore s'assurer que l'exportation du DataFrame dans un format ne posera pas de problème.
# Voici les noms des colonnes names(df5) # Renommer toutes les colonnes names(df5) <- c('superficie_km2','population_2016', 'maison_individuelle_prt') names(df5) # Renommer avec dplyr library(dplyr) df4 <- rename(df4, "population_2016" = "Pop2016", "prs_moins_14ans_prt" = "A014", "prs_15_64_ans_prt" = "A1564", "prs_65plus_ans_prt" = "A65plus" )
Il est possible d'utiliser les colonnes de type numérique pour calculer de nouvelles colonnes en utilisant les opérateurs mathématiques vus dans la section \@ref(sect0135). Prenons un exemple concret : calculons la densité de population par secteur de recensement dans notre DataFrame, puis affichons un résumé de cette nouvelle variable.
# Calcul de la densité df$pop_density_2016 <- df$Pop2016 / df$KM2 # Statistiques descriptives summary(df$pop_density_2016)
Nous pouvons aussi calculer le ratio entre le nombre de maisons et le nombre d'appartements.
# Calcul du ratio df$total_maison <- (df$MaisonIndi + df$MaisJumule + df$MaisRangee + df$AutreMais) df$total_apt <- (df$AppDuplex + df$App5Moins + df$App5Plus) df$ratio_maison_apt <- df$total_maison / df$total_apt
Retenez ici que R va appliquer le calcul à chaque ligne de votre jeu de données et stocker le résultat dans une nouvelle colonne. Cette opération est du calcul vectoriel : toute la colonne est calculée en une seule fois. R est d'ailleurs optimisé pour le calcul vectoriel.
R propose un ensemble de fonctions de base pour effectuer du calcul. Voici une liste non-exhaustive des principales fonctions :
abs
calcule les valeurs absolues des valeurs d'un vecteursqrt
calcule les racines carrées des valeurs d'un vecteurlog
calcule les logarithmes des valeurs d'un vecteurexp
calcule les exponentielles des valeurs d'un vecteurfactorial
calcule la factorielle des valeurs d'un vecteurround
arrondit les valeurs d'un vecteurceiling
, floor
arrondit à l'unité supérieure ou inférieure les valeurs d'un vecteursin
, asin
, cos
, acos
, tan
, atan
sont des fonctions de trigonométriecumsum
calcule la somme cumulative des valeurs d'un vecteur.Ces fonctions sont des fonctions vectorielles puisqu'elles s'appliquent à tous les éléments d'un vecteur. Si votre vecteur en entrée comprend cinq valeurs, le vecteur en sortie comprendra aussi cinq valeurs.
À l'inverse, les fonctions suivantes s'appliquent directement à l'ensemble d'un vecteur et ne vont renvoyer qu'une seule valeur :
sum
calcule la somme des valeurs d'un vecteurprod
calcule le produit des valeurs d'un vecteurmin
, max
renvoient les valeurs maximale et minimale d'un vecteurmean
, median
renvoient la moyenne et la médiane d'un vecteurquantile
renvoit les percentiles d'un vecteur.Outre les données numériques, vous aurez à travailler avec des données de type texte (string
). Le tidyverse avec le package stringr offre des fonctions très intéressantes pour manipuler ce type de données. Pour un aperçu de toutes les fonctions offertes par stringr, référer-vous à sa Cheat Sheet{target="_blank"}. Commençons avec un DataFrame assez simple comprenant des adresses et des noms de personnes.
library(stringr) df <- data.frame( noms = c("Jérémy Toutanplace","constant Tinople","dino Resto","Luce tancil"), adresses = c('15 rue Levy', '413 Blvd Saint-Laurent', '3606 rue Duké', '2457 route St Marys') )
Pour harmoniser ce dataframe, nous allons dans un premier temps mettre des majuscules au premier caractère des prénoms et noms des individus avec la fonction str_to_title
.
df$noms_corr <- str_to_title(df$noms) print(df$noms_corr)
On pourrait également tout mettre en minuscules ou tout en majuscules.
df$noms_min <- tolower(df$noms) df$noms_maj <- toupper(df$noms) print(df$noms_min) print(df$noms_maj)
Les adresses comprennent des caractères accentués. Ce type de caractères pose régulièrement des problèmes d'encodage. Nous pourrions alors décider de les remplacer par des caractères simples avec la fonction str_replace_all
.
df$adresses_1 <- str_replace_all(df$adresses,'é','e') print(df$adresses_1)
La même fonction peut être utilisée pour remplacer les St par Saint et les Blvd par Boulevard.
df$adresses_2 <- str_replace_all(df$adresses_1,' St ',' Saint ') df$adresses_3 <- str_replace_all(df$adresses_2,' Blvd ',' Boulevard ') print(df$adresses_3)
Il est parfois nécessaire de découper du texte pour en extraire des éléments. On doit alors choisir un caractère de découpage. Dans notre exemple, on pourrait vouloir extraire les numéros civiques des adresses, en utilisant le premier espace comme caractère de découpage, en utilisant la fonction str_split_fixed
.
df$num_civique <- str_split_fixed(df$adresses_3, ' ',n=2)[,1] print(df$num_civique)
Pour être exact, sachez que pour notre exemple, la fonction str_split_fixed
renvoie deux colonnes de texte : une avec le texte avant le premier espace, soit le numéro civique, et une avec le reste du texte. Le nombre de colonnes est contrôlé par l'argument n
. Si n = 1
, la fonction ne fait aucun découpage, avec n = 2
la fonction va découper en deux parties le texte avec la première occurence du délimiteur, et ainsi de suite. En ajoutant [,1]
à la fin, nous indiquons que l'on souhaite garder seulement la première des deux colonnes.
Il est également possible d'extraire des parties de texte et de ne garder par exemple que les N premiers caractères ou les N derniers caractères :
# ne garder que les 5 premiers caractères substr(df$adresses_3,start = 1, stop = 5) # ne garder que les 5 derniers caractères n_caract <- nchar(df$adresses_3) substr(df$adresses_3, start = n_caract-4, stop = n_caract)
Notez que les paramètres start
et stop
de la fonction substr
peuvent accepter un vecteur de valeurs. Il est ainsi possible d'appliquer une sélection de texte différente à chaque chaîne de caractères dans notre vecteur en entrée. On pourrait par exemple vouloir récupérer tout le texte avant le second espace pour garder uniquement le numéro civique et le type de rue.
# étape 1 : récupérer les positions des espaces pour chaque adresses positions <- str_locate_all(df$adresses_3, " ") # étape 2 : récupérer les positions des seconds espaces sec_positions <- sapply(positions, function(i){ i[2,1] }) # étape 3 : appliquer le découpage substr(df$adresses_3, start = 1, stop = sec_positions-1)
À l'inverse du découpage, il est parfois nécessaire de concaténer des éléments de texte, ce qu'il est possible de réaliser avec la fonction paste
.
df$texte_complet <- paste(df$noms_corr, df$adresses_3, sep = " : ") print(df$texte_complet)
Le paramètre sep
permet d'indiquer le ou les caractères à intercaler entre les éléments à concaténer. Notez qu'il est possible de concaténer plus que deux éléments.
df$ville <- c('Montreal','Montreal','Montreal','Montreal') paste(df$noms_corr, df$adresses_3, df$ville, sep = ", ")
Nous avons vu que les principaux types de données dans R sont le numérique, le texte, le booléen et le facteur. Il existe d'autres types introduits par différents packages. Nous abordons ici les types date et heure (date and time). Pour les manipuler, nous privilégions l'utilisation du package lubridate du tidyverse. Pour illuster le tout, nous l'appliquerons avec un jeu de données ouvertes de la Ville de Montréal représentant les collisions routières impliquant au moins un cycliste survenues après le 1^er^ janvier 2017.
accidents_df <- read.csv(file="data/priseenmain/accidents.csv", sep = ",") names(accidents_df)
Nous disposons de trois colonnes représentant respectivement l'heure, la date et le nombre de victimes impliquées dans la collision.
Actuellement, les colonnes HEURE_ACCDN et DT_ACCDN sont au format texte. Nous pouvons afficher quelques lignes du jeu de données avec la fonction head
pour visualiser comment elles ont été saisies.
head(accidents_df, n = 5)
Un peu de ménage s'impose : les heures sont indiquées comme des périodes d'une heure. Nous utilisons la fonction str_split_fixed
du package stringr pour ne garder que la première partie de l'heure (avant le tiret). Nous allons ensuite concaténer l'heure et la date avec la fonction paste
, puis nous convertirons ce résultat en un objet date-time.
library(lubridate) # Étape 1 : découper la colonne Heure_ACCDN accidents_df$heure <- str_split_fixed(accidents_df$HEURE_ACCDN, "-", n=2)[,1] # Étape 2 : concaténer l'heure et la date accidents_df$date_heure <- paste(accidents_df$DT_ACCDN, accidents_df$heure, sep = ' ') # Étape 3 : convertir au format datetime accidents_df$datetime <- as_datetime(accidents_df$date_heure, format = "%Y/%m/%d %H:%M:%S")
Pour effectuer la conversion, nous avons utilisé la fonction as_datetime
du package lubridate. Elle prend comme paramètre un vecteur de texte et une indication du format de ce vecteur de texte. Il existe de nombreuses façons de spécifier une date et une heure et l'argument format permet d'indiquer celle à utiliser. Dans cet exemple, la date est structurée comme suit :
année/mois/jour heure:minute:seconde
, ce qui se traduit par le format %Y/%m/%d %H:%M:%S
.
Notez que les caractères séparant les années, jours, heures, etc. sont aussi à indiquer dans le format. Dans notre exemple, nous utilisons des /
pour séparer les éléments de la date et des :
pour l'heure, et un espace pour séparer la date et l'heure.
Il existe d'autres nomenclatures pour spécifier un format datetime : par exemple, des mois renseignés par leur nom, l'indication AM-PM, etc. Vous pouvez vous référez à la documentation de la fonction strptime
(help(strptime)
) pour explorer les différentes nomenclatures et choisir celle qui vous convient. Bien évidemment, il est nécessaire que toutes les dates de votre colonne soient renseignées dans le même format. Sinon, la fonction renverra des valeurs NA
aux endroits où elle a échoué à lire le format. Après toutes ces opérations, rejettons un oeil à notre DataFrame.
head(accidents_df, n = 5)
À partir de la nouvelle colonne datetime
, nous sommes en mesure d'extraire des informations intéressantes comme :
weekdays
accidents_df$jour <- weekdays(accidents_df$datetime)
am
et pm
accidents_df$AM <- am(accidents_df$datetime) accidents_df$PM <- pm(accidents_df$datetime) head(accidents_df[c("jour", "AM", "PM")], n=5)
Il est aussi possible d'accéder aux sous-éléments d'un datetime comme l'année, le mois, le jour, l'heure, la minute, la seconde avec les fonctions year()
, month()
,day()
, hour()
, minute()
et second()
.
Une autre utilisation intéressante du format datetime est de calculer des différences de temps. Par exemple, nous pourrions utiliser le nombre de minutes écoulées depuis 7h00 le matin comme une variable dans une analyse visant à déterminer le moment critique des collisions routières durant l'heure de pointe du matin.
Pour cela, nous devons créer un datetime de référence en concaténant la date de chaque observation, et le temps 07:00:00
qui sera notre point de départ.
accidents_df$date_heure_07 <- paste(accidents_df$DT_ACCDN, '07:00:00', sep = ' ') accidents_df$ref_datetime <- as_datetime(accidents_df$date_heure_07, format = "%Y/%m/%d %H:%M:%S")
Il ne nous reste plus qu'à calculer la différence de temps entre la colonne datetime et notre temps de référence ref_datetime.
accidents_df$diff_time <- difftime(accidents_df$datetime, accidents_df$ref_datetime, units = 'min')
Notez qu'ici la colonne diff_time est d'un type spécial : une différence temporelle (difftime). Il faut encore la convertir au format numérique pour pourvoir l'utiliser avec la fonction as.numeric
. Par curiosité, réalisons rapidement un histogramme avec la fonction hist
pour analyser rapidement cette variable d'écart de temps !
accidents_df$diff_time_num <- as.numeric(accidents_df$diff_time) hist(accidents_df$diff_time_num, breaks = 50)
On observe clairement deux pics, un premier entre 0 et 100 ( entre 07h00 08h30 environ) et un second plus important entre 550 et 650 (entre 16h00 et 17h30 environ), ce qui correspond sans surprise aux heures de pointe. Il est intéressant de noter que plus d'accidents se produisent à l'heure de pointe du soir qu'à celle du matin.
Lorsque l'on travaille avec des données provenant de différents endroits dans le monde ou que l'on doive tenir compte des heures d'été et d'hiver, il convient de tenir compte du fuseau horaire. Pour créer une date avec un fuseau horaire, il est possible d'utiliser le paramètre tz
dans la fonction as_datetime
et d'utiliser l’identifiant du fuseau approprié. Dans notre cas, les données d'accident ont été collectées à Montréal qui a un décalage de -5 heures par rapport au temps de référence UTC (+1 heure en été). Le code spécifique de ce fuseau horaire est EDT, il est facile de trouver ces codes avec le site web timeanddate.com.
accidents_df$datetime <- as_datetime(accidents_df$date_heure, format = "%Y/%m/%d %H:%M:%S", tz = "EDT")
Recoder des variables signifie changer la valeur d'une variable selon une condition afin d'obtenir une nouvelle variable. Si nous reprenons le jeu de données précédent sur les accidents à vélo, nous pourrions vouloir créer une nouvelle colonne nous indiquant si la collision a eu lieu en heures de pointe ou non. On obtiendrait ainsi une nouvelle variable avec seulement deux catégories plutôt que la variable numérique originale. Nous pourrions aussi définir quatre catégories avec l'heure de pointe du matin, l'heure de pointe du soir, le reste de la journée et la nuit.
Si l'on ne souhaite créer que deux catégories, le plus simple est d'utiliser la fonction ifelse
. Cette fonction va évaluer une condition (section \@ref(sect0135)) pour chaque ligne d'un DataFrame et produire un nouveau vecteur. Créons donc une variable binaire indiquant si une collision a eu lieu durant les heures de pointe ou hors heures de pointe. Nous devons alors évaluer les conditions suivantes :
Est-ce que l'accident a eu lieu entre 07h00 (0) ET 09h00 (120), OU est ce que la collision a eu lieu entre 16h30 (570) ET 18h30 (690)?
table(is.na(accidents_df$diff_time_num))
Notons dans un premier temps que nous avons 40 observations sans valeur pour la colonne diff_time_num
. Il s'agit d'observations pour lesquelles nous ne disposions pas de dates au départ.
Cond1 <- accidents_df$diff_time_num >= 0 & accidents_df$diff_time_num <= 120 Cond2 <- accidents_df$diff_time_num >= 570 & accidents_df$diff_time_num <= 690 accidents_df$moment_bin <- ifelse(Cond1 | Cond2, "en heures de pointe", "hors heures de pointe")
Comme vous pouvez le constater, la fonction ifelse
nécessite trois arguments :
TRUE
ou FALSE
,FALSE
Avec la fonction table
, nous pouvons rapidement visualisuer les effectifs des deux catégories ainsi créées :
table(accidents_df$moment_bin) # vérifier si on a toujours seulement 40 NA table(is.na(accidents_df$moment_bin))
Les heures de pointe représentent quatre heures de la journée, ce qui nous laisse neuf heures hors heures de pointe entre 07h00 et 20h00.
# Ratio de collisions routières en heures de pointe (841 / 2414) / (4 / 13) # Ratio de collisions routières hors heure de pointe (1573 / 2414) / (9 / 13)
En rapportant les collisions aux durées des deux périodes, on observe une nette surreprésentation des collisions impliquant un vélo pendant les heures de pointe d'environ 13% comparativement à la période hors des heures de pointe.
Lorsque l'on souhaite créer plus que deux catégories, il est possible soit d'enchaîner plusieurs fonctions ifelse
(ce qui produit un code plus long et moins lisible), soit d'utiliser la fonction case_when
du package dplyr du tidyverse. Reprenons notre exemple et créons quatre catégories :
library(dplyr) accidents_df$moment_multi <- case_when( accidents_df$diff_time_num >= 0 & accidents_df$diff_time_num <= 120 ~ "pointe matin", accidents_df$diff_time_num >= 570 & accidents_df$diff_time_num <= 690 ~ "pointe soir", accidents_df$diff_time_num > 690 & accidents_df$diff_time_num < 780 ~ "journee", accidents_df$diff_time_num > 120 & accidents_df$diff_time_num < 570 ~ "journee", accidents_df$diff_time_num < 0 | accidents_df$diff_time_num >= 780 ~ "nuit" ) table(accidents_df$moment_multi) #vérifions encore les NA table(is.na(accidents_df$moment_multi))
La syntaxe de cette fonction est un peu particulière. Elle accepte un nombre illimité d'arguments. Chaque argument est composé d'une condition et d'une valeur à renvoyer si la condition est vraie; ces deux éléments étant reliés par le symbole ~
. Notez que toutes les évaluations sont effectuées dans l'ordre des arguments. En d'autres termes, la fonction va d'abord tester la première condition et assigner ces valeurs, puis recommencer pour les prochaines conditions. Ainsi, si une observation (ligne du tableau de données) obtient TRUE
à plusieurs conditions, elle obtiendra la valeur de la dernière condition qu'elle a validée.
Dans cette section, nous verrons comment extraire des sous-parties d'un DataFrame. Il est possible de sous-sélectionner des lignes et des colonnes en se basant sur des conditions ou leurs index. Pour cela, nous allons utiliser un jeu de données fourni avec R : le jeu de données iris décrivant des fleurs du même nom.
data("iris") # Nombre de lignes et de colonnes dim(iris)
Sous-sélectionner des lignes par index est relativement simple. Admettons que nous souhaitons sélectionner les lignes 1 à 5, 10 à 25, 37 et 58.
sub_iris <- iris[c(1:5, 10:25, 37, 58),] nrow(sub_iris)
Sous-sélectionner des lignes avec une condition peut être effectué soit avec une syntaxe similaire, soit en utilisant la fonction subset
. Sélectionnons toutes les fleurs de l'espèce Virginica.
iris_virginica1 <- iris[iris$Species == "virginica",] iris_virginica2 <- subset(iris, iris$Species == "virginica") # Vérifions que les deux dataframes ont le même nombre de lignes nrow(iris_virginica1) == nrow(iris_virginica2)
Vous pouvez utiliser dans les deux cas tous les opérateurs vus dans les sections \@ref(sect01352) et \@ref(sect01353). L'enjeu est d'arriver à un vecteur booléen final permettant d'identifier les observations à conserver.
Nous avons déjà vu comment sélectionner des colonnes en utilisant leur nom ou leur index dans la section \@ref(sect014221). Ajoutons ici un cas particulier où nous souhaiterions sélectionner des colonnes selon une condition. Par exemple, nous pourrions vouloir conserver que les colonnes comprenant le mot Length. Pour cela, nous utiliserons la fonction grepl
, permettant de déterminer si des caractères sont présents dans une chaîne de caractères.
nom_cols <- names(iris) print(nom_cols) test_nom <- grepl("Length",nom_cols, fixed = TRUE) ok_nom <- nom_cols[test_nom] iris_2 <- iris[ok_nom] print(names(iris_2))
Il est possible d'obtenir ce résultat en une seule ligne de code, mais elle est un peu moins lisible.
iris2 <- iris[names(iris)[grepl("Length",names(iris), fixed = TRUE)]]
Nous avons vu qu'avec les crochets, nous pouvons extraire les colonnes et les lignes d'un DataFrame. Il est possible de combiner les deux opérations simultanément. Pour ce faire, il faut indiquer en premier les indices ou la condition permettant de sélectionner une ligne, puis les indices ou la condition pour sélectionner les colonnes : [index_lignes , index_colonnes]
. Sélectionnons cinq premières lignes et les les trois premières colonnes du jeu de données iris :
iris_5x3 <- iris[c(1,2,3,4,5),c(1,2,3)] print(iris_5x3)
Combinons nos deux exemples précédents pour sélectionner uniquement les lignes avec des fleurs de l'espèce virginica, et les colonnes avec le mot Length.
iris_virginica3 <- iris[iris$Species == "virginica", names(iris)[grepl("Length",names(iris), fixed = TRUE)]] head(iris_virginica3, n=5)
Terminons cette section avec la fusion de DataFrames qu'il est possible de réaliser de deux façons, soit par ajout, soit par jointure.
Ajouter deux DataFrames peut se faire en fonction de leurs colonnes, ou en fonction de leurs lignes. Dans ces deux cas, on utilisera respectivement les fonction cbind
et rbind
. La figure \@ref(fig:fig016) résume graphiquement le fonctionnement des deux fonctions.
knitr::include_graphics('images/introduction/rbind_cbind.png', dpi = NA)
Pour que cbind
fonctionne, il faut que les deux DataFrames aient le même nombre de lignes. Pour rbind
, les deux DataFrames doivent avoir le même nombre de colonnes. Prenons à nouveau comme exemple le jeu de données iris. Nous allons commencer par le séparer en trois sous-jeux de données comprenant chacun une espèce d'iris. Puis, nous fusionnerons deux d'entre eux avec la fonction rbind
.
iris1 <- subset(iris, iris$Species == "virginica") iris2 <- subset(iris, iris$Species == "versicolor") iris3 <- subset(iris, iris$Species == "setosa") iris_comb <- rbind(iris2,iris3)
Nous pourrions aussi extraire dans les deux DataFrames les colonnes comprenant le mot Length et le mot Width, puis les fusionner.
iris_l <- iris[names(iris)[grepl("Length",names(iris), fixed = TRUE)]] iris_w <- iris[names(iris)[grepl("Width",names(iris), fixed = TRUE)]] iris_comb <- cbind(iris_l,iris_w) names(iris_comb)
Une jointure est une opération un peu plus complexe qu'un simple ajout. L'idée est d'associer des informations de plusieurs DataFrames en utilisant une colonne (appelée une clef) présente dans les deux jeux de données. On distingue plusieurs types de jointure :
Ces trois jointures sont présentées à la figure \@ref(fig:fig016); pour ces trois cas, la colonne commune se nomme id.
knitr::include_graphics('images/introduction/merging.png', dpi = NA)
Vous retiendrez que les deux dernières jointures peuvent produire des valeurs manquantes. Pour réaliser ces opérations, on utilise la fonction merge
. Prenons un exemple simple à partir d'un petit jeu de données.
auteurs <- data.frame( name = c("Tukey", "Venables", "Tierney", "Ripley", "McNeil", "Apparicio"), nationality = c("US", "Australia", "US", "UK", "Australia", "Canada"), retired = c("yes", rep("no", 5))) livres <- data.frame( aut = c("Tukey", "Venables", "Tierney", "Ripley", "Ripley", "McNeil","Wickham"), title = c("Exploratory Data Analysis", "Modern Applied Statistics ...", "LISP-STAT", "Spatial Statistics", "Stochastic Simulation", "Interactive Data Analysis", "R for Data Science"))
Nous avons donc deux DataFrames, le premier décrivant des auteurs et le second des livres. Effectuons une première jointure interne afin de savoir pour chaque livre la nationnalité de son auteur et si ce dernier est à la retraite.
df1 <- merge(livres, auteurs, #les deux DataFrames by.x = "aut", by.y = 'name', #les noms des colonnes de jointures all.x = FALSE, all.y = FALSE) print(df1)
Cette jointure est interne car les deux paramètres all.x et all.y ont pour valeur FALSE
. Ainsi, nous indiquons à la fonction que nous ne souhaitons ni garder tous les éléments du premier DataFrame ni tous les éléments du second, mais uniquement les éléments présents dans les deux. Vous noterez ainsi que le livre "R for Data Science" n'est pas présent dans le jeu de données final car son auteur "Wickham" ne fait pas partie du DataFrame auteurs. De même, l'auteur "Apparicio" n'apparaît pas dans la jointure, car aucun livre dans le DataFrame books n'a été écrit par cet auteur.
Pour conserver tous les livres, nous pouvons effectuer une jointure à gauche en renseignant all.x = TRUE
. Nous allons ainsi forcer la fonction à garder tous les livres et mettre des valeurs vides aux informations manquantes des auteurs.
df2 <- merge(livres, auteurs, #les deux DataFrames by.x = "aut", by.y = 'name', #les noms des colonnes de jointures all.x = TRUE, all.y = FALSE) print(df2)
Et pour garder tous les livres et tous les auteurs, nous pouvons faire une jointure complète en indiquant all.x = TRUE
et all.y = TRUE
.
df3 <- merge(livres, auteurs, #les deux DataFrames by.x = "aut", by.y = 'name', #les noms des colonnes de jointures all.x = TRUE, all.y = TRUE) print(df3)
Terminons ici avec quelques conseils sur la rédaction d’un code R. Bien rédiger son code est essentiel pour trois raisons :
Ne négligez pas l'importance d'un code bien rédigé et bien documenté, vous vous éviterez ainsi des migraines lorsque vous devrez exhumer du code écrit il y a plusieurs mois.
Voici quelques lignes directrices peu contraignantes, mais qui devraient vous être utiles :
iris
comprenant le mot Length
: iris_l <- iris[names(iris)[grepl("Length",names(iris), fixed = TRUE)]]
Il serait possible de simplifier la lecture de ce code en détaillant les différentes étapes comme suit :
noms_cols <- names(iris) sel_noms <- noms_cols[grepl("Length",noms_cols, fixed = TRUE)] iris_l <- iris[sel_noms]
#
ne sera pas interprétée par le logiciel. Utilisez des commentaires le plus souvent possible pour décrire les actions que vous souhaitez effectuer avec votre code. Il sera ainsi plus facile de le relire, de naviguer dans votre code, mais également de repérer d'éventuelles erreurs. Si l'on reprend l'exemple précédent : # récupération du nom des colonnes dans le dataframe iris noms_cols <- names(iris) # sélection des colonnes avec les caractères "Length" sel_noms <- noms_cols[grepl("Length",noms_cols, fixed = TRUE)] # extraction des colonnes sélectionnées dans un nouveau dataframe iris_l <- iris[sel_noms]
Éviter le code à rallonge… : typiquement, essayez de vous limiter à des lignes de code d'une longueur maximale de 80 caractères. Au-delà de ce seuil, il est judicieux de découper votre code en plusieurs lignes.
Adopter une convention d'écriture : une convention d'écriture est un ensemble de règles strictes définissant comment un code doit être rédigé. À titre d'exemple, il est parfois recommandé d'utiliser le lowerCamelCase, le UpperCamelCase, ou encore de séparer les mots par des tirets bas upper_camel_case. Un mélange de ces différentes conventions peut être utilisé pour distinguer les variables, les fonctions et les classes. Il peut être difficile de réellement arrêter une telle convention, car les différents packages dans R utilisent des conventions différentes. Dans vos propres codes, il est surtout important d'avoir une certaine cohérence et ne pas changer de convention au fil de votre code.
Indenter son code : l'indentation du code permet de le rendre beaucoup plus lisible. Indenter son code signifie insérer au début de chaque ligne de code un certain nombre d'espaces permettant d'indiquer à quel niveau de profondeur on se situe. Typiquement, lorsque des accolades ou des parenthèses sont ouvertes dans une fonction, une boucle ou une condition, on rajoute deux ou quatre espaces en début de ligne. Prenons un exemple très concret, admettons que nous écrivons une fonction affichant un résumé statistique à chaque colonne d'un jeu de données si cette colonne est de type numérique. L'indentation dans cette fonction va jouer un rôle crucial dans sa lisibilité. Sans indentation et sans respecter la règle des 80 caractères, on obtient ceci :
summary_all_num_cols <- function(dataset){for(col in names(dataset)){if(class(dataset[[col]] == "numeric")){print(summary(dataset[[col]]))}}}
Avec de l'indentation et des commentaires, la syntaxe est beaucoup plus lisible puisqu’elle permet de repérer facilement trois niveaux / paliers dans le code :
#définition d'une fonction summary_all_num_cols <- function(dataset){ #Itération sur chaque colonne de la fonction for(col in names(dataset)){ # A chaque itération, testons si la colonne est de type numérique if(class(dataset[[col]] == "numeric")){ # Si oui, on affiche un résumé statistique pour cette colonne print(summary(dataset[[col]])) } # ici on sort de la condition (niveau 3) } # ici on sort de la boucle (niveau 2) }# ici on sort de la fonction (niveau 1)
setwd
et charger les données nécessaires.
d. Effectuer au besoin les opérations de manipulations sur les données.
e. Effectuer les analyses nécessaires en scindant si possibles les différentes étapes.Notez également que l'étape de définition des fonctions complémentaires peut être effectuée dans une feuille de code séparée, et l'ensemble de ces fonctions chargées à l'aide de la fonction source
. De même, si la manipulation des données est conséquente, il est recommandé de l'effectuer avec un code à part, d'enregistrer les données structurées, puis de le charger directement au début de votre code dédié à l'analyse.
-
, =
ou #
à la suite : # Voici ma section 1 ---------------------------------- # Voici ma section 2 ================================== # Voici ma section 3 ################################## # Autre exemple pour mieux marquer la rupture dans un code : #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #### Titre de ma section 4 #### #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
knitr::include_graphics('images/introduction/sections_rstudio.png', dpi = NA)
knitr::include_graphics('images/introduction/structuration projet.png', dpi = NA)
Ne négligez jamais l'importance d'un code bien écrit et documenté !
Voilà qui conclut ce chapitre sur les bases du langage R. Vous avez maintenant les connaissances nécessaires pour commencer à travailler. N'hésitez pas à revenir sur les différentes sous-sections au besoin ! Pour aller plus loin dans l'apprentissage du langage, vous pouvez également vous plonger dans le chapitre R AVANCÉ. Cependant, nous vous recommandons de faire vos premiers pas avec cette base avant de vous lancer dans cette partie davantage orientée programmation. Quelques ressources pertinentes qui pourraient vous être utiles sont aussi reportées au tableau ci-dessous.
if(knitr::is_latex_output()){ df <- data.frame( Ressource = c("Rbloggers","CRAN packages by date", "Introduction à R et au TidyVerse", "Numyard", "Cheasheets"), Description = c("Un recueil de nombreux blogues sur R : parfait pour être tenu au courant des nouveautés et faire des découvertes.", "Les derniers packages publiés sur CRAN : cela permet de garder un oeil sur les nouvelles fonctionnalités de ses packages préférés.", "Une excellente ressource en français pour en apprendre plus sur le tidyverse.", "Une chaîne YouTube pour revoir les bases de R en vidéo.", "Des feuilles de triche résumant les fonctionnalités de nombreux packages."), Url = c("https://www.r-bloggers.com", "https://cran.r-project.org/web/packages","https://juba.github.io/tidyverse","https://www.youtube.com/user/TheLearnR","https://rstudio.com/resources/cheatsheets")) show_table(df, col.names = c("Ressource","Description","Url"), caption = 'Ressources pertinente pour en apprendre plus sur R', col.to.resize = c(2,3), col.width = "6cm") }else{ df <- data.frame( Ressource = c("[Rbloggers](https://www.r-bloggers.com){target='_blank'}","[CRAN packages by date](https://cran.r-project.org/web/packages/available_packages_by_date.html){target='_blank'}", "[Introduction à R et au TidyVerse](https://juba.github.io/tidyverse/index.html){target='_blank'}", "[Numyard](https://www.youtube.com/user/TheLearnR/featured){target='_blank'}", "[Cheasheets](https://rstudio.com/resources/cheatsheets){target='_blank'}"), Description = c("Un recueil de nombreux blogues sur R : parfait pour être tenu au courant des nouveautés et faire des découvertes.", "Les derniers *packages* publiés sur CRAN : cela permet de garder un oeil sur les nouvelles fonctionnalités de ses *packages* préférés.", "Une excellente ressource en français pour en apprendre plus sur le tidyverse.", "Une chaîne YouTube pour revoir les bases de R en vidéo.", "Des feuilles de triche résumant les fonctionnalités de nombreux *packages*.")) show_table(df, col.names = c("Ressource","Description"), caption = 'Ressources pertinente pour en apprendre plus sur R') }
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.