3 Importer des fichiers externes de données

“Friends don’t let friends use Excel for statistics!” — J. D. Cryer

3.1 Vue d’ensemble

R propose de base tous les outils pour gérer (importer et exporter) des fichiers textes, comme les fichiers .txt ou .csv. Une série de packages permet de gérer de nombreux autres formats :

  • La gestion des fichiers EpiInfo, Minitab, S-PLUS, SAS, SPSS, Stata, Systat, Octave, etc. via le package foreign ;
  • La liaison en lecture/écriture avec des bases de données (relationnelles ou non), que ce soit MySQL, MariaDB, PostgreSQL, Oracle, SQLite, MongoDB, etc. ainsi que MS Access (voir la CRAN Task View dédiée aux bases de données) ;
  • Et bien sûr la gestion complète des fichiers Excel, par exemple via les packages xlsx et WriteXLS.

Dans ce court module, nous allons utiliser deux packages en particulier, readr et readxl qui proposent une approche simple et cohérente pour importer/exporter des fichiers textes et (seulement) importer des fichiers Excel.

3.2 Fichiers texte (.txt et .csv)

L’importation de fichiers de données est généralement simple et directe, et R automatise la conversion des données dans les formats natifs de R au maximum. En revanche, plus le jeu de données est complexe, et plus le risque d’erreurs dans les choix automatiques est grand.

À noter : R n’est en principe pas vraiment limité par la taille des jeux de données. Par exemple, le nombre de lignes maximum par défaut pour un tableau est de (2^{31} - 1) (on peut regarder sous R combien de lignes cela fait). Tout est en revanche stocké dans la mémoire vive, on peut donc atteindre les limites de traitement plus rapidement, surtout sur des machines peu équipées en RAM.

3.2.1 Le package readr

On commence par charger le package :

library("readr")

Le package readr propose 7 fonctions d’importation de fichiers textes, de la forme read_*() selon leur format. Les plus importantes sont :

  • read_csv() et read_csv2() : fichiers de valeurs séparées par des virgules ou point-virgules (CSV, comma-separated values) ;
  • read_tsv() : fichiers de valeurs séparées par des tabulations (TSV, tab-separated values) ;
  • read_fwf() : fichiers de colonnes à largeurs fixées (fixed width files).

Concernant les fichiers CSV, la fonction read_csv() utilise le point (.) comme séparateur décimal, et la virgule (,) comme délimiteur de champ. Elle s’adresse de fait plutôt au monde anglo-saxon. En France, où la virgule est réservée comme séparateur décimal, on utilise plus souvent le point-virgule (;) pour délimiter les champs. La variante read_csv2() s’occupe précisément de ça (, comme séparateur décimal, ; comme délimiteur de champ).

Les mêmes fonctions existent en écriture pour exporter des fichiers textes à partir de R, sous la forme write_*(). Ce module ne couvre pas leur utilisation, qui est cependant relativement simple (consulter les fiches d’aide devrait être suffisant en cas de besoin).

La fiche de synthèse pour l’importation de fichiers texte via readr couvre bien toutes les possibilités du package. L’on pourra s’y référer pour des fichiers moins courants que ceux présentés ici. La fiche est disponible en cliquant sur la vignette ci-dessous (en anglais) :

readr

3.2.2 Spécifications du format et des colonnes

Le plus simple quand on veut importer un fichier texte est de regarder ce qu’il y a dans les premières lignes du fichier. On peut avoir un aperçu grâce à la fonction readLines(), en spécifiant bien que l’on ne veut que les 5 premières lignes (sans quoi il affichera l’intégralité du fichier) :

readLines("data/ins_culture_18_ano.csv", n = 5)
[1] "NUMINS;IDETU;COMPOS;REGIME;SITUPRE;PARIPA;DIPLOM;TYPREPA;DIPDER;NIVEAU;ETABLI;ACAETA;DEPETA;TYP_DIPL;SECTDIS;DISCIPLI;CYCLE;DEGETU;CURPAR;NBACH;NET;EFFECTIF;GROUPE;CURSUS_LMD;VOIE;NUMED"
[2] "180001;1;0040562P;10;T;100;8000246;;A;01;0040562P;02;004;NA;21;04;0;0;;0;0;1;3;L;;"                                                                                                       
[3] "180002;2;0040562P;10;A;100;8000246;;A;01;0040562P;02;004;NA;21;04;0;0;;0;0;1;3;L;;"                                                                                                       
[4] "180003;3;0040562P;10;A;100;8000246;;A;01;0040562P;02;004;NA;21;04;0;0;;0;0;1;3;L;;"                                                                                                       
[5] "180004;4;0040562P;10;A;100;8000246;;A;01;0040562P;02;004;NA;21;04;0;0;;0;0;1;3;L;;"                                                                                                       

Cela nous permet de nous assurer qu’il s’agit d’un fichier CSV à la française (avec le point-virgule utilisé comme délimiteur de champ), que l’on pourra donc importer avec la fonction read_csv2(). Au-delà du format de fichier, readr fait ce qu’il peut pour détecter automatiquement le format de chacune des colonnes du tableau de données, en analysant les 1000 premières lignes (par défaut). Pour de gros jeux de données, il est toutefois préférable de voir ce que cela donnerait avant d’importer le fichier :

spec_csv2("data/ins_culture_18_ano.csv")
ℹ Using '\',\'' as decimal and '\'.\'' as grouping mark. Use `read_delim()` for more control.

── Column specification ────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  IDETU = col_double(),
  REGIME = col_double(),
  PARIPA = col_double(),
  TYPREPA = col_double(),
  SECTDIS = col_double(),
  CYCLE = col_double(),
  DEGETU = col_double(),
  CURPAR = col_logical(),
  NBACH = col_double(),
  NET = col_double(),
  EFFECTIF = col_double(),
  GROUPE = col_double(),
  VOIE = col_logical(),
  NUMED = col_logical()
)
ℹ Use `spec()` for the full column specifications.
cols(
  NUMINS = col_character(),
  IDETU = col_double(),
  COMPOS = col_character(),
  REGIME = col_double(),
  SITUPRE = col_character(),
  PARIPA = col_double(),
  DIPLOM = col_character(),
  TYPREPA = col_double(),
  DIPDER = col_character(),
  NIVEAU = col_character(),
  ETABLI = col_character(),
  ACAETA = col_character(),
  DEPETA = col_character(),
  TYP_DIPL = col_character(),
  SECTDIS = col_double(),
  DISCIPLI = col_character(),
  CYCLE = col_double(),
  DEGETU = col_double(),
  CURPAR = col_logical(),
  NBACH = col_double(),
  NET = col_double(),
  EFFECTIF = col_double(),
  GROUPE = col_double(),
  CURSUS_LMD = col_character(),
  VOIE = col_logical(),
  NUMED = col_logical()
)

L’on peut y voir que les colonnes ont été interprétées soit comme suite de nombres (“double”), suite de caractères (“character”) ou bien suite d’opérateurs logiques TRUE/FALSE (“logical”). En épluchant la documentation du jeu de données SISE, on peut se rendre compte que ce n’est pas forcément juste… et qu’il faut donc corriger manuellement la spécification des colonnes à l’importation. On utilisera les spécifications suivantes pour toutes les colonnes présentes dans les différents fichiers SISE, avec mes annotations explicatives en commentaires (attention, certaines variables apparaissent parfois en minuscules) :

cols(
  ACAETA = col_character(), # Variable caractère de longueur 2 : 01:41
  BAC = col_character(),    # Variable caractère de longueur 4.
  BAC_RGRP = col_double(),  # Variable caractère de longueur 1 : 1:7
  COMPOS = col_character(), # Variable caractère de longueur 8.
  COMPOSR = col_character(), # Variable caractère de longueur 8.
  CONV = col_character(),   # Variable caractère de longueur 1 : C/P
  CURPAR = col_character(), # Variable caractère de longueur 2 : 00:18
  CURSUS_LMD = col_character(), # Variable caractère de longueur 1 : L/M/D
  CURSUS_LMDR = col_character(), # Variable caractère de longueur 1 : L/M/D
  CYCLE = col_double(),     # Variable caractère de longueur 1 : 0:3
  CYCLEr = col_double(),     # Variable caractère de longueur 1 : 0:3
  DEGETU = col_double(),    # Variable caractère de longueur 3 (dont 1 utile) : 0:6
  DEPETA = col_character(), # Variable caractère de longueur 3.
  DIPDER = col_character(), # Variable caractère de longueur 1 : A:Z/1:3
  DIPLOM = col_character(), # Variable caractère de longueur 7.
  DIPLOMR = col_character(), # Variable caractère de longueur 7.
  DISCIPLI = col_character(), # Variable caractère de longueur 2 : 01:40
  DISCIPLIr = col_character(), # Variable caractère de longueur 2 : 01:40
  EFFECTIF = col_double(),  # Variable caractère de longueur 3 (dont 1 utile) : 0/1
  ETABLI = col_character(), # Variable caractère de longueur 8.
  ETABLI_DIFFUSION = col_character(), # Variable caractère de longueur 36.
  FLAG_MEEF = col_double(), # Variable numérique de longueur 1 : 0/1
  GROUPE = col_character(), # Variable caractère de longueur 1 : 1:8/A
  GROUPEr = col_character(), # Variable caractère de longueur 1 : 1:8/A
  IDETU = col_character(),  # Variable caractère de longueur 11.
  NBACH = col_double(),     # Variable numérique de longueur 1 : 0/1
  NET = col_double(),       # Variable numérique de longueur 1 : 0/1
  NIVEAU = col_character(), # Variable caractère de longueur 2 : 01:13
  nonappar = col_character(), # Variable caractère de longueur 1 : A:E
  NUMED = col_character(),  # Variable caractère de longueur 4.
  NUMINS = col_character(), # Variable caractère de longueur 8.
  NUMINSR = col_character(), # Variable caractère de longueur 8.
  PARIPA = col_character(), # Variable caractère de longueur 3 : 100 pour la France ; code alphanumérique pour les autres
  REGIME = col_double(),    # Variable caractère de longueur 2 : 10:22
  SECTDIS = col_character(),   # Variable caractère de longueur 2 : 01:71 + quelques alphanumériques
  SECTDISr = col_character(),   # Variable caractère de longueur 2 : 01:71 + quelques alphanumériques
  SITUPRE = col_character(), # Variable caractère de longueur 1 : A:U
  SPECIA = col_character(), # Variable caractère de longueur 4 (dont 3 utiles) : code numérique
  SPECIB = col_character(), # Variable caractère de longueur 4 (dont 3 utiles) : code numérique
  TYPREPA = col_character(), # Variable caractère de longueur 5 : code sur 5 chiffres.
  TYP_DIPL = col_character(), # Variable caractère de longueur 2.
  TYP_DIPr = col_character(), # Variable caractère de longueur 2.
  UNIV = col_character(),   # Variable caractère de longueur 1 : O/N
  VOIE = col_double(),       # Variable caractère de longueur 1 : 0:2
  voier = col_double()       # Variable caractère de longueur 1 : 0:2
)

Au sujet des dates : readr détecte automatiquement les dates aux formats “2021-03-19” et “2021/03/19”, et les dates-heures au format ISO_8601 par exemple “2021-03-19T18:22:10+00:00”). Pour tout autre format, (par exemple “25/12/2020” ou “25/12/2020 15:18”), il faudra spécifier manuellement la colonne avec (dans ces deux cas) col_date(format = “%d/%m/%Y”) ou col_datetime(format = “%d/%m/%Y %H:%M”). On se réferrera à l’aide de la fonction parse_datetime(), et notamment les exemples, pour plus de détails sur le format. le package lubridate propose des outils simples pour aller plus loin dans la gestion des dates.

3.2.3 Importation d’un fichier CSV

La dernière chose à savoir est la manière dont sont codées les données manquantes. Sous R (entre autres), on parle de NA, pour Not Available. Il n’y a pas vraiment de règle pour trouver la bonne formule. La fonction read_csv2() prend par défaut "NA" et "" (cellule vide). Les fichiers SISE en revanche s’appuient sur les cellules vides… ainsi que le point ("."), que j’ai pu identifier après plusieurs essais infructueux.

Ceci étant posé, on peut donc passer à l’étape de l’importation proprement dite, en précisant bien ce qui fournit les données manquantes, et les spécifications de colonnes :

ins18_culture <- read_csv2("data/ins_culture_18_ano.csv", na = c("", "."), col_types =
    cols(
        ACAETA = col_character(),
        COMPOS = col_character(),
        CURPAR = col_character(), # col_logical()
        CURSUS_LMD = col_character(),
        CYCLE = col_double(),
        DEGETU = col_character(),
        DEPETA = col_character(),
        DIPDER = col_character(),
        DIPLOM = col_character(),
        DISCIPLI = col_character(),
        EFFECTIF = col_double(),
        ETABLI = col_character(),
        GROUPE = col_character(), # col_double(),
        IDETU = col_character(),
        NBACH = col_double(),
        NET = col_double(),
        NIVEAU = col_character(),
        NUMED = col_character(), # col_logical()
        NUMINS = col_character(),
        PARIPA = col_character(), # col_double(),
        REGIME = col_double(),
        SECTDIS = col_character(), # col_double(),
        SITUPRE = col_character(),
        TYPREPA = col_character(), # col_double(),
        TYP_DIPL = col_character(),
        VOIE = col_double() # col_logical(),
    ))
ℹ Using '\',\'' as decimal and '\'.\'' as grouping mark. Use `read_delim()` for more control.

On s’assure que tout s’est bien passé en regardant la structure du tableau de données :

ins18_culture
# A tibble: 81,063 x 26
   NUMINS IDETU COMPOS REGIME SITUPRE PARIPA DIPLOM TYPREPA DIPDER NIVEAU ETABLI
   <chr>  <chr> <chr>   <dbl> <chr>   <chr>  <chr>  <chr>   <chr>  <chr>  <chr> 
 1 180001 1     00405…     10 T       100    80002… <NA>    A      01     00405…
 2 180002 2     00405…     10 A       100    80002… <NA>    A      01     00405…
 3 180003 3     00405…     10 A       100    80002… <NA>    A      01     00405…
 4 180004 4     00405…     10 A       100    80002… <NA>    A      01     00405…
 5 180005 5     00405…     10 A       100    80002… <NA>    A      01     00405…
 6 180006 6     00405…     10 A       100    80002… <NA>    A      01     00405…
 7 180007 7     00405…     10 A       100    80002… <NA>    A      01     00405…
 8 180008 8     00405…     10 A       100    80002… <NA>    A      01     00405…
 9 180009 9     00405…     10 A       100    80002… <NA>    A      01     00405…
10 180010 10    00405…     10 A       100    80002… <NA>    A      01     00405…
# … with 81,053 more rows, and 15 more variables: ACAETA <chr>, DEPETA <chr>,
#   TYP_DIPL <chr>, SECTDIS <chr>, DISCIPLI <chr>, CYCLE <dbl>, DEGETU <chr>,
#   CURPAR <chr>, NBACH <dbl>, NET <dbl>, EFFECTIF <dbl>, GROUPE <chr>,
#   CURSUS_LMD <chr>, VOIE <dbl>, NUMED <chr>

Exercice 1 : Dans cet exercice, l’objectif est simplement de reproduire les étapes précédentes afin d’importer le fichier concernant les inscriptions à l’université ins_univ_18_ano.csv, présent dans le même dossier.

3.3 Fichiers Excel (.xls et .xlsx)

3.3.1 Le package readxl

Le package readxl permet tout simplement, comme son nom l’indique, d’importer des classeurs Excel, que ce soit au format .xls ou bien .xlsx. On commence donc par charger le package :

library("readxl")

À noter que ce package fournit très peu de fonctions, et permet uniquement d’importer des classeurs Excel, et pas d’exporter des données R dans un classeur Excel.

3.3.2 Importation d’un classeur Excel

De manière surprenante, l’importation de fichiers Excel est finalement beaucoup plus simple, puisque R peut directement utiliser l’information du classeur Excel pour définir les spécifications de colonnes. On peut en revanche vérifier la présence d’un ou plusieurs onglets, ainsi que leurs noms, avec la fonction excel_sheets :

excel_sheets("data/2018 2019 Apogée anonymisée.xlsx")
[1] "Apogee"

L’importation se fait ensuite directement via la fonction générique read_excel, ou bien directement les fonctions read_xls et read_xlsx si l’on veut spécifier précisément le format XLS vs. XLSX :

apogee_18 <- read_excel("data/2018 2019 Apogée anonymisée.xlsx")

À nouveau, l’on s’assure que tout s’est bien passé en regardant la structure du tableau de données :

apogee_18
# A tibble: 57,699 x 41
   Decede Annee_univ   idetu Civilite Date_naiss          Code  Departement_pays
   <chr>       <dbl>   <dbl> <chr>    <dttm>              <chr> <chr>           
 1 non          2018 1035789 Mme      2000-10-05 00:00:00 84    VAUCLUSE        
 2 non          2018 2211839 M        1997-06-16 00:00:00 6     ALPES MARITIMES 
 3 non          2018 2211840 M        2000-04-13 00:00:00 84    VAUCLUSE        
 4 non          2018  921768 Mme      1993-10-08 00:00:00 205   LIBAN           
 5 non          2018  921685 Mme      1994-10-02 00:00:00 352   ALGERIE         
 6 non          2018 2211841 M        1992-03-27 00:00:00 127   ITALIE          
 7 non          2018  921909 Mme      1995-10-06 00:00:00 205   LIBAN           
 8 non          2018  921827 M        1995-05-05 00:00:00 134   ESPAGNE         
 9 non          2018 2211842 Mme      1990-01-08 00:00:00 331   BURKINA FASO    
10 non          2018  921775 M        1988-05-01 00:00:00 352   ALGERIE         
# … with 57,689 more rows, and 34 more variables: Code_nationalite <dbl>,
#   Libelle_nationalite <chr>, Bac <chr>, Bac_regroup <chr>, Annee_bac <dbl>,
#   Departement_bac <chr>, Regime <chr>, Bourse <chr>, Echelon_bourse <chr>,
#   Pcs_parent <chr>, Pcs_parent_autre <chr>, Lic_tpd <chr>, Composante <chr>,
#   Domaine_disciplinaire <chr>, Mention <chr>, Code_sise_diplome <dbl>,
#   Diplome <chr>, Code_diplome <chr>, Intitule1_sise <chr>, Code_etape <chr>,
#   Lib_etape <chr>, Intitule2_sise <chr>, Version_de_diplome <chr>,
#   Inscription_premiere <chr>, Nature_diplome <chr>, Temoin_dip <chr>,
#   Cp_annuel <dbl>, Pays_annuel <chr>, Cp_fixe <dbl>, Pays_fixe <chr>,
#   Date_inscription_iae <dttm>, Prem_insc_univ_fr <dbl>,
#   Date_premiere_ins_ens_sup <dbl>, Prem_insc_etab <dbl>