Importer plusieurs feuilles ou fichiers
Objectifs
- Savoir importer des données de plusieurs fichiers différents
- Savoir importer les données de plusieurs feuilles différentes
Mise en place
Téléchargez et dé-zippez le dossier suivant contenant les données et .Rproj pour ce tutoriel.
Accédez au nouveau dossier et créez un nouveau script nommé import_multiple.R. Ajoutez un en-tête approprié au script et chargez les paquets {rio} et {here}. Créez un objet path_data_raw qui pointe vers le répertoire data/raw du dossier du cours.
Introduction
Vous avez appris à importer plusieurs types de fichiers à l’aide du paquet {rio} lors du cours sur l’import de données. Dans 90% des cas, vous utiliserez ce paquet pour importer des fichiers .xlsx et .csv. Dans ce satellite, nous allons apprendre à importer des données lorsqu’elles sont dispersées entre plusieurs feuilles ou fichiers, ce qui est un problème courant. Par exemple :
Vous avez un fichier Excel avec des donnés de linelist, des données d’occupation des lits et des données labo dans différentes feuilles, et vous voudriez tout importer dans R.
Vous avez plusieurs fichiers Excel dont vous voudriez importer les données (un fichier linelist, un fichier lab etc.)
Vous avec un fichier Excel avec des données dans plusieurs feuilles, mais cette fois toutes les feuilles sont dans le même format, et il vous faut toutes les importer.
Vous avez plusieurs fichiers Excel tous formatés de la même manière (une linelist par site par exemple) et vous voudriez importer toutes les données et les rassembler dans un même data frame.
On peut résoudre les deux premiers cas avec des commandes différentes visant les feuilles ou fichiers différents, et stocker les données dans des data frames différents. C’est valable s’il n’y a pas trop de jeux de données différents. En revanche, les deux cas suivants peuvent être automatisés !
Dans ce tutoriel, nous allons voir comment le paquet {rio} règle ces problèmes avec la fonction import_list(), qui peut importer plusieurs éléments d’un coup. Ici, les éléments peuvent être des fichiers ou des feuilles, en fonction de quels arguments on passe à la fonction.
Importer plusieurs feuilles
La fonction import_list() peut importer toutes les feuilles d’un fichier Excel et les stocker dans une liste. Celà évite d’avoir à écrire une commande import() différente par feuille.
# NE PAS EXÉCUTER (PSEUDO-CODE)
# Renvoi une liste de data frames
import_list(here(path_data_raw, "example_linelist.xlsx")) Imaginons que vous ayez un fichier labo avec les patients positifs, négatifs et indéterminés sur trois feuilles différentes et vous voulez importer ces données.
Allez dans le sous-dossier data/raw/satellite_multiple/multiple_sheets/. Il y a deux versions des données labo. Ouvrez les deux fichiers et inspectez leur structure. Voyez-vous en quoi les deux fichier diffèrent ?
On va commencer par le premier fichier, qui est un peu plus propre.
Utilisez la fonction import_list() pour importer toutes les feuilles de msf_laboratory_moissala_2023-09-24_first.xlsx d’un coup, et stockez ces données dans un objet lab_data_list.
L’objet lab_data_list est une liste qui contient plusieurs data frames (un data frame par feuille). Il est possible que vous ne soyez pas encore familiarisés avec les listes. Pour le moment nous nous contenterons de jeter un œil à la structure de l’objet avec des fonctions que vous connaissez :
# Structure de la liste :
str(lab_data_list) List of 3
$ inconclusive:'data.frame': 58 obs. of 5 variables:
..$ MSF Number ID : num [1:58] 147 414 455 469 533 ...
..$ Laboratory id : chr [1:58] "BOU-4" "MOI-5" "DAN-12" "DAN-13" ...
..$ Date of the test : POSIXct[1:58], format: "2022-10-12 18:40:49" "2022-12-09 05:55:56" ...
..$ CT value : logi [1:58] NA NA NA NA NA NA ...
..$ Final Test Result: chr [1:58] "inconclusive" "inconclusive" "inconclusive" "inconclusive" ...
$ negative :'data.frame': 101 obs. of 5 variables:
..$ MSF Number ID : num [1:101] 13 397 416 490 767 ...
..$ Laboratory id : chr [1:101] "DAN-1" "DAN-9" "BOU-12" "BEK-12" ...
..$ Date of the test : POSIXct[1:101], format: "2022-09-10 21:25:04" "2022-11-21 20:02:44" ...
..$ CT value : logi [1:101] NA NA NA NA NA NA ...
..$ Final Test Result: chr [1:101] "negative" "negative" "negative" "negative" ...
$ positive :'data.frame': 888 obs. of 5 variables:
..$ MSF Number ID : num [1:888] 1 10 21 39 62 71 73 75 105 107 ...
..$ Laboratory id : chr [1:888] "BED-1" "KOU-1" "DAN-2" "BEK-1" ...
..$ Date of the test : POSIXct[1:888], format: "2022-08-20 00:00:00" "2022-09-04 15:21:36" ...
..$ CT value : num [1:888] 27 26.9 28.1 27.2 26.3 26 27.8 28.9 26.8 25.9 ...
..$ Final Test Result: chr [1:888] "positive" "positive" "positive" "positive" ...
# Nb d'éléments de la liste :
length(lab_data_list) [1] 3
# Nom des éléments de la liste :
names(lab_data_list) [1] "inconclusive" "negative" "positive"
Vous pouvez aussi cliquer sur le nom de la liste dans le panneau Environment pour inspecter sa structure et l’ouvrir dans le Viewer, comme on l’a fait pour les data frames.
Vous noterez que la View est un peu différente. Cliquez sur les boutons que vous voyez pour voir ce qui se passe.
Indice : Il y a des boutons à la gauche et à la droite du panneau Viewer, mais ceux de droite n’apparaissent que quand on passe la souris sur la ligne.
Vous pouvez accéder aux éléments d’une liste avec ces commandes
# Accéder à l'élément par son nom :
head(lab_data_list$positive) MSF Number ID Laboratory id Date of the test CT value Final Test Result
1 1 BED-1 2022-08-20 00:00:00 27.0 positive
2 10 KOU-1 2022-09-04 15:21:36 26.9 positive
3 21 DAN-2 2022-09-12 00:54:38 28.1 positive
4 39 BEK-1 2022-09-15 02:31:22 27.2 positive
5 62 BOU-1 2022-09-30 17:51:01 26.3 positive
6 71 BEK-2 2022-10-02 15:03:51 26.0 positive
# Accéder au contenu du premier élément :
head(lab_data_list[[1]]) MSF Number ID Laboratory id Date of the test CT value Final Test Result
1 147 BOU-4 2022-10-12 18:40:49 NA inconclusive
2 414 MOI-5 2022-12-09 05:55:56 NA inconclusive
3 455 DAN-12 2022-11-22 17:09:53 NA inconclusive
4 469 DAN-13 2022-11-18 07:03:57 NA inconclusive
5 533 MOI-9 2023-01-05 03:38:26 NA inconclusive
6 882 DAN-21 2023-02-05 21:57:09 NA inconclusive
# Accéder au premier élément de la liste (par position) :
lab_data_list[1] $inconclusive
MSF Number ID Laboratory id Date of the test CT value Final Test Result
1 147 BOU-4 2022-10-12 18:40:49 NA inconclusive
2 414 MOI-5 2022-12-09 05:55:56 NA inconclusive
3 455 DAN-12 2022-11-22 17:09:53 NA inconclusive
4 469 DAN-13 2022-11-18 07:03:57 NA inconclusive
5 533 MOI-9 2023-01-05 03:38:26 NA inconclusive
6 882 DAN-21 2023-02-05 21:57:09 NA inconclusive
7 1070 BEK-20 2023-01-29 19:46:36 NA inconclusive
8 1163 BEK-25 2023-02-08 01:12:21 NA inconclusive
9 1326 BED-11 2023-02-23 22:48:47 NA inconclusive
10 1600 DAN-31 2023-03-03 12:47:31 NA inconclusive
11 1869 MOI-50 2023-03-18 00:41:43 NA inconclusive
12 1948 MOI-56 2023-04-01 08:46:37 NA inconclusive
13 1987 BOU-42 2023-03-16 15:04:54 NA inconclusive
14 2242 MOI-73 2023-03-27 10:37:40 NA inconclusive
15 2268 MOI-74 2023-04-07 13:24:03 NA inconclusive
16 2449 BOU-48 2023-04-29 23:22:57 NA inconclusive
17 2536 MOI-91 2023-04-07 11:09:42 NA inconclusive
18 2928 MOI-124 2023-04-21 09:21:41 NA inconclusive
19 3068 MOI-136 2023-04-25 03:29:41 NA inconclusive
20 3580 MOI-166 2023-05-07 09:01:07 NA inconclusive
21 3759 MOI-175 2023-06-02 18:53:39 NA inconclusive
22 3820 MOI-177 2023-05-12 06:22:39 NA inconclusive
23 3928 BED-61 2023-06-09 07:45:48 NA inconclusive
24 4050 BOU-74 2023-06-18 12:26:59 NA inconclusive
25 4316 BED-70 2023-06-02 23:11:48 NA inconclusive
26 4331 BOU-80 2023-06-06 15:23:00 NA inconclusive
27 4609 MOI-216 2023-07-14 06:03:25 NA inconclusive
28 4680 MOI-225 2023-07-19 17:24:27 NA inconclusive
29 4721 MOI-229 2023-07-22 12:43:41 NA inconclusive
30 5025 BOU-86 2023-06-05 04:24:20 NA inconclusive
31 5111 MOI-249 2023-08-04 17:51:58 NA inconclusive
32 5283 MOI-261 2023-08-12 09:09:33 NA inconclusive
33 5337 BED-91 2023-06-28 16:46:56 NA inconclusive
34 5634 MOI-281 2023-08-27 08:31:02 NA inconclusive
35 5844 MOI-297 2023-06-23 13:13:51 NA inconclusive
36 6142 BOU-105 2023-09-06 17:26:24 NA inconclusive
37 6591 MOI-346 2023-09-26 10:45:51 NA inconclusive
38 6675 MOI-353 2023-08-02 07:21:04 NA inconclusive
39 6788 BOU-113 2023-08-31 16:29:50 NA inconclusive
40 6945 MOI-369 2023-07-13 08:48:11 NA inconclusive
41 7236 BED-123 2023-07-31 09:45:47 NA inconclusive
42 7314 MOI-395 2023-08-12 11:32:27 NA inconclusive
43 7594 MOI-417 2023-08-15 09:00:03 NA inconclusive
44 7743 MOI-432 2023-09-22 11:59:36 NA inconclusive
45 7776 MOI-437 2023-10-21 17:40:38 NA inconclusive
46 7876 MOI-442 2023-09-18 00:03:53 NA inconclusive
47 7900 MOI-445 2023-08-06 18:07:45 NA inconclusive
48 8076 MOI-454 2023-10-01 19:23:50 NA inconclusive
49 8317 MOI-471 2023-08-09 17:52:31 NA inconclusive
50 8332 MOI-473 2023-08-21 03:51:30 NA inconclusive
51 8772 MOI-496 2023-09-25 00:47:21 NA inconclusive
52 8834 MOI-502 2023-10-20 12:12:01 NA inconclusive
53 9089 BED-156 2023-10-22 04:24:13 NA inconclusive
54 9090 MOI-519 2023-10-15 19:57:32 NA inconclusive
55 9564 MOI-550 2023-10-15 12:26:19 NA inconclusive
56 9859 BED-166 2023-11-06 09:27:17 NA inconclusive
57 9917 MOI-585 2023-11-12 22:42:52 NA inconclusive
58 9917 MOI-585_retest 2023-11-14 22:42:52 NA inconclusive
Concaténer un un data frame
Si vous êtes sûrs que les données dans les différentes feuilles ont toutes la même structure, vous pouvez demander à R de concaténer les différents data frames en un seul à l’aide de l’argument rbind = TRUE [bind veut dire lier en anglais, et le “r” est un raccourcis pour row, la ligne; cette commande concatène les jeux de données par les lignes] :
# NE PAS EXÉCUTER (PSEUDO-CODE)
# Renvoyer un data frame concaténé :
import_list(
here(path_data_raw, "example_linelist.xlsx"),
rbind = TRUE, # Indique à R de rassembler les données en un seul df
rbind_label = "Sheet_position" # Optionnel : garde le nom de la feuille d'origine
)Utilisez la fonction import_list() pour importer toutes les feuilles de msf_laboratory_moissala_2023-09-24_first.xlsx d’un seul coup, et stockez les données dans un data frame unique lab_data.
Vous pouvez utiliser l’argument rbind_label = et lui fournir une chaîne de caractères pour le nom de la colonne et voir ce qui se passe.
Feuilles un peu différentes
Maintenant nous allons travailler avec le second jeu de données dans le dossier, msf_laboratory_moissala_2023-09-24_second.xlsx. Vous devriez avoir réalisé lors de l’exploration que ces données sont un peu moins propres :
- la colonne
CT valuen’existe que sur la troisième feuille - la colonne
Laboratory idn’est pas au même endroit sur les trois feuilles - la date des tests n’a pas le même nom sur les trois feuilles
Que va faire la fonction import_list() dans ces conditions ?
lab_data_messy <- import_list(
here(path_data_raw, "multiple_sheets",
"msf_laboratory_moissala_2023-09-24_second.xlsx"),
rbind = TRUE,
rbind_label = "Sheet_position")Exécutez le code de l’encart précédent et utilisez les outils à votre disposition pour inspecter la structure et le contenu de l’objet lab_data_messy :
- Qu’est devenue la colonne
CT value? A-t-elle été importée ? Si oui, comment est-elle remplie ?
- Et la colonne
Laboratory id? - Que sont devenues les colonnes de dates ? Ont-elles été ignorées ?
Comme vous pouvez le voir, la fonction se comporte assez raisonnablement :
La correspondance entre colonnes des différentes feuilles se fait sur leur nom : les variables qui ont le même nom dans les différentes feuilles vont être concaténées quelque soit leurs positions.
Le corollaire est que si le nom est un peu différent les colonnes seront traitées comme des variables différentes ! Vérifiez vos noms de variables !
Si une colonne n’existe que dans une feuille, elle existera dans le data frame final mais contiendra des valeurs
NApour les individus venant des feuilles ou la variable n’existe pas (rappel sur les valeursNA).
Maintenant que vous savez comment importer des données de plusieurs feuilles, on va voir comment importer les données de plusieurs fichiers.
Importer plusieurs fichiers
Le paquet {rio} étant bien fait, la fonction import_list() peut aussi recevoir un vecteur contenant des noms de fichiers à importer. Elle importe alors tous les fichier et les stocke sans une liste (sauf si vous utilisez rbind = TRUE ici aussi).
# NE PAS EXÉCUTER (PSEUDO-CODE)
# Créer un vecteur de fichiers (fictifs)
vector_paths <- c(
here(path_data_raw, "example_linelist_A.xlsx"),
here(path_data_raw, "example_linelist_B.xlsx"),
here(path_data_raw, "example_linelist_C.xlsx"),
here(path_data_raw, "example_linelist_D.xlsx"),
here(path_data_raw, "example_linelist_E.xlsx"),
here(path_data_raw, "example_linelist_F.xlsx"),
here(path_data_raw, "example_linelist_G.xlsx"),
here(path_data_raw, "example_linelist_H.xlsx"),
here(path_data_raw, "example_linelist_I.xlsx"),
here(path_data_raw, "example_linelist_J.xlsx"),
here(path_data_raw, "example_linelist_K.xlsx"),
here(path_data_raw, "example_linelist_L.xlsx"),
here(path_data_raw, "example_linelist_M.xlsx")
)
import_list(vector_paths) # renvoi une listeAllez dans le sous-dossier data/raw/multiple_files. Ouvrez les fichiers Excel pour inspecter leur contenu. Est-ce que ça aurait du sens de les concaténer en un seul data frame ?
Importez tous les fichiers Excel d’un coup et stockez-les dans un data frame linelist_data.
Inspectez le data frame créé : combien y-a-t’il de lignes ? De colonnes ? Est ce que l’import s’est passé comme vous le souhaitiez ?
Automatiser les chemins d’accès avec file.path()
Lister tous les fichiers
Lister manuellement tous les fichiers que vous vouliez importer, c’était instructif pour comprendre ce que la fonction import_list() attend comme argument, mais dans la vraie vie, c’est fastidieux et il y a un vrai risque de faire des typos et des erreurs.
Nous allons voir comment utiliser la fonction list.files() pour automatiquement générer les chemins d’accès. Son argument principal, path, accepte un chemin de dossier, et renvoi un vecteur contenant les noms des fichiers.
path_mult <- here::here(path_data_raw, "multiple_files") # optionel, pour raccourcir les instructions suivantes
list.files(path = path_mult) # Renvoi un vecteur de noms de fichier [1] "moissala_linelist_FR_BEDAYA.xlsx" "moissala_linelist_FR_BEDJONDO.xlsx"
[3] "moissala_linelist_FR_BEKOUROU.xlsx" "moissala_linelist_FR_BOUNA.xlsx"
[5] "moissala_linelist_FR_GOUNDI.xlsx" "moissala_linelist_FR_KOUMRA.xlsx"
[7] "moissala_linelist_FR_MOISSALA.xlsx" "pad_des_donnes.doc"
[9] "pas_des_donnes_non_plus.txt" "un_document.txt"
On peut demander à la fonction de renvoyer les chemins absolus plutôt que les noms de fichier avec l’argument full.names = TRUE :
list.files(path = path_mult,
full.names = TRUE) [1] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_BEDAYA.xlsx"
[2] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_BEDJONDO.xlsx"
[3] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_BEKOUROU.xlsx"
[4] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_BOUNA.xlsx"
[5] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_GOUNDI.xlsx"
[6] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_KOUMRA.xlsx"
[7] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/moissala_linelist_FR_MOISSALA.xlsx"
[8] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/pad_des_donnes.doc"
[9] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/pas_des_donnes_non_plus.txt"
[10] "/tmp/RtmpRJBDYU/file780457cda138/data/session-specific/import-multiple/satellite_multiple_FR/data/raw/multiple_files/un_document.txt"
Rappelez-vous que grâce à {here} et aux chemins relatifs identiques dans le dossier de cours, ces chemins absolus s’adapteront et fonctionneront également sur votre ordinateur.
Garder seulement les fichiers Excel
Ça commence à être pas mal, mais on a aussi listé d’autres fichiers qui traînaient dans le dossier et qu’on ne veut pas importer. Pour régler ça on va utiliser l’argument pattern de la fonction list.files() [pattern est un motif en anglais]. Ce dernier accepte une chaîne de caractères à rechercher dans les noms des fichiers, pour ne garder que certains d’entre eux. Il accepte des expressions régulières, dites regex, qui vont au delà du contenu de ce tutoriel. Ici nous vous guidons pour créer un motif adapté.
La fonction ne va renvoyer que les noms de fichier qui contiennent le motif donné :
# Tous les fichiers contenant "bedaya" :
list.files(path = path_mult, pattern = "BEDAYA") [1] "moissala_linelist_FR_BEDAYA.xlsx"
# Tous les fichiers contenant "msf_linelist" :
list.files(path = path_mult, pattern = "moissala_linelist") [1] "moissala_linelist_FR_BEDAYA.xlsx" "moissala_linelist_FR_BEDJONDO.xlsx"
[3] "moissala_linelist_FR_BEKOUROU.xlsx" "moissala_linelist_FR_BOUNA.xlsx"
[5] "moissala_linelist_FR_GOUNDI.xlsx" "moissala_linelist_FR_KOUMRA.xlsx"
[7] "moissala_linelist_FR_MOISSALA.xlsx"
# Tous les fichiers contenant "msf_ll" :
list.files(path = path_mult, pattern = "msf_ll") # Rien ne correspondcharacter(0)
# Tous les fichiers contenant "doc" :
list.files(path = path_mult, pattern = "doc")[1] "pad_des_donnes.doc" "un_document.txt"
Vous avez remarqué que dans le dernier cas deux fichiers contiennent “doc” dans leur chemin, l’un dans le nom lui même, l’autre dans l’extension ? Si on veut ne garder que le document Word il faut écrire un motif plus précis.
# Renvoyer le document word
list.files(path = path_mult, pattern = "\\.doc$") [1] "pad_des_donnes.doc"
La syntaxe du motif est plus restrictive :
Le
\\.au début est la manière des regex de dire qu’il devrait y avoir un “.” avant “doc”.Le
$à la fin du motif indique qu’il devrait être trouvé à la fin du nom de fichier.
Ce motif permet donc de chercher les fichiers qui contiennent “.doc” comme extension.
Ecrivez une commande qui utilise list.files() et du regex pour lister tous les fichiers qui finissent en “.xlsx”.
Ensuite, mettez à jour votre code précédent pour importer tous ces fichiers dans R et les concaténer en un seul data frame, sans les répétitions.
L’argument recursive = TRUE permet de faire la liste de tous les fichiers présents y compris dans les sous-dossiers de votre chemin ciblé, ce qui peut être pratique si vos données sont rangées dans des sous dossiers.
C’est fini !
Bravo, vous savez maintenant importer plusieurs fichiers ou feuilles d’un seul coup. Ce sera très utile pour importer les données agrégées par semaine ou mois qui doivent vous être familières si vous travaillez avec des données de surveillance.
Peut-être était-ce votre premier contact avec les expressions régulières, qui sont un sujet à part entière, donc ne vous inquiétez pas si cette partie vous semble un peu obscure, c’est quelque chose que vous pourrez apprendre plus tard si vous en avez besoin.
Resources
Une introduction aux expressions régulières