L'un des enjeux actuels autour du traitement et de l'analyse de données se trouve au niveau des étapes de préparation (nettoyage, filtre, restructuration) et d'exploration (statistique, visualisation, cartographie) des jeux de données.
L'open data c'est super, mais bien souvent les jeux de données mis à disposition ne sont pas "prets" et "optimisés" à être directement intégrés au sein des logiciels d'analyse comme les SIG.
La principale problématique est bien celle du volume des données. Dans un fichier .csv ou .xls classique, la plupart des opérations de préparation et d'exploration de données peuvent aisement être réalisées avec excel ou librecalc.
Mais lorsqu'on commence à vouloir manipuler et explorer des jeux de données plus volumineux (plus d'un million de lignes), ces logiciels familiers ne sont plus en capacité de répondre aux besoins. Et le passage à des environnement plus "adapatés" aux données plus volumineuses comme R s'impose.
L'une des solutions les plus efficaces pour cette étape primordiale de "Dataprep" réside dans l'utilisation de l'environnement R qui permet au sein d'un outil unique de disposer d'une multitude de fonctionnalités pour répondre aux besoins des explorateurs de données.
Nous explorons ici la collection de packages proposée au sein du Tidyverse qui permettent de manipuler des données, les restructurer, les modifier, les filtrer, les agréger, les visualiser,… de manière assez "simple".
Développé par Hadley Wickham il propose une "grammaire de la structuration et de la maniplation de données dans R". Tidy data > "Données ordonnées"
Cette collection propose les packages suivants :
Concrétement il s'agit d'une suite d’outils graphiques et de manipulation de données cohérents reposant sur une grammaire qui rend la programmation dans R cohérente, lisible et intuitive.
Cheatsheets dplyr et tidyr en français >
https://thinkr.fr/aide-memoire-manipulation-de-donnees-avec-dplyr-tidyr/
Ressources :
En théorie tout parait simple, mais l'environnement R repose sur un langage propre qui n'est pas forcemment accessible et compréhensible à tous de prime abord…et souvent malgrès des potentialités très intéressantes, les non spécialistes se découragent et de fait se limitent dans leurs manières d'appréhender la préparation de données.
L'objectif de ce document est de proposer une syntaxe "simple" et "accessible" pour la manipulation de données avec R
Dans cet exemple nous nous basons sur le jeu de données des signalements de l'application Dans Ma Rue de la ville de Paris.
Ce jeu de données mis à jour quotidiennement est disponible sur le portail open data de la ville de Paris
Ce jeu de données est particuliérement intéressant à plusieurs titres :
2 options:
DMR <- read.csv("D:/Articles/DansMaRue/dans-ma-rue_2020.csv", encoding="UTF-8", sep=";")
Voilà le jeu de données est dans R !
install.packages("tidyverse")
library(tidyverse)
Quand on manipule un tableau de données, il est très fréquent d’enchaîner plusieurs opérations. On va par exemple filtrer pour extraire une sous-population, sélectionner des colonnes puis trier selon une variable.
Pour simplifier et améliorer encore la lisibilité du code, on va utiliser un opérateur indispensable, baptisé pipe
Le pipe a été introduit à l’origine par l’extension magrittr, et repris par dplyr
Le pipe se note %>% il peux se traduire par "ensuite"
la fonction Select
permet de selectionner et de renommer les colonnes (champs) dans un nouveau dataframe
Par exemple je veux constituer un nouveau jeu de données avec uniquement :
La syntaxe de la commande est la suivante :
DMRsimplifie <- DMR %>% select(Type = TYPE, Arrondissement = ARRONDISSEMENT, Date = DATEDECL, Intervenant = INTERVENANT, Coordonnées = geo_point_2d)
Ne pas sélectionner une ou certaine colonne (ici toutes les colonnes sauf celle des coordonnées géographiques
DMRnotgeo <-DMR %>% select(everything(), -c("geo_point_2d"))
La fonction Rename
permet de renommer un champ.
On spécifie le nouveau nom du champ = l'ancien nom du champ
Je veux renommer le champ "Arondissement" en "Ardt"
DMRsimplifie <- DMRsimplifie %>% rename(Ardt = Arrondissement)
La fonction Filter
permet de filtrer des lignes en fonctions des modalités des colonnes.
On peut combiner plusieurs conditions logiques :
Par exemple je veux garder uniquement les signalements de type Graffitis, tags, affiches et autocollants en utilisant l'opérateur ==
Graffitis <- DMR %>% filter(TYPE == 'Graffitis, tags, affiches et autocollants')
Il est aussi possible d'utiliser l'opérateur %in%
Graffitis <- DMR %>% filter(TYPE %in% 'Graffitis, tags, affiches et autocollants')
Garder les signalements de type Graffitis, tags, affiches et autocollants OU Propreté.
L'opérateur OR est ici mobilisé via le symbole |
Graffitisetproprete <- DMR %>% filter(TYPE == 'Graffitis, tags, affiches et autocollants' | TYPE == 'Propreté')
Garder les signalements de type Proprété ET dans le 10eme arrondissement
L'opérateur AND est ici mobilisé via le symbole , ou &
Proprete10eme <- DMR %>% filter(TYPE == 'Propreté', ARRONDISSEMENT== '10')
Garder les signalements relatifs à la propreté ET qui ont eu lieu en 2018 ET dans le 18eme arrondissement
Sign2018et18emeetProprete <- DMR %>% filter(ANNEE.DECLARATION==2018 & ARRONDISSEMENT==18 & TYPE=='Propreté')
Garder tous les signalements relatifs à la propreté dans le 1er, le 2eme et le 3eme arrondissement ET qui ont eu lieu depuis 2016
Signalementdepuis2016 <- DMR %>% filter(TYPE == 'Propreté', ARRONDISSEMENT == '1' |ARRONDISSEMENT == '2'| ARRONDISSEMENT == '3', ANNEE.DECLARATION == '2016' | ANNEE.DECLARATION == '2017' |ANNEE.DECLARATION == '2018' |ANNEE.DECLARATION == '2019')
On peux égallement venir compléter les critères de selection en utilisant des fonctions supplémentaires :
%in%
> équivalent du in en SQL (groupe de modalités)grepl
> équivalent du like en SQL (intérroger une chaîne de charactères)Garder tous les signalements relatifs aux 1er, 2eme, 3eme 4eme et 5eme arrondissement grâce à la fonction %in%
Signalementscentre <- DMR %>% filter(ARRONDISSEMENT %in% c(1,2,3,4,5))
Garder tous les signalements sauf ceux relatifs aux 1er, 2eme, 3eme 4eme et 5eme arrondissement grâce à la fonction %in% couplée à la clause négative !
Signalementsnotcentre <- DMR %>% filter( ! ARRONDISSEMENT %in% c(1,2,3,4,5))
Garder les signalements de type Graffitis, dans les arrondissements du centre (1,2,3,4,5), qui ont eu lieu l'été (juin, juillet, août) et depuis 2017 grâce à la fonction %in%
Signalementetecentre <- DMR %>% filter(TYPE =='Graffitis, tags, affiches et autocollants', ARRONDISSEMENT %in% c(1,2,3,4,5), ANNEE.DECLARATION %in% c(2017,2018,2019), MOIS.DECLARATION %in% c(6,7,8))
La fonction grepl
peux être utilisée de différentes manières dans l'interrogation de chaines de charactères :
- grepl("valeur", variable) renvoie toute les lignes contenant la chaine de charatères
- grepl("^valeur", variable) renvoie toute les lignes contenant le début de chaine de charatères
- grepl("valeur$", variable) renvoie toute les lignes contenant la fin de chaine de charatères
Garder les signalements relatifs aux "vélos" grâce à la fonction grepl (équivalent du like en SQL)
Velos <- DMR %>% filter(grepl("vélo", SOUSTYPE) | grepl("cycl", SOUSTYPE))
Garder les signalements relatifs aux "stationnement" grâce à la fonction grepl (équivalent du like en SQL)
Stationnement <- DMR %>% filter(grepl("tationne", SOUSTYPE))
Garder les signalement des arrondissement supérieurs au 10eme
ARDT10etplus <- DMR %>% filter(grepl("^7501", CODE_POSTAL))
Filtres basés sur des sous sélections
La fonction arrange
permet de trier un tableau selon l'ordre d'une variable (tri alphabétique, croissant, décroissant)
Trier tous les signalements par arrondissement
Signalementtriardt <- DMR %>% arrange(ARRONDISSEMENT)
Trier tous les signalements par date (croissante)
SignalementTriDate <- DMR %>% arrange(DATEDECL)
Trier tous les signalements par date (décroissante)
SignalementTriDate <- DMR %>% arrange (desc(DATEDECL))
Trier tous les signalements par arrondisement et par date
SignalementTriArdtDate <- DMR %>% arrange(ARRONDISSEMENT,DATEDECL)
La fonction mutate
permet de créer des nouvelles colonnes pour par exemple effectuer un calcul, une reclassification, une concaténation, une séparation ou encore reformater une variable.
Je veux concatener (fusionner au sein de la même colonne) le TYPE et le SOUSTYPE des signalements avec comme séparteur des deux modalités un tiret (-)
DMRconcat <- DMR %>% mutate(Typecomplet=paste(TYPE, SOUSTYPE, sep='-'))
On peux aussi utiliser la fonctionunite
pour concaténer plusieurs champs
Par exemple je veux concaténer le mois et l'année dans un champ unique.
Attention avec cette méthode, les colonnes concaténer disparaissent du dataframe
DMRUnite <- DMR %>% unite("Datebis", MOIS.DECLARATION, ANNEE.DECLARATION, sep= "/")
La fonction separate
permet de diviser les valeurs d'un champ au sein de plusieurs colonnes
Je veux diviser le champ des coordonées géographique geopoint2D en deux colonnes distinctes (latitude et longitude) car ces informations sont concatenées au sein de la même colonne
DMRGEO <- DMR %>% separate(col= geo_point_2d, into = c("Latitude", "Longitude"), sep= ",")
Dans beaucoup de cas les colonnes ne sont pas formatées dans des formats adéquats (texte, numeric, date,…) et il faut procéder à un reformatage des valeurs des colonnes.
Dans l'environnement R cette opération peux s'effectuer en combinant la fonction mutate
et la reformatage désiré
as.character pour passer en chaine de charactères
as.numeric / as.integer pour passer en format numeric
as.date pour passer en format date
...
Je veux transformer la variables DATEDECL (à la base en factor) en format date
DMR <- DMR %>% mutate(DATEDECL = as.Date(DATEDECL))
DMR$DATEDECL <- as.Date(DMR$DATEDECL)
Avec cette modification les sélections basées sur des dates sont plus simples ;)
Garder les signalements entre le 1er mars 2018 et le 30 juin 2019
SignalementsMars2018Juin2019 <- DMR %>% filter(DATEDECL >= as.Date("2018-03-01") & DATEDECL <= as.Date("2019-06-30"))
Cette fonction permet de renvoyer les valeur uniques d'une variable ou de compter les valeur uniques des ID par exemple.
Je veux sortir la liste des "Intervenants"
Intervant <- DMR %>% distinct(INTERVENANT)
Je veux compter le nombre de dates différentes, donc le nombre de jour présents dans la base
length(unique(DMR$DATEDECL))
Les fonctions group_by
pour le regroupement et summarise
pour les résumés statistiques (compte, somme, moyenne, médiane,…) sont très utiles face à de gros de de données caractérisés par des variables qualitatives et quantitatives.
Compte > n()
Moyenne > mean()
Somme > sum()
Médiane > median()
...
Ces fonctions permettent notamment de créer rapidement des tableaux croisés dynamiques.
Je veux par exemple compter le nombre de signalements par arrondissement
NbsignalementARDT <- DMR %>% group_by(ARRONDISSEMENT) %>% summarise(NBSignalements = n())
Je veux compter le nombre de signalements par arrondissement et par année
NbsignalementARDTAnnee <- DMR %>% group_by(Ardt = ARRONDISSEMENT, Annee = ANNEE.DECLARATION) %>% summarise(NBSignalements = n())
Je veux compter le nombre de signalements par arrondissement, par année et par type
NbsignalARDTAnneeType <- DMR %>% group_by(ARRONDISSEMENT, ANNEE.DECLARATION, TYPE) %>% summarise(NBSignalements = n())
Je veux calculer le nombre moyen de signalements par mois de chacun des arrondissement
MeansignalArdtMois <- DMR %>% group_by(ARRONDISSEMENT, MOIS.DECLARATION) %>% summarise(NBMoyen = mean(n()))
Je veux calculer le nombre moyen de signalements quotidiens en 2018 par arrondissement
MeansignalJourArdt <- DMR %>% filter(ANNEE.DECLARATION==2018) %>% group_by(ARRONDISSEMENT) %>% summarise(NbMoyen=(n()/365))`
Je veux compter le nombre total et calculer le nombre moyen de signalements quotidiens en 2015, 2016, 2017 et 2018
NbsignalAnne <- DMR %>% filter(ANNEE.DECLARATION %in% c(2015,2016,2017,2018)) %>% group_by(ANNEE.DECLARATION) %>% summarise(Total = n(), Moyennequotidienne = n()/365)
Je veux calculer la proportion de chacun des types de signalements
PropType <- DMR %>% group_by(TYPE) %>% summarise(nb = n(), prop = (n()/nrow(DMR))*100) %>% mutate(prop = round(prop,1)) %>% arrange(desc(prop))
Je veux calculer la proportion de chacun des types de signalement pour le 1er arrondissement
PropType1erArdt <- DMR %>% filter(ARRONDISSEMENT=='1') %>% group_by(ARRONDISSEMENT, TYPE) %>% summarise(nb = n()) %>% arrange(ARRONDISSEMENT, desc(nb)) %>% ungroup(.self) %>% mutate( total = sum(nb), Prop = (nb/total)*100, Prop = round(Prop,1)) %>% arrange(desc(Prop))
Je veux calculer la proportion de chacun des types de signalement pour chaque arrondissement
PropTypeArdt <- DMR %>% group_by(ARRONDISSEMENT, TYPE) %>% summarise(nb = n()) %>% arrange(ARRONDISSEMENT, desc(nb)) %>% ungroup(TYPE) %>% group_by(ARRONDISSEMENT) %>% mutate( total = sum(nb), Prop = (nb/total)*100, Prop = round(Prop,1)) %>% arrange(ARRONDISSEMENT, desc(Prop))
Il est facilement possible de passer d'un mode long (long) à un mode large (wide) et vice-versa grâce aux fonctions :
spread
passage lignes vers colonnesgather
passage colonnes vers lignesCes deux fonctions sont particuliérement utiles pour restructurer des jeux de données pour par exemple faire des datavisualisation ou des représentations cartographiques.
spread
Je veux produire un tableau avec en lignes les arrondissements et en colonne le nombre de signalements par années
Etape 1. produire un tableau récapitulatif en utilisant le group by
SignalementArdtAnnee <- DMR %>% group_by(Code = CODE_POSTAL, Annee = ANNEE.DECLARATION) %>% summarise(nb= n())
Etape 2. Passage de long à large
Signalementcolonnes <- SignalementArdtAnnee %>% spread(key=Annee, value = nb)
Autre exemple, je veux produire un tableau avec en lignes l'année et en colonne les arrondissements
Signalementcolonnesbis <- SignalementArdtAnnee %>% spread(key=Code, value = nb)
Dernier exemple, je veux produire un tableau récapitulatif avec en ligne l'année et en colonne le nombre de signalement par type
Etape 1. Produire le premier tableau récapitulatif
DMRtype<- DMR %>% group_by(ANNEE = ANNEE.DECLARATION, TYPE) %>% summarise(nb= n())
Etape 2. Passage de long à large
Signalementcolonnestype <- DMRtype %>% spread(key=TYPE, value = nb)
gather
En partant d'un tableau structuré en colonnes (le dernier produit), je veux produire un tableau où je vais retrouver les nombre de signalements par années et par types structurés en lignes (pour une visualisation spatio-temporelle par exemple)
DMRenlignes <- Signalementcolonnestype %>% gather(key="Type", value = "Nb", `Activités commerciales et professionnelles`:`Voirie et espace public`)
La jointure attributaire est une fonction très pratique pour venir enrichir des tableau avec des données issus d'autres tableaux.
Nous volons ici venir enrichir la tableau avec les données INSEE du nombre de signalements par arrondissement en 2019
La première fonction merge
permet de faire des jointures "simples" et "brutes"
merge(DatasetA, DatasetB, by = "id")
dans le cas où le nom des champs de jointure est le mêmemerge(DatasetA, DatasetB, by.x = "id", by.y = "ID")
dans le cas où les noms des champs de jointure sont différentsEtape 1. Produire un tableau récapitulatif avec le nombre de signalements en 2019 pour chaque arrondissements
DMRArdt2019 <- DMR %>% filter(ANNEE.DECLARATION == 2019) %>% group_by(Ardt = CODE_POSTAL) %>% summarise(nb=n())
Importer le jeu de données avec les données de l'INSEE
Les noms des 2 champs de jointure sont différentes >il va falloir créer dans l'un des jeux de données un champ de jointure avec un nom similaire à celui de l'autre
Créer dans le tableau récapitulatid une nouvelle colonne CODGEO
DMRArdt2019<- DMRArdt2019 %>% mutate(CODGEO=Ardt)
Etape2. Faire la jointure
DMRINSEE <- merge(ParisINSEE, DMRArdt2019, by = "CODGEO")
Méthode bien plus simple !
DMRINSEEbis <- merge(ParisINSEE, DMRArdt2019, by.x = "CODGEO", by.y = "Ardt")
Le Graal !
DMRINSEEparfait <- merge(ParisINSEE, DMRArdt2019, by.x = "CodeArdt", by.y = "Ardt") [,c("CodeArdt","Pop2016", "nb")]
lubridate
Le package lubridate
permet de travailler avec des dates et des heures pour reformter des timestamps extraires des noms de journée ou des numéros de semaine par exemple.
https://evoldyn.gitlab.io/evomics-2018/ref-sheets/R_lubridate.pdf
install.packages("lubridate")
library(lubridate)
Première étape pour manipuler des dates, reformater la colonne DATEDECL en format Date
DMR$DATEDECL <- as.Date(DMR$DATEDECL)
Nous voulons extraire le jour de la semaine de chaque signalement en utilisant la fonction wday
DMR <- DMR %>% mutate(journee= wday(DATEDECL,label= TRUE, abbr= FALSE))
traces$timestamp <- as_datetime(traces$timestamp)
traces <- traces %>% mutate(UNIX= as.numeric(as.POSIXct(timestamp)))
Nous volons enfin exporter un ou plusieurs dataframe pour une utilisation dans un logiciels de visualisation de données ou de SIG.
Il faut utiliser la fonction write.csv
en spécifiant :
J'exporte en csv le dataframe Signalement2019
dans le dossier "documents"
write.csv(Signalement2019, 'Signalement2019.csv')
J'exporte en csv le dataframe Signalement2019
dans un dossier dédié sur mon bureau
write.csv(Signalement2019, 'C:/Users/Xo/Desktop/DMR/Signalement2019.csv')
Séance sur la visualisation de données avec ggplot2
Séance sur la manipulation de données de billétique avec Tidyverse
ggplot2
lubridate
et sf
Séance sur la manipulation des données DVF avec Tidyverse
et sf
Séance sur la manipulation des données spatiales, géotraitements avec Tidyverse
et sf