Introduction

Pour rappel, R est un langage statistique avec un ensemble de fonctions de base. Ces fonctionnalités peuvent être étendues grâce à l’utilisation de librairies. Il en existe un (très) grand nombre et il est parfois compliqué de choisir lesquelles utiliser. Un critère à prendre en compte est la personne et/ou l’entité qui la produit. Un développeur seul aura moins de temps pour développer/améliorer/maintenir une librairie qu’il a développé, par rapport à un laboratoire ou une entreprise.

L’équipe de R Studio a développé un ensemble de packages pour la Data Science dénommé tidyverse, et à la vue de l’expérience de l’entreprise, nous pouvons raisonnablement pensé que ceux-ci seront pérennes dans le temps. C’est pourquoi nous avons décidé de les présenter dans ce cours.

Dans la suite, nous allons donc voir quelques uns de ces librairies et leur utilisation. Il faut exécuter les commandes ci-dessous pour voir ce qu’elles réalisent, et ne pas hésiter à les modifier pour les comprendre.

Structure de données

Le package tibble permet de définir une nouvelle structure (tbl_df), basée sur un data.frame.

library(tibble)
as_tibble(mtcars)

Les différences avec un data.framesont minimes, mais importantes :

  • il n’y a plus de rownames
  • les noms de variables ne sont pas modifiées (il est possible d’avoir des espaces dedans)
  • lors de la création, les chaînes de caractères ne sont pas transformés en factor
  • à l’affichage, il n’y a que les deix premières lignes qui sont affichés, et les premières colonnes jusqu’à la largeur possible. Une phrase est ajoutée à la fin pour dire combien de lignes (et de colonnes si besoin) ne sont pas affichés. On voit aussi le type de chaque colonne

Importation de données

Données classiques (objets décrits par des variables)

Le tidyverse inclut trois packages permettant l’importation de données externes, contenues dans des fichiers (ainsi que l’exportation). Nous allons ici importer des données classiques (150 iris de trois espèces différentes, décrits par 4 variable), disponibles dans trois formats : texte délimité, Excel et SAS.

Les fonctions qu’on va utiliser renvoient un objet de type tibble. Pour plus d’options, il faut aller voir l’aide de chaque package.

Texte délimité

Pour cela, nous utilisons le package readr, qui améliore les fonctions déjà présentes. Les nouvelles fonctions sont plutôt performantes, et comprennent les données, ce qui permet de limiter les besoins de paramétrisation de celles-ci.

library(readr)
iris_txt = read_delim("Iris.txt", delim = "\t")

Fichier Excel

Le package readxl permet donc de lire des fichiers Excel (quelque soit le format : xlsx ou xls). La fonction read_excel() détecte justement le format du fichier.

library(readxl)
iris_xlsx = read_excel("Iris.xlsx")

Fichier SAS

Enfin, pour importer des données SAS mais aussi SPSS et Stata, il existe le package haven, avec la fonction read_sas().

library(haven)
iris_sas = read_sas("Iris.sas7bdat")

Données autres

De type JSON

Le format JSON est un format très utilisées pour les données sur le web, qui sont souvent structurées autrement qu’avec des objets décrits par le même ensemble de variable.

Par exemple, le site SWAPI nous permet de récupére toutes les informations sur les personnages, planètes et vaisseaux spatiaux de la saga Star Wars. Le résultat en première page nous permet d’avoir un aperçu des objets JSON.

La librarie jsonlite permet de lire et d’écrire des données dans le format JSON.

library(jsonlite)
fromJSON("https://swapi.co/api/people/1/")

Page web

Pour lire une page web, nous pouvons utiliser le package rvest, qui permet donc de récupérer le contenu d’une page web.

library(rvest)
sw = read_html("https://www.imdb.com/title/tt0076759/")

Dans celle-ci, on sait que la note est dans la première balise span dans la div de la classe ratingValue. On peut la récupérer directement comme ci-dessous (la syntaxe avec %>% est expliquée plus tard).

sw %>% 
  html_nodes(".ratingValue span") %>% 
  head(1) %>% 
  html_text() %>%
  as.numeric()

Vous remarquerez l’utilisation de l’opérateur %>% pour enchaîner les opérations. Celui-ci est fournit par le package magrittr, lui aussi dans le tidyverse. Ce formalisme permet d’avoir un code plus lisible, car déroulant les étapes les unes après les autres, contrairement à la programmation classique dans R. Voici un exemple d’utilisation simple.

x = rnorm(1000)
mean(x)
x %>% mean()

Interrogation de données

Pour tout ce qui est manipulation de données, au sens SQL, nous disposons du package dplyr qui fournit toutes les fonctions utilisées ici. Bien qu’il y ait aussi des fonctions de base dans R, ce package est préférable.

library(dplyr)

Restriction et projection

Ici, les deux opérations consistent respectivement à sélectionner des lignes (restriction) ou des colonnes (projection).

Restriction par filtre

mtcars %>% filter(mpg > 30)
mtcars %>% filter(mpg > 30 & qsec < 19)
mtcars %>% filter(carb == 8)
mtcars %>% filter(between(mpg, 30, 32))

Sélection de lignes par indices

mtcars %>% slice(1:5)
mtcars %>% slice(25:n()) # n() indique le nombre de lignes de la table
mtcars %>% slice(seq(1, n(), by = 4))

Projection

head(mtcars) %>% select(mpg)
head(mtcars) %>% select(qsec, mpg, hp)
head(mtcars) %>% select(5, 3, 9)
head(mtcars) %>% select(starts_with("c")) # d'autres possibilités de ce type existent (voir ?select_helpers)

Suppression des doublons

head(mtcars) %>% select(cyl)
head(mtcars) %>% select(cyl) %>% distinct()

Tri

La fonction arrange() permet le tri sur une ou plusieurs variables, avec desc() pour indiquer un ordre décroissant.

head(mtcars) %>% arrange(mpg)
head(mtcars) %>% arrange(am, mpg)
head(mtcars) %>% arrange(desc(mpg))

Ajout de variables

Comme vous avez pu le remarquer, dans les différents exemples ci-dessus, le nom des lignes est supprimé. Pour le récupérer, nous utilisons la fonction rownames_to_column(), qui permet de le transformer en variable.

head(mtcars) %>% rownames_to_column(var = "car")

Pour ajouter une variable, il existe la fonction mutate().

head(mtcars) %>% mutate(ratio = wt / hp, zero = 0, wt = NULL, hp = NULL)
head(mtcars) %>% mutate(n = row_number(), rang_mpg = min_rank(desc(mpg)))

Si l’on veut garder uniquement la variable nouvellement créée, on utilise transmute().

head(mtcars) %>% transmute(ratio = wt / hp)

Calcul d’agrégat

Pour résumer une ou plusieurs variables, nous utilisons la fonction summarise().

mtcars %>% summarise(
  n = n(),
  mpg_mean = mean(mpg),
  nbtype_carb = n_distinct(carb)
)

Et si l’on souhaite effecture par groupes déterminés par les modalités d’une variable, il existe la fonction group_by().

mtcars %>%
  group_by(cyl) %>%
  summarise(
  n = n(),
  mpg_mean = mean(mpg),
  nbtype_carb = n_distinct(carb)
)

Jointure

Pour tester les méthodes, nous créons une nouvelle table engine, indiquant le type en fonction de la valeur de cyl. Vous remarquerez que par rapport à mtcars, il y a une valeur en moins (4) et une valeur en plus (12).

engine = tibble(
  cyl = c(6, 8, 12),
  type = c("medium", "big", "very big")
)

Nous pouvons maintenant utiliser les différentes fonctions de jointure disponibles.

mtcars %>% inner_join(engine)
mtcars %>% left_join(engine)
mtcars %>% right_join(engine)
mtcars %>% full_join(engine)
mtcars %>% semi_join(engine)
mtcars %>% anti_join(engine)

Manipulations spécifiques

Il est parfois utile de modifer la structure des données, par exemple dans le cadre d’une visualisation de données avec ggplot que nous verrons plus tard. Dans ce cas, plutôt que d’avoir un tableau individus décrits par des variables, il est nécessaire d’avoir un tableau à trois variables : individu, variable, valeur de la variable. Le package tidyr contient les fonctions nécessaires pour faire les opérations dans les deux sens (et donc de pouvoir faire une transposée en réalisant une double opération).

Pour créer une table longue à partir d’une table large, nous utilisons gather().

tab_longue = mtcars %>% 
  rownames_to_column("car") %>%
  as_tibble() %>%
  gather(variable, value, -car)
tab_longue

A l’inverse, pour avoir une table large à partir d’une table longue, c’est la fonction spread(). On réalise ici la transposée de la table mtcars.

tab_large 
tab_longue %>%
  spread(car, value)

Données spécifiques

Chaînes de caractères

Le package stringr permet de gérer les chaînes de caractères de manière plus simple que les fonctions de base. Voici quelques exemples de manipulations classiques.

library(stringr)
mt = mtcars %>% rownames_to_column("car")

str_length(mt$car)
str_c(mt$car, collapse = ", ")
str_sub(mt$car, 1, 3)

Expressions régulières

Une expression régulière permet de décrire une chaîne de caractères, pour retrouver toutes les chaînes correspondant à la description. C’est un outil très puissant, et pas forcément simple à maîtriser.

str_subset(mt$car, "Merc")
str_subset(mt$car, "[0-9]")
str_detect(mt$car, "[0-9]")
str_match(mt$car, "(.+)[ ](.+)")
str_split(mt$car, " ")

Dates

Ici, c’est le package lubridate qu’il faut utiliser pour manipuler facilement les dates, comme dans les exemples ci-dessous.

library(lubridate)
now()
today = today()
today
year(today)
month(today)
month(today, label = TRUE)
month(today, label = TRUE, abbr = FALSE)
day(today)
mday(today)
wday(today)
wday(today, label = TRUE)
wday(today, label = TRUE, week_start = 1)
wday(today, week_start = 1)
yday(today)

Durées et intervales

Ce package permet de définir des périodes, et donc de les ajouter à une date (par exemple, pour avoir la date un an après un évènement).

today + period(week = 1, day = 3)
today + period("1W 3D")
today - years(1) - days(10)

On peut avoir faire des différences entre dates (par défaut en jour), et avoir cette valeur en secondes ou en années.

bday = ymd("19771114")
diff = today - bday
diff
as.period(diff)
as.duration(diff)

Enfin, on peut même évaluer si une date est dans un intervale ou non.

nextyear = today + years(1)
int = interval(today, nextyear)
ymd("20190101") %within% int
ymd("20191101") %within% int

Pour information, la librairie hms permet elle de gérer les heures, minutes et secondes mais nous ne les verrons pas ici.

A faire

La banque mondiale fournit un grand nombre de données, dont des indicateurs de gouvernance au niveau mondial (voir ici), pour la période 1996-2016.

Vous trouverez les informations dans ces quatre fichiers :

  1. Importer les données dans quatre tibbles
  2. Créer la table des valeurs avec une ligne par pays | indicateur | année, que vous stockerez dans un cinquième tibble
  3. Donner le nombre de pays présent dans les données
  4. Top des pays selon le contrôle de la corruption
    1. Dans la table initiales des valeurs, ne retenir que les valeurs dont le TypeCode est "EST".
    2. Pour la série "CC" et le type "EST", quelles sont les valeurs moyenne, maximale et minimale ? (utiliser la table longue)
    3. Je veux les mêmes informations, mais pour chaque code de pays
    4. Maintenant, on souhaite classer les pays par ordre décroissant de la valeur moyenne.
    5. Pour finaliser le tableau, on souhaite avoir les noms des pays plutôt que le code (pas les deux informations - nom du pays en premier)
  5. Evolution des indicateurs de la France sur la période analysée
    1. Donner toutes les informations concernant la France (années en colonnes)
    2. Dans ce résultat, on ne souhaite que les valeurs dont le type est "Estimate".
    3. Ce tableau n’étant pas très clair, nous allons ajouter en début de tableau les noms des séries et supprimer toutes les informations inutiles.
    4. Maintenant, nous souhaitons un tableau devant contenir en ligne les années et en colonnes les séries. On va en quelque sorte réaliser une transposée de la table précédente.
  6. Pour quel pays (nom du pays) y a t’il des données manquantes ? et en quelle quantité ? (trier les pays dans l’ordre décroissant du nombre de valeurs manquantes)