Structures de contrôle

Les structures de contrôle (control flow en anglais) font partie des éléments les plus utiles en informatique. Elles servent à guider les opérations que vous souhaitez voir effectuer lorsque certaines conditions sont rencontrées de même que leur débit.

Les boucles for

L'un des éléments-clés pour les structures de contrôle est la boucle for. Celle-ci vous permet d'effectuer une même opération (ou série d'opérations), mais en faisant varier des éléments de votre code. Cette fonction est donc très pratique pour réitérer une opération pour une série d'objets (ou ensembles d'objets).

En R, les éléments de base de la boucle for sont les suivants:

# Présentation à titre indicatif, produit une erreur dans R
for (i in I){
    do_something(i)
}

ì correspond à la variable identifiant l'élément qui changera à chaque itération, I la plage de valeur que peut prendre i et do_something() à une opération quelconque à appliquer sur i. Par exemple,

# Afficher dans la console i où i peut correspondre à [1, 10]
for (i in 1:10){
    print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
# Print jour où la variable "nom" peut correspondre au jour de la semaine
noms_jours_semaine <- c("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi")
for (nom in noms_jours_semaine){
    print(nom)
}
## [1] "Dimanche"
## [1] "Lundi"
## [1] "Mardi"
## [1] "Mercredi"
## [1] "Jeudi"
## [1] "Vendredi"
## [1] "Samedi"
# Vous pouvez créer des objets locaux au sein des boucles pour conserver le résultat des opérations que vous réalisez dans la boucle
for (nom in noms_jours_semaine){
    tmp <- paste("Le nom de la journée est:", nom)
    print(tmp)
}
## [1] "Le nom de la journée est: Dimanche"
## [1] "Le nom de la journée est: Lundi"
## [1] "Le nom de la journée est: Mardi"
## [1] "Le nom de la journée est: Mercredi"
## [1] "Le nom de la journée est: Jeudi"
## [1] "Le nom de la journée est: Vendredi"
## [1] "Le nom de la journée est: Samedi"

Note: En programmation, les objets locaux au sein des boucles ne sont pas toujours accessibles en dehors de celles-ci. En R, vous ne rencontrerez pas ce problème. Cependant, gardez en tête que comme la boucle réassigne une nouvelle valeur à chaque itération, seule la dernière valeur assignée sera disponible dans votre environnement.

Les boucles for sont par contre assez exigeantes puisqu'elles demandent de spécifier l'entièreté des opérations devant être lancées. Par exemple, si l'opération itérée n'est pas sauvegardée dans un objet ou affichée dans la console via des commandes comme print() ou cat(), le résultat ne sera pas conservé. Plus encore, alors que la plupart des fonctions dans R vont vous indiquer lorsqu'une erreur est rencontrée et vont en afficher sommairement la source, les boucles for ne vous indiquerons que ce que vous aurez programmé.

Par exemple,

# for loop sans affichage ou sauvegarde de résultat
total_heures_semaines <- 0
for (i in 1:7){
     total_heures_semaines + 24
}
total_heures_semaines
## [1] 0
# for loop avec erreur silencieuse
for (i in 1:7){
     total_heures_semaines <- 0
     total_heures_semaines <- total_heures_semaines + 24
}
total_heures_semaines
## [1] 24

Exercice

Écrivez une boucle for qui trouve le résultat de la somme de tous les entiers de 1 à 10. (1 + 2 + ... + 9 + 10)

somme <- 0
for (i in 1:10){
  somme <- somme + i
}
somme

Les expressions conditionnelles

Dans l'ensemble d'outils existants pour assurer que votre boucle for effectue ce que vous souhaitez qu'elle exécute, les expressions conditionnelles sont les plus courantes. Ces dernières, dont l'usage n'est par limitées aux boucles, fonctionnent en utilisant des vecteurs booléens unaires ou le résultat d'opérateurs booléens sur ceux-ci comme condition pour l'exécution (ou non) de lignes de code.

Les opérateurs logiques

Les opérateurs booléens en R comprennent, entre autres,:

Opérateur Équivalent R
OU | ou ||
ET & ou &&
Plus grand >
Plus grand ou égal >=
Plus petit <
plus petit ou égal <=
égalité ==
non égalité !=

Les opérateurs <, > et == nous permettent ainsi de faire des comparaisons entre des nombres, comme on peut voir ci-bas :

3 > 2
## [1] TRUE

3 > 2 renvoie TRUE parce que 3 est plus grand que 2

3 < 2
## [1] FALSE

3 < 2 renvoie FALSE parce que 3 est plus grand que 2

3 == 2
## [1] FALSE

3 == 2 renvoie FALSE parce que 3 n'égale pas 2

L'opérateur == et != permettent aussi de savoir si deux chaînes de caractères sont identiques, ou si les éléments de vecteurs sont identiques.

(c(1,2,3)) == (c(1,2,3))
## [1] TRUE TRUE TRUE


"allo" != "salut"
## [1] TRUE

Les opérateurs || et && permettent de combiner des opérations logiques. On pourrait, par exemple, se demander si un nombre se trouve dans un certain interval, prenons l'interval entre 2 et 10. Une manière de traduire cela en des termes logiques consiste à dire que ce nombre doit être plus grand que 2 ET plus petit que 10. Voyons un exemple :

3 > 2 && 3 < 10
## [1] TRUE

renvoit TRUE parce que 3 est effectivement entre 2 et 10.
Voici la table de vérité des opérateurs || et && :

  • TRUE || TRUE donne TRUE
  • TRUE || FALSE donne TRUE
  • FALSE || TRUE donne TRUE
  • FALSE || FALSE donne FALSE


  • TRUE && TRUE donne TRUE
  • TRUE && FALSE donne FALSE
  • FALSE && TRUE donne FALSE
  • FALSE && FALSE donne FALSE
Nous vous invitons à faire quelques tests avec les opérateurs logiques afin de les apprivoiser ici :

Note: Il existe une différence subtile, mais importante entre les opérateurs simples (|, &) et doubles (||, &&). Les opérateurs simples font des comparaisons pour chaque élément dans les vecteurs comparés, alors que les opérateurs doubles comparent les premiers éléments de chaque vecteur entre eux. Comme la version double rapporte une valeur unique, celles-ci sont typiquement plus utilisées dans les expressions conditionnelles.

Les expressions if else

Les expressions conditionnelles vous permettent d'intégrer le respect d'une condition de votre choix préalablement au lancement d'une commande. Cela est très utile pour inclure des tests de validation dans vos scripts pour vous assurer que le code effectue ce qui est attendu.

Le deux éléments clés pour ces expressions sont if et else, le premier pouvant être utilisé indépendamment du second, et ont la forme suivante:

# Présentation à titre indicatif, produit une erreur dans R
if(test) {
    dosomethingiftesttrue()
}

if(test) {
    dosomethingiftesttrue()
} else {
    dosomethingelseiftestfalse()
}

if(test1) {
    dosomethingiftesttrue()
} else if (test2) {
    dosomethingelseiftest1falsebuttest2true()
} else {
    dosomethingelseiftest1andtest2false()
}

if vous permet d'exécuter une commande si une condition (que vous avez fixé préalablement) est respectée, c'est-à-dire si elle renvoie la valeur TRUE. Dans le cas contraire, la commande n'est pas exécutée. else vous permet un niveau de contrôle plus fin ou l'enchainement de plusieurs expressions conditionnelles (voir switch() pour une syntaxe simplifiée pour l'enchainement d'expressions conditionnelles). Par exemple,

# Afficher qu'une erreur a été rencontrée si le vecteur contenant les entrées d'heures par semaine ne contient pas 7 entrées
heures_de_travail_par_jour <- c(3, 2.2, 2, 7, 4.3, 1)

if(length(heures_de_travail_par_jour) != 7){
    print("Il y a un problème avec les entrées d'heure de travail")
}
## [1] "Il y a un problème avec les entrées d'heure de travail"
# Afficher qu'une erreur a été rencontrée si le vecteur contenant les entrées d'heures par semaine ne contient pas 7 entrées et, dans le cas contraire, afficher que le compte est bon.
if(length(heures_de_travail_par_jour) != 7){
    print("Il y a un problème avec les entrées d'heure de travail")
} else {
    print("Toutes les entrées d'heure de travail sont présentes")
}
## [1] "Il y a un problème avec les entrées d'heure de travail"
# Afficher qu'une erreur a été rencontrée si le vecteur contenant les entrées d'heures par semaine ne contient pas 7 entrées et, dans le cas contraire, afficher que le compte est bon. Préciser le problème.
if(length(heures_de_travail_par_jour) < 7){
    print("Il manque des entrées d'heure de travail")
} else if(length(heures_de_travail_par_jour) > 7) {
    print("Il y a des entrées d'heure de travail superflues")
} else {
    print("Toutes les entrées d'heure de travail sont présentes")
}
## [1] "Il manque des entrées d'heure de travail"

Note: la fonction ifelse() possède une syntaxe simplifiée pour l'enchainement d'un if et d'un else. Son pouvoir expressif est plus limité par contre.

Les expressions conditionnelles peuvent prendre des expressions complexes pour condition. Celles-ci peuvent aisément être construites à l'aide des opérateurs logiques présentés plus haut. Par exemple,

heures_de_travail_par_jour <- c(3, 2.2, 2, 7, 4.3, 1, 13)
noms_jours_semaine <- c("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi")
names(heures_de_travail_par_jour) <- noms_jours_semaine

# 2 conditions
test1 <- sum(heures_de_travail_par_jour) > 30
test2 <- sum(heures_de_travail_par_jour) < 50
if (test1 && test2){
    print("La semaine a été productive, mais pas épuisante")
}
## [1] "La semaine a été productive, mais pas épuisante"
# 3 conditions
test1 <- sum(heures_de_travail_par_jour["Dimanche"], heures_de_travail_par_jour["Samedi"]) > 8 # trop d'heures de travail la fin de semaine
test2 <- mean(heures_de_travail_par_jour) > 8 # la moyenne d'heures par jour trop élevé
test3 <- sum(heures_de_travail_par_jour[2:6]) > 40 # le nombre d'heures effectuées la semaine (hors fin de semaine) est trop élevé

if ((test1 || test2) || test3){
    print("Il est nécessaire d'ajuster les heures effectuées dans la semaine")
}
## [1] "Il est nécessaire d'ajuster les heures effectuées dans la semaine"

Une fois que vous vous serez approprié les expressions conditionnelles, la capacité expressive de vos structures de contrôle sera grandement améliorée. Cela, couplé à une bonne compréhension de comment récupérer les éléments au sein des structures de données, vous permettra une manipulation très fine des opérations à réaliser.

Par exemple,

noms_jours_semaine <- c("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi")
heures_de_travail_par_jour <- c(3, 2.2, 2, 7, 4.3, 1, 13)
names(heures_de_travail_par_jour) <- noms_jours_semaine

for (i in seq(noms_jours_semaine)){
    aujourd_hui <- noms_jours_semaine[i]
    if (i == 1){
        hier <- noms_jours_semaine[7]
        demain <- noms_jours_semaine[i+1]
    } else if (i == 7) {
        hier <- noms_jours_semaine[i-1]
        demain <- noms_jours_semaine[1]
    } else {
        hier <- noms_jours_semaine[i-1]
        demain <- noms_jours_semaine[i+1]
    }
    print(paste("Si nous sommes", aujourd_hui, ", alors hier nous étions", hier, "et demain nous serons", demain))
}
## [1] "Si nous sommes Dimanche , alors hier nous étions Samedi et demain nous serons Lundi"
## [1] "Si nous sommes Lundi , alors hier nous étions Dimanche et demain nous serons Mardi"
## [1] "Si nous sommes Mardi , alors hier nous étions Lundi et demain nous serons Mercredi"
## [1] "Si nous sommes Mercredi , alors hier nous étions Mardi et demain nous serons Jeudi"
## [1] "Si nous sommes Jeudi , alors hier nous étions Mercredi et demain nous serons Vendredi"
## [1] "Si nous sommes Vendredi , alors hier nous étions Jeudi et demain nous serons Samedi"
## [1] "Si nous sommes Samedi , alors hier nous étions Vendredi et demain nous serons Dimanche"
suivi_heures <- 0
for (jour in noms_jours_semaine){
    suivi_heures <- suivi_heures + heures_de_travail_par_jour[[jour]]
    print(paste(jour, "j'ai travaillé", heures_de_travail_par_jour[[jour]], "heure(s), pour un total cumulatif de", suivi_heures, "heure(s)"))
}
## [1] "Dimanche j'ai travaillé 3 heure(s), pour un total cumulatif de 3 heure(s)"
## [1] "Lundi j'ai travaillé 2.2 heure(s), pour un total cumulatif de 5.2 heure(s)"
## [1] "Mardi j'ai travaillé 2 heure(s), pour un total cumulatif de 7.2 heure(s)"
## [1] "Mercredi j'ai travaillé 7 heure(s), pour un total cumulatif de 14.2 heure(s)"
## [1] "Jeudi j'ai travaillé 4.3 heure(s), pour un total cumulatif de 18.5 heure(s)"
## [1] "Vendredi j'ai travaillé 1 heure(s), pour un total cumulatif de 19.5 heure(s)"
## [1] "Samedi j'ai travaillé 13 heure(s), pour un total cumulatif de 32.5 heure(s)"

Exercice

Écrivez une opération conditionnelle qui vérifie sur la variable aujourd_hui contient la valeur lundi et imprime «Nous sommes lundi» si c'est le cas, et «Nous ne sommes pas lundi» dans le cas contraire. (La variable aujourd_hui est déjà chargée, vous n'avez pas besoin de la déclarer)

if (aujourd_hui == "lundi"){
  print("Nous sommes lundi")
}else{
  print("Nous ne sommes pas lundi")
}

Parenthèse: les fonctions personnalisées

L'usage des boucles for présente certaines limites rendant leur usage pas toujours optimal pour la tâche à réaliser, i.e. (1) la difficulté de les réutiliser et (2) la difficulté à faire varier plusieurs variables en même temps. Sur (1), le problème vient du fait que celles-ci ne peuvent être enregistrées dans un objet pour ensuite être appelées. Sur (2), la syntaxe de base de R pour les boucles ne permet simplement pas de mobiliser plusieurs variables dans une même itération.

Note: La librairie foreach permet de passer outre le problème (2). De même, il est possible de faire varier plusieurs objets en faisant varier l'index et en utilisant celui-ci pour récupérer le contenu de vecteur (ou autre structure de données).

Une alternative est l'adoption d'une approche fonctionnelle plutôt qu'impérative. Les fonctions sont des objets qui vous permettent d'enregistrer une série d'étapes que vous souhaitez voir appliquer à une variable donnée (ou plusieurs). Bien que celles-ci peuvent être mobilisées au sein d'une boucle for, une série de fonctions existent pour permettre de les appliquer sur une série d'objets (lapply(), sapply(), vapply(), Map()). Par ailleurs, il est souvent possible d'utiliser ces approches pour répondre aux mêmes besoins que les boucles for

La syntaxe est la suivante:

# Présentation à titre indicatif, produit une erreur dans R
do_something <-
    function(variable){
        dosomething(variable)
}

do_something_if <-
    function(variable){
        if(variable == condition){
            dosomething(variable)
        }
}

do_something_with_more_steps <-
    function(variable){
       tmp1 <- step1(variable)
       tmp2 <- step2(variable)
       ...
       tmpn <- stepn(tmp1, tmp2, ...)
       return(tmpn)
}

group_of_object <- c(object1, ..., objectn)

# Application à une série d'objets
lapply(X = group_of_object, function = do_something)

Avec des exemples concrets:

# Fonction utilisant plusieurs des fonctions de base de R vues plus haut pour imprimer des informations d'intérêt à propos d'un objet donné
describe_object_of_interest <-
    function(object_of_interest){
        print(paste("type:", typeof(object_of_interest)))
        print(paste("length:", length(object_of_interest)))
        print("str:")
        return(str(object_of_interest))
    }

# Exemple d'objet à décrire
object1 <- 2
object2 <- TRUE
object3 <- "mardi"
object4 <- c(object1, object2, object3)
# Exemple de description
describe_object_of_interest(object1)
## [1] "type: double"
## [1] "length: 1"
## [1] "str:"
##  num 2
describe_object_of_interest(object2)
## [1] "type: logical"
## [1] "length: 1"
## [1] "str:"
##  logi TRUE
describe_object_of_interest(object3)
## [1] "type: character"
## [1] "length: 1"
## [1] "str:"
##  chr "mardi"
describe_object_of_interest(object4)
## [1] "type: character"
## [1] "length: 3"
## [1] "str:"
##  chr [1:3] "2" "TRUE" "mardi"
# Fonction testant si l'objet est pair ou impair
is_even <-
    function(x){
        if(typeof(x) != "double" && typeof(x) != "integer"){
            return("Not a number")
        } else if((x %% 2) == 0) {
            return(TRUE)
        } else {
            return(FALSE)
        }
    }
# test
is_even(2)
## [1] TRUE
is_even(3)
## [1] FALSE
is_even("2")
## [1] "Not a number"
is_even(TRUE)
## [1] "Not a number"
# Application de la fonction où la valeur de x varie à chaque itération
lapply(X = 1:10, FUN = is_even)
## [[1]]
## [1] FALSE
## 
## [[2]]
## [1] TRUE
## 
## [[3]]
## [1] FALSE
## 
## [[4]]
## [1] TRUE
## 
## [[5]]
## [1] FALSE
## 
## [[6]]
## [1] TRUE
## 
## [[7]]
## [1] FALSE
## 
## [[8]]
## [1] TRUE
## 
## [[9]]
## [1] FALSE
## 
## [[10]]
## [1] TRUE
# Fonction réalisant une série d'opérations à une paire d'objets
plus_minus_multiply_divide_summation <-
    function(x,y){
        tmp1 <- x + y
        tmp2 <- x - y
        tmp3 <- x * y
        tmp4 <- x / y
        result <- sum(tmp1, tmp2, tmp3, tmp4)
        return(result)
    }
# test
plus_minus_multiply_divide_summation(1,5)
## [1] 7.2
plus_minus_multiply_divide_summation(2,6)
## [1] 16.33333
plus_minus_multiply_divide_summation(3,7)
## [1] 27.42857
plus_minus_multiply_divide_summation(4,8)
## [1] 40.5
# Application de la fonction où les valeurs de x et y varient à chaque itération
Map(f = plus_minus_multiply_divide_summation,
    1:4,
    4:5)
## [[1]]
## [1] 6.25
## 
## [[2]]
## [1] 14.4
## 
## [[3]]
## [1] 18.75
## 
## [[4]]
## [1] 28.8

Pour une tâche similaire, les approches fonctionnelles (utilisant des fonctions et les fonctions comme lapply() pour les appliquer à une série de valeur) ont tendance à être plus efficientes en R que les approches impératives (utilisant les boucles for). Cela vient du fait que plusieurs étapes d'optimisations se trouvent sous le capot des fonctions comme lapply() et dérivées. Il vous est possible d'obtenir des performances similaires (vitesse, usage de mémoire, etc.) avec vos boucles for, cependant il vous faudra rédiger les étapes d'optimisation vous-même. Pour la majeure partie des tâches (qui ne nécessitent pas d'être optimisées ou d'être réutilisées), la flexibilité des boucles for et leurs simplicités font que leur usage est préféré aux approches fonctionnelles.

Parenthèse: les données manquantes (NAs)

Les NA sont des valeurs particulières en R permettant d'accommoder les données manquantes (particulièrement présentes en statistique). Celles-ci sont classées intuitivement comme des valeurs logiques et les opérations les incluant résultent souvent en NA. Par exemple,

# Résultats avec connecteurs logiques
NA && TRUE
## [1] NA
NA && FALSE
## [1] FALSE
NA || TRUE
## [1] TRUE
NA || FALSE
## [1] NA
# Résultats avec opérations arithmétiques
sum(1,2,3,4,5,NA)
## [1] NA
mean(c(1,2,3,4,5,NA))
## [1] NA
# Certaines fonctions possèdent des arguments précis pour gérer les NAs
sum(1,2,3,4,5,NA, na.rm = TRUE)
## [1] 15
mean(c(1,2,3,4,5,NA), na.rm = TRUE)
## [1] 3
# Test avec les NA
NA == NA # ne fonctionne pas
## [1] NA
is.na(NA)
## [1] TRUE

Les NAs (tout comme les valeurs logiques) ont peu d'influence sur le type des vecteurs. Cela présente l'avantage de ne pas perturber l'application de votre code, mais au coût de souvent invisibiliser la présence de NAs dans vos jeux de données. En ce sens, il n'est pas rare de se rendre compte de la présence de ces entrées seulement plus tard dans vos analyses.

vecteur_sans_nas <- c(1,2,3,4,5)
vecteur_avec_nas <- c(1,2,3,4,5,NA,NA,NA)
typeof(vecteur_sans_nas) == typeof(vecteur_avec_nas)
## [1] TRUE
str(vecteur_sans_nas)
##  num [1:5] 1 2 3 4 5
str(vecteur_avec_nas)
##  num [1:8] 1 2 3 4 5 NA NA NA
length(vecteur_sans_nas)
## [1] 5
length(vecteur_avec_nas)
## [1] 8
sum(vecteur_sans_nas)
## [1] 15
sum(vecteur_avec_nas)
## [1] NA
is.na(vecteur_sans_nas)
## [1] FALSE FALSE FALSE FALSE FALSE
is.na(vecteur_avec_nas)
## [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE

La fonction de base is.na() est en ce sens très utile pour examiner vos structures de données.

Retour à l'accueil

Lecture/écriture de données

Il est rare lorsque vous programmez en R (ou autres langages de programmation scientifiques) de définir votre jeu de données à l'intérieur même d'un script. La majorité du temps, vous importerez ceux-ci à partir de fichiers sauvegardés sur votre ordinateur (ou serveur dans votre cas). Plusieurs fonctions de base existent en R pour ce faire dépendamment du type de fichier dans lequel vos données sont sauvegardées (.txt, .csv, .tsv, etc.).

Environnement de travail et chemins

Lorsque vous travaillez avec R (et plus largement, lorsque vous faites de la programmation), il est important de garder en tête la position dans votre arbre de dossier où vous devez vous trouver pour effectuer vos différentes tâches. En effet, cet environnement de travail sert de point de référence pour vos différentes opérations, notamment lorsque vous sauvegardez ou importez des données. Pour que R trouve où se trouvent vos données, vous devez lui indiquer le chemin. Ce chemin (ou le path) prend toujours comme point initial votre environnement de travail.

Pour illustrer rapidement ce en quoi cela consiste, considérez l'arbre de dossier suivant:

Dépendamment du dossier dans lequel vous êtes, le path, par exemple pour le fichier data.csv, est différent. Explicitement,

Position Path
dossier nom_du_project "data/data.csv"
dossier scripts "../data/data.csv"
dossier data "data.csv"

Les paths ont une syntaxe particulière avec certaines particularités selon votre système d'exploitation (OS).

Path Signification
data/data.csv Chemin pour le fichier nommé data.csv situé dans le sous-dossier data de mon environnement de travail. (sous Linux ou macOS)
../data/data.csv Chemin pour le fichier nommé data.csv situé dans le dossier data qui est dans le dossier parent de mon environnement de travail. (sous Linux ou macOS)
..\data\data.csv Chemin pour le fichier nommé data.csv situé dans le dossier data qui est dans le dossier parent de mon environnement de travail. (sous Windows)

Ainsi, pour vous assurer que vos paths soient valides entre vos sessions, il est pratique de fixer votre environnement de travail manuellement. Pour ce faire, vous pouvez identifier votre position grâce à la fonction getwd(). Pour la modifier, vous pouvez utiliser la fonction setwd(). Par exemple, si le résultat de getwd() vous donne le chemin "/home/nom/Document/projet/data" et que vous souhaitez fixer votre environnement de travail dans le dossier parent du dossier data, vous pouvez lancer la commande suivante:

path_environnement_de_travail <- "/home/nom/Document/projet"
setwd(path_environnement_de_travail)
# OU
setwd("/home/nom/Document/projet")

Note: Pour ce cours, vous travaillez sur un serveur linux, vous devez construire vos paths en conséquence. Pour des raisons de simplicité, toutes les données seront importées et sauvegardées dans le dossier data/

Astuce: fixer votre environnement de travail au début de chacun de vos scripts.

Exercice

Changez la position de votre environnement de travail pour qu'il corresponde au chemin : /home/user/Desktop/projet_R
setwd("/home/user/Desktop/projet_R")

Fichiers texte

Une première fonction pour importer les données contenues dans un fichier texte se nomme readLines(). Le fichier se trouve alors importé comme un vecteur de caractères.

Une seconde fonction, read.table(), permet d'importer les fichiers .txt cette fois sous une structure de données très utile en R nommée dataframe (abordée dans les modules subséquents). Cette fonction (et ses dérivées) est plus souvent utilisée pour importer des données tabulaires, mais elle offre aussi l'option d'importer du texte.

Les données en format csv

L'un des formats les plus répandus dans le monde des données est le csv, qui signifie comma separated values. Dans ces fichiers, chaque valeur est séparée par une virgule. Les données qui se présentent sous la forme de tableau (ce qu'on retrouve dans un fichier excel par exemple) peuvent généralement être sauvegardées en format csv. Chaque ligne du fichier csv représente une ligne du tableau. Chaque ligne a aussi le même nombre de virgules, ce qui nous permet de séparer ces données en colonnes. Voici un exemple d'un fichier csv. La première ligne représente le nom des colonnes, et les lignes suivantes représentent les observations :

id,age,ville
1,31,Montréal
2,45,Québec
3,22,Rimouski
4,55,Laval
5,63,Boucherville

R offre une fonction qui permet d'importer les données en format csv, la fonction read.csv(). Ci-bas, on importe les données du fichier age_ville.csv qu'on assigne à la variable age_ville. Le fait d'écrire age_ville seul sur une ligne nous permet ensuite de voir ce qu'il y a dans la variable. On peut voir qu'on retrouve les mêmes données présentées dans l'exemple précédent.

age_ville <- read.csv("data/age_ville.csv")
age_ville

Même si csv signifie, comma separated values, il arrive que le caractère de séparation soit autre que la virgule. Les plus communs sont le point-virgule (;) et le tab (" ") qu'on représentent souvent par le symbole spéciale \t. Un fichier séparé par des tab est parfois appelé tsv, qui veut dire tab separated values. Ce qui motive l'utilisation d'un autre caractère que la virgule est que la virgule est parfois présente dans les données. Par exemple si une colonne représente le lieu de résidence d'une personne et qu'il est écrit "Montréal, Qc", la virgule pourra être interprétée de manière erronée comme signifiant un changement de colonne. C'est pour cette raison qu'on utilisera souvent d'autres caractères. Lorsque le caractère de séparation d'un csv n'est pas une virgule, il est possible de l'indiquer à read.csv() grâce au paramètre sep. Voici un exemple où l'on importe un csv où le caractère de séparation est le point-virgule.

age_ville <- read.csv("age_ville.tsv", sep = ";")

Les logiciels comme Libreoffice Calc et Microsoft Excel vous permettent de spécifier le séparateur à utiliser pour délimiter vos colonnes au moment de la sauvegarde des fichiers. Consultez leur documentation respective pour voir comment.

Fichiers natifs de R

R possède des formats de fichier lui étant propre pour sauvegarder les données.

En effet, l'avantage principal des formats comme les comma-separated values (.csv) est qu'ils peuvent être ouverts et traités par plusieurs logiciels différents. Cependant, cette interopérabilité vient au coût de l'élimination des particularités de R comme les types de vos structures de données.

Trois options natives s'offrent à vous pour sauvegarder/charger des données R.

Pour sauvegarder/charger un objet particulier, vous avez accès aux fonctions saveRDS() et loadRDS(). Leur usage est simple:

noms_jours_semaine <- c("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi")
heures_de_travail_par_jour <- c(3, 2.2, 2, 7, 4.3, 1, 13)
names(heures_de_travail_par_jour) <- noms_jours_semaine
resultats_d_interets <- heures_de_travail_par_jour
chemin_de_sauvegarde <- "data/heures_de_travail_par_jour.RDS"

# Sauvegarde
saveRDS(resultats_d_interets, file = chemin_de_sauvegarde)
# OU
saveRDS(resultats_d_interets, file = "data/heures_de_travail_par_jour.RDS")

# Chargement
resultats_d_interets <- loadRDS(chemin_de_sauvegarde)
# OU
resultats_d_interets <- loadRDS("data/heures_de_travail_par_jour.RDS")

Pour sauvegarder/charger un ou plusieurs objets, vous avez accès aux fontions save() et load() utilisables de la façon suivante:

noms_jours_semaine <- c("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi")
heures_de_travail_par_jour <- c(3, 2.2, 2, 7, 4.3, 1, 13)
names(heures_de_travail_par_jour) <- noms_jours_semaine
chemin_de_sauvegarde <- "data/analyse_semaine.RData"

# Sauvegarde
save(noms_jours_semaine, heures_de_travail_par_jour, file = chemin_de_sauvegarde)
# OU
save(noms_jours_semaine, heures_de_travail_par_jour, file = "data/analyse_semaine.RData")

# Chargement
load(chemin_de_sauvegarde)
# OU
load("data/analyse_semaine.RData")

Il est aussi possible de sauvegarder/charger l'ensemble de votre environnement de travail via les fonctions save.image()/load() (RStudio vous demande automatiquement si vous souhaitez sauvegarder votre environnement de travail à la fermeture). Il est conseillé de ne pas utiliser ces fonctions. En effet, le risque est de sauvegarder des objets pour lesquels vous avez malencontreusement effacé les lignes permettant leur production. Vous devriez toujours vous assurer que vos scripts permettent de produire les objets d'intérêts pour assurer la réplicabilité de vos résultats, ainsi que la possibilité d'examiner les étapes réalisées ultérieurement. De plus, il est important de noter que R à la fâcheuse tendance de planter lorsqu'il tente de charger/sauvegarder des environnements de travail lourd.

Exercice

Importez le fichier age_ville.csv, dont le séparateur est le ;. Le fichier est dans le dossier data/. Sauvegardez-le ensuite en format tsv sous le nom age_ville.tsv.

age_ville <- read.csv("data/age_ville.csv", sep = ";")
write.csv(age_ville, file="data/age_ville.tsv")


Retour à l'accueil