Opérations sur les fichiers et les dossiers sous R

gerer-fichiers-avec-r

« Non mais ça je le fais à la main directement depuis mon explorateur de fichiers, c’est plus simple ! » me direz-vous… Oui mais ! Dans certains cas, il peut être utile de pouvoir manipuler ses fichiers et dossiers directement depuis R.

Je m’explique : si je suis dans la situation où j’ai une centaine de fichiers csv à importer dans R afin de me créer une base de données propre, je ne vais pas taper tous les chemins à la main, je vais chercher à boucler sur les fichiers présents dans mon dossier en récupérant leurs noms. Si je veux vérifier que mon fichier existe bien avant de l’importer, comment faire ? Dans R-base, il existe un certain nombre de fonctions permettant de gérer ses dossiers et fichiers.

Le working directory

Késako ? Il s’agit de notre répertoire de travail, le dossier dans lequel on est en train de travailler au cours de notre session R. Quand on travaille en mode “projet” dans RStudio, le working directory est, si on ne le modifie pas, l’emplacement dans lequel est enregistré notre projet sur le disque dur.

Il est fortement recommandé de travailler en projet. Que vous travailliez avec Rstudio ou non, un projet reste un dossier sur votre disque dur où vous stocker la totalité de ce dont vous avez besoin pour réaliser votre projet d’analyse ou de développement actuel. Lorsque vous êtes à l’aise, vous pouvez même en faire un package documenté, testé, quelque soit le type de projet.

La fonction getwd() permet de d’afficher le chemin du répertoire dans lequel on est en train de travailler.

Attention, si vous avez utilisé setwd()dans votre passé, sachez que c’est une mauvaise pratique. Préférez travailler en projet, avec un unique répertoire de travail racine. Tous les chemins vers vos fichiers seront relatifs à cette racine. De ce fait, votre travail est transférable, partageable et plus reproductible.

Création et suppression de dossiers

La fonction dir.create() permet de créer un nouveau dossier :

dir.create("nouveau_dossier")

Dans ce cas, le dossier est créé à la racine de votre répertoire de travail

Vérifions maintenant si notre dossier a bien été créé, s’il existe :

# avec dir.exists()
dir.exists("nouveau_dossier")
## [1] TRUE
# avec file.exists()
file.exists("nouveau_dossier")
## [1] TRUE

Dans la console R directemenent ou dans Rstudio, lorsque vous ajoutez des guillemets "", vous pouvez utiliser la touche tabulation pour vous voir proposer la liste des fichiers et dossiers disponibles.

Et si on souhaite supprimer un dossier, on utilise la fonction unlink() (Attention ici, si vous utilisez l’auto-complétion, si vous laissez le / à la fin, votre dossier ne sera pas supprimé). Le paramètre recursive = TRUE permet de supprimer le dossier et tout son contenu s’il n’est pas vide :

unlink("nouveau_dossier", recursive = TRUE)
dir.exists("nouveau_dossier")
## [1] FALSE

Opérations sur les fichiers

De la même manière que pour les dossiers, on peut tout à fait créer des fichiers vides, vérifier leur existence, les supprimer

La fonction file.create() permet de créer des fichiers .txt, .csv, ou encore .docx, vides :

# création d'un .csv :
file.create("nouveau_fichier.csv")
# création d'un .txt :
file.create("nouveau_fichier.txt")
# création d'un .docx :
file.create("nouveau_fichier.docx")

Pour vérifier qu’un fichier existe, on utilise la fonction file.exists() :

# existence du fichier :
file.exists("nouveau_fichier.csv")

Et comme unlink(), la fonction file.remove() permet de supprimer un fichier :

# supression d'un fichier avec unlink():
unlink("nouveau_fichier.csv")
# supression d'un fichier avec file.remove():
file.remove("nouveau_fichier.txt")

Il est également possible de :

  • renommer un fichier grâce à la fonction file.rename() :
file.rename(from = "nouveau_fichier.csv", to = "mon_fichier.csv")
  • copier un ficher via la fonction file.copy(). Si le fichier de destination existe déjà, le fichier ne sera pas copié sauf avec l’argument overwrite = TRUE:
file.copy(from = "nouveau_fichier.csv", to = "nouvelle_copie.csv")
  • déplacer un fichier grâce à la fonction file.move() du package filesstrings :
filesstrings::file.move("nouveau_fichier.csv", "repertoire_de_destination")
  • concaténer deux fichiers, avec la fonction file.append() :
# création du fichier A contenant le texte "file A\n"
# "\n" permet de creer une nouvelle ligne vide a la fin du fichier
cat("file A\n", file = "A")
# création du fichier B contenant le texte "file B\n"
cat("file B\n", file = "B")
# concaténation : le fichier A contiendra le texte :
# file A
# file B
file.append("A", "B")

Lister les éléments d’un dossier

La fonction list.files() renvoie la liste des éléments d’un dossier, fichiers et dossiers. Appelée sans argument, elle renvoie les noms des éléments du répertoire courant.

# éléments du répertoire courant :
list.files()
## [1] "A"                          "art02_gestionFichiers.html" "art02_gestionFichiers.Rmd" 
## [4] "B"                          "img"                        "nouveau_fichier.csv"
# éléments d'un autre répertoire :
list.files("img")
## [1] "archives.jpg"  "archives2.jpg"

Comme on peut le voir ici, seuls les éléments présents dans le dossier sont listés. Pour afficher le contenu des sous répertoires, il faut utiliser l’argument recursive = TRUE :

# éléments du répertoire courant et sous-dossiers:
list.files(recursive = TRUE)
## [1] "A"                          "art02_gestionFichiers.html" "art02_gestionFichiers.Rmd" 
## [4] "B"                          "img/archives.jpg"           "img/archives2.jpg"         
## [7] "nouveau_fichier.csv"

Et si je veux récupérer les chemins complets depuis la racine de l’ordinateur ? Il suffit de mettre le paramètre full.names à TRUE :

# éléments du répertoire courant et sous-dossiers:
list.files(full.names = TRUE)

Ok, mais en fait, il n’y a que les .csv qui m’intéressent… On peut faire un filtre sur les fichiers selon l’extension ? Of course ! L’argument pattern permet de ne sélectionner que les fichiers dont le nom comprend le pattern donné. Donc si je ne souhaite que les .csv :

# éléments du répertoire courant et sous-dossiers:
list.files(pattern = ".csv")
## [1] "nouveau_fichier.csv"

Ici on se rend compte de l’utilité que peuvent avoir ces fonctions. Je disais au début que je voulais importer ma centaine de fichiers csv pour en faire une table propre. Si tous les fichiers ont la même structure, une manière de procéder :

# liste des csv à importer :
liste_csv <- list.files("csv_a_importer", pattern = ".csv", full.names = TRUE)
# import des données :
all_csv <- lapply(liste_csv, readr::read_csv)
# un data.frame unique :
df_csv <- dplyr::bind_rows(all_csv)
# enregistrement du dataframe :
readr::write_csv(df_csv, file = "df_csv.csv")
# on renomme le dossier
file.rename(from = "csv_a_importer", to = "csv_importes")

Informations sur les fichiers

snapshsot <- utils::fileSnapshot()
snapshsot$info
##                             size isdir mode               mtime               ctime               atime uid
## A                             16 FALSE  777 2019-12-18 15:56:39 2020-01-10 09:56:55 2020-01-10 09:56:55   0
## art02_gestionFichiers.html 26198 FALSE  777 2020-02-05 18:28:56 2020-02-05 18:28:56 2020-02-05 18:28:56   0
## art02_gestionFichiers.Rmd   9907 FALSE  777 2020-02-05 18:28:50 2020-02-05 18:28:50 2020-02-05 18:28:50   0
## B                              8 FALSE  777 2019-12-18 15:56:35 2020-01-10 09:56:55 2020-01-10 09:56:55   0
## img                            0  TRUE  777 2019-12-25 15:55:28 2020-01-10 09:56:55 2020-01-10 09:56:55   0
## nouveau_fichier.csv            0 FALSE  777 2019-12-17 18:42:29 2020-01-10 09:56:55 2020-01-10 09:56:55   0
##                            gid uname grname
## A                            0  root   root
## art02_gestionFichiers.html   0  root   root
## art02_gestionFichiers.Rmd    0  root   root
## B                            0  root   root
## img                          0  root   root
## nouveau_fichier.csv          0  root   root

La fonction file.info() dans {base}, très similaire à fileSnapshot() permet d’obtenir les mêmes informations, mais pour un fichier en particulier :

file.info("nouveau_fichier.csv")
##                     size isdir mode               mtime               ctime               atime uid gid
## nouveau_fichier.csv    0 FALSE  777 2019-12-17 18:42:29 2020-01-10 09:56:55 2020-01-10 09:56:55   0   0
##                     uname grname
## nouveau_fichier.csv  root   root

Enfin, si seules certaines informations nous intéressent, il existe des fonctions plus spécifiques. Par exemple :

file.mtime("nouveau_fichier.csv")
# taille du fichier :
file.size("nouveau_fichier.csv")
# nom du fichier :
basename("nouveau_fichier.csv")
# emplacement du fichier :
dirname("nouveau_fichier.csv")
# extension du fichier :
tools::file_ext("nouveau_fichier.csv")

Gestion des chemins

La fonction file.path() permet de créer un chemin, de façon alternative et plus rapide que l’utilisation de la fonction paste(..., sep = "/"). Elle s’assure que le slash soit dans le bon sens selon que vous travaillez sur Windows, GNU/Linux ou MacOS :

file.path("mon_dossier", "sous-dossier", "sous-sous-dossier", "test.csv")
## [1] "mon_dossier/sous-dossier/sous-sous-dossier/test.csv"

Si on souhaite convertir le chemin de notre fichier en sa forme canonique, on utilise la fonction normalizePath(). Le paramètre mustWork permet d’afficher ou non une erreur si le fichier n’existe pas.

normalizePath("mon_dossier/sous-dossier/sous-sous-dossier/test.csv", mustWork = FALSE)
## [1] "mon_dossier/sous-dossier/sous-sous-dossier/test.csv"

Allez plus loin avec {fs}

Au prix de l’utilisation d’un package, {fs} permet de gérer ces mêmes opérations avec quelques bonus. La majorité des fonctions de {fs} ont le même nom que {base} sauf qu’il faut remplacer les . par _. Ce package a notamment la particularité de mieux gérer les différents systèmes d’exploitation, ce qui peut être intéressant si vous travaillez en collaboration avec des collègues qui travaillent sur Windows, MacOS ou GNU/Linux…

Vous avez maintenant les différents outils pour gérer les opérations sur les dossiers et les fichiers directement depuis R, sans besoin d’ajouter de dépendances. Faîtes-en bon usage !


À propos de l'auteur


Commentaires


À lire également