4 min read

Manipulation des données volumineuses (Big Data) avec R

Contexte

En référence à ce twit d’un internaut demandant comment importer et manipuler un fichier de 4,5 Gb dans R avec une machine de 8Gb de RAM, j’ai decidé de faire un petit post sur l’importation et la maniplation des données sous R. Dans ce petit post, nous allons faire le tour des différentes solutions possibles. Dans un passé récent, R n’était pas connu pour la manipulation des fichiers assez volumineux mais la situation a assez évolué avec le développement de nouveaux packages.

Dans tous les cas, la puissance d’importation et de manipulation des fichiers volumineux dans R dépend aussi de la puissance de la machine utilisée. Pour illustrer ce post, nous utiliserons un fichier de près de 6 millions de lignes et 35 variables

La solution avec les packages

fichier <- "C:/Users/Samati/Desktop/r/SPSS_RGPH4/population.csv"

Les solutions avec les packages sont assez nombreuses:data.table,disk.frame, vroom, arrow, sparklyr, disk.frame, etc. Dans ce post nous allons aborder data.table et disk.frame

1. data.table

A mon avis, c’est le package naturel pour importer et manipuler les données volumineuses dans R. C’est un package assez puissant et assez simple à apprendre. Je suis toujours assez fasciné par sa simplicité dans la manipulation des données. Pour l’importation de fichier csv, data.table dispose la fonction fread() et pour l’exportation fwrite()

library(data.table)
dt <- fread(fichier)
dim(dt);class(dt)
## [1] 5847453      35
## [1] "data.table" "data.frame"

Un objet créé avec data.table est de class data.table et data.frame, en gros on peut utiliser les syntaxes de base de R pour manipuler les objets importer avec data.table. Ainsi si nous vous voulons l’effectif par sexe et l’age moyen des individus :

dt[,sexe := as.factor(P04)] # Renommer la colonne P04 en sexe
setattr(dt$sexe,"levels",c("Masculin","Feminin"))# Renommer les modalités de la variable sexe
kable(
dt[,by=sexe,.(.N,mean(P06AGE, na.rm = TRUE))] 
) # compter les individus,calculer l'âge moyen par sexe
sexe N V2
Masculin 2839319 22.08893
Feminin 3008134 23.94456

Si jamais vous êtes familier avec le package dplyr de la famille de tidyverse, vous pouvez utiliser les syntaxe de dplyr avec les objets data.table à travers dtplyr comme ceci.

library(dtplyr)
library(dplyr)
df <- lazy_dt(setDF(dt))

kable(
df %>% group_by(sexe) %>% 
       summarise(Effectif = n(),
                 age_moy = mean(P06AGE,na.rm = TRUE))
)
sexe Effectif age_moy
Masculin 2839319 22.08893
Feminin 3008134 23.94456

2. disk.frame

Un autre package que je trouve assez intéressant, c’est le package disk.frame. C’est assez comparable à data.table en terme de puissance. On peut utiliser un objet disk.frame avec les syntaxes de dplyr et de data.frame.

Les options nécessaires,

library(disk.frame)
setup_disk.frame()
options(future.globals.maxSize = Inf)

-L’importation du fichier avec disk.frame


disk_df <- csv_to_disk.frame(
  fichier, 
  outdir = "population",
  overwrite = T)
  • La manipulation des données du disk.frame avec dplyr
kable(
disk_df %>% group_by(P04) %>% 
            summarise(Effectif = n(),
            age_moy = mean(P06AGE,na.rm = TRUE)) %>% 
            collect()
)
  • La manipulation des données du disk.frame avec data.table
kable(
collect(disk_df)[,by=P04,.(.N,mean(P06AGE, na.rm = T))]
)

La solution base de données

Dans un article précédent, nous avions déjà abordé l’utilisation des base de données avec R et python. Cette solution devient particulièrement intéressante dans le contexte des données volumineuses. Pour l’illustrer, nous avons crée une petite base de données sous SQLite avec les données précédentes (PostgreSQl est aussi une solution intéressante).

library(RSQLite)
library(DBI)
library(dplyr)
setwd("C:/Users/Samati/Desktop/r/SPSS_RGPH4")
con <- dbConnect(RSQLite::SQLite(), dbname = "census.sqlite3")
dbListTables(con)
## [1] "deces"      "menage"     "population"

On a donc dans notre base, trois table: population, décès et ménage.

On peut alors refaire notre calcul comme ceci mais cette fois-ci de deux façons:

  • Soit en utilisant dplyr
population <- tbl(con, "population")

knitr::kable(
  population %>% group_by(P04) %>% 
             summarise(Effectif = n(),
                     age_moy = mean(P06AGE,na.rm = TRUE))
  )
P04 Effectif age_moy
1 2839319 22.08893
2 3008134 23.94456

-soit une requêt SQL

SELECT P04, COUNT() AS Effectif, AVG(P06AGE) AS age_moy
FROM population
GROUP BY P04
Table 1: 2 records
P04 Effectif age_moy
1 2839319 22.08893
2 3008134 23.94456

En conclusion, chaque méthode dépend de ce qu’on voudrait faire avec les données et la méthode dans laquelle vous plus à l’aise pour la manipulation des données.