Cour Datatable1
Cour Datatable1
Pour des tables de données de taille petite et moyenne (inférieure à 1 Go ou moins d’un
million d’observations), il est recommandé d’utiliser
les packages tibble, dplyr et tidyr qui sont présentés dans la fiche Manipuler des
données avec le tidyverse;
Pour des tables de données de grande taille (plus de 1 Go ou plus d’un million
d’observations), il est recommandé d’utiliser soit le package [Link] qui fait l’objet
de la présente fiche, soit les packages arrow et duckdb présentés dans les
fiches Manipuler des données avec arrow et Manipuler des données avec duckdb.
Note
Certains exemples de cette fiche utilisent les données disponibles dans
le package doremifasolData ; vous ne pourrez reproduire ces exemples que si ce package est
installé sur la machine sur laquelle vous travaillez. Si vous ne savez pas si ce package est déjà
installé, consultez la fiche Comment utiliser la documentation utilitR.
Il n’est pas nécessaire d’utiliser le préfixe DT$ pour se référer aux variables à l’intérieur
de [...] ;
Le code est très concis, ce qui aide à le rendre lisible.
Note
Cette syntaxe compacte est aussi un des atouts fondamentaux de [Link] pour sa
rapidité : [Link] ne manipule que les colonnes mentionnées dans l’opérateur [...], ce qui
réduit le temps de traitement des données.
Voici un exemple simple. A partir des données générées ci-dessus, on veut calculer la moyenne
de y par groupe défini par x, uniquement sur les observations pour lesquelles x est supérieur à 3.
Voici comment on peut réaliser cette opération avec Base R, dplyr et [Link]. Vous
pouvez juger vous-même de la concision du code.
aggregate(
dt[dt[["x"]] > 3]$y,
Base R by = list(dt[dt[["x"]] > 3]$z),
FUN = sum)
dt %>%
dplyr::filter(x > 3) %>%
dplyr dplyr::group_by(z) %>%
dplyr::summarise(sum(y))
Tip
L’utilisation du package [Link] peut paraître plus déroutante pour les débutants que
l’utilisation de dplyr. Toutefois, l’apprentissage de [Link] est particulièrement
recommandé si vous avez l’intention d’utiliser R avec des données volumineuses
car [Link] est beaucoup plus rapide et puissant que dplyr. Des remarques et conseils sont
présents dans cette fiche pour vous aider à vous familiariser avec la syntaxe de [Link].
Il est facile d’enchaîner des opérations avec [Link] : il suffit d’accoler les
opérateurs []. Votre code [Link] prendra alors la forme suivante : dt[opération 1]
[opération 2][opération 3][...]. Voici un exemple simple, dans lequel on calcule la
moyenne d’une variable par groupe, puis on trie la table.
# En chaînant
ans <- dt[ , .(moyenne = mean(y, [Link] = TRUE)), by = x][order(moyenne)]
ans
x moyenne
1: A 5.5
2: B 6.5
3: C 7.5
Le problème avec l’enchaînement d’opérations multiples est qu’on aboutit rapidement à des
lignes de codes extrêmement longues. C’est pourquoi il est préférable de revenir régulièrement
à la ligne, de façon à garder un code qui reste lisible. Il y a évidemment plusieurs façons
d’organiser le code. La seule obligation est que le crochet qui commence une nouvelle opération
doit être accolé au crochet qui termine l’opération précédente (...][...). Voici deux
organisations possibles, à vous de choisir celle qui vous paraît la plus claire et la plus adaptée à
votre travail.
La première organisation enchaîne toutes les opérations en une seule fois :
resultat <-
dt[i = ...,
j = ...,
by = ...
][i = ...,
j = ...,
by = ...
]
Comme indiqué précédemment, i, j et by ne sont pas forcément présents dans toutes les étapes.
Voici ce que cette organisation du code donne sur un exemple légèrement plus complexe que le
précédent :
dt[ , total := y + z]
resultat <- dt[ ,
.(moyenne = mean(total, [Link] = TRUE)),
by = x
][order(moyenne)]
resultat
x moyenne
1: A 10
2: B 11
3: C 12
Partir de dt, conserver uniquement les Partir de dt, créer une nouvelle
observations pour lesquelles x > 3, et variable newvar qui vaut 1 pour les
Signification
créer une nouvelle variable newvar qui observations pour lesquelles x >
vaut 1 partout 3 et NA ailleurs
Il y a principalement deux méthodes pour mettre des données sous forme d’un [Link] :
la fonction fread() importe un fichier plat comme les .csv (voir la fiche Importer des
fichiers plats (.csv, .tsv, .txt) ;
Les fonctions setDT() et [Link]() convertissent
un [Link] en [Link].
Dans la suite de cette section, on va illustrer les opérations de base en [Link] avec la base
permanente des équipements (table bpe_ens_2018), qu’on transforme en [Link].
# Charger la base permanente des équipements
bpe_ens_2018 <- doremifasolData::bpe_ens_2018
# Convertir ce [Link] en [Link]
bpe_ens_2018_dt <- [Link](bpe_ens_2018)
On peut sélectionner des lignes dans un [Link] avec dt[i]. Voici un exemple de code
qui sélectionne les magasins de chaussures (TYPEQU == "B304") dans le premier
arrondissement de Paris (DEPCOM == "75101") dans la table bpe_ens_2018_dt :
selection <- bpe_ens_2018_dt[DEPCOM == "75101" & TYPEQU == "B304"]
Note
Voici une remarque très importante sur le fonctionnement de [Link] : lorsqu’on souhaite
conserver toutes les lignes d’un [Link], il faut laisser vide l’emplacement pour i, sans
oublier la virgule. Par exemple, pour connaître le nombre de lignes de iris_dt, on
écrit : iris_dt[ , .N]. Notez bien l’emplacement vide et la virgule après [.
Note
La seconde méthode peut vous sembler inutilement complexe. C’est vrai dans l’exemple donné
ci-dessus, mais les fonctions .SD et .SDcols s’avèrent très puissantes dans un grand nombre de
situations (notamment quand on veut programmer des fonctions qui font appel à [Link]).
On peut trier un [Link] avec la fonction order(). Le code suivant trie la BPE selon le
code commune et le type d’équipement.
bpe_ens_2018_dt[order(DEPCOM, TYPEQU)]
REG DEP DEPCOM DCIRIS AN TYPEQU NB_EQUIP
1: 84 01 01001 01001 2018 A401 2
2: 84 01 01001 01001 2018 A404 4
3: 84 01 01001 01001 2018 A504 1
4: 84 01 01001 01001 2018 A507 1
---
1035561: 06 976 97617 97617 2018 F113 4
1035562: 06 976 97617 97617 2018 F114 1
1035563: 06 976 97617 97617 2018 F120 1
1035564: 06 976 97617 97617 2018 F121 3
Il suffit d’ajouter un signe - devant une variable pour trier par ordre décroissant. Le code suivant
trie la BPE par code commune croissant et type d’équipement décroissant.
bpe_ens_2018_dt[order(DEPCOM, -TYPEQU)]
[Link] Calculer des statistiques
La méthode pour sélectionner des colonnes est également valable pour calculer des
statistiques, car [Link] accepte les expressions dans j. Le code suivant calcule le
nombre total d’équipements dans la BPE, sum(NB_EQUIP, [Link] = TRUE) :
bpe_ens_2018_dt[ , .(sum(NB_EQUIP, [Link] = TRUE))]
V1
1: 2504782
Il est possible de calculer plusieurs statistiques à la fois, et de donner des noms aux variables ; il
suffit de séparer les formules par une virgule. Le code suivant calcule le nombre total
d’équipements dans la BPE sum(NB_EQUIP, [Link] = TRUE), et le nombre total de
boulangeries sum(NB_EQUIP * (TYPEQU == "B203"), [Link] = TRUE).
bpe_ens_2018_dt[ ,
.(NB_EQUIP_TOT = sum(NB_EQUIP, [Link] = TRUE),
NB_BOULANG_TOT = sum(NB_EQUIP * (TYPEQU == "B203"), [Link] =
TRUE))]
NB_EQUIP_TOT NB_BOULANG_TOT
1: 2504782 48568
On peut évidemment combiner i et j pour calculer des statistiques sur un sous-ensemble
d’observations. Dans l’exemple suivant, on sélectionne les boulangeries avec i, (TYPEQU ==
"B203"), et on calcule le nombre total d’équipements avec j, sum(NB_EQUIP, [Link] =
TRUE).
bpe_ens_2018_dt[TYPEQU == "B203", .(NB_BOULANG_TOT = sum(NB_EQUIP, [Link] =
TRUE))]
NB_BOULANG_TOT
1: 48568
Toutes les opérations précédentes peuvent être réalisées par groupe. Il suffit d’ajouter le nom
des variables de groupe dans by (c’est l’équivalent du group_by() du package dplyr).
Lorsqu’il y a plusieurs variables de groupe, on peut écrire l’argument by de deux façons :
Le code suivant groupe les données de la BPE par département, by = .(DEP), puis calcule le
nombre total d’équipements, sum(NB_EQUIP, [Link] = TRUE) et le nombre total de
boulangeries, sum(NB_EQUIP * (TYPEQU == "B203"), [Link] = TRUE).
bpe_ens_2018_dt[ ,
.(NB_EQUIP_TOT = sum(NB_EQUIP, [Link] = TRUE),
NB_BOULANG_TOT = sum(NB_EQUIP * (TYPEQU == "B203"), [Link] =
TRUE)),
by = .(DEP)]
DEP NB_EQUIP_TOT NB_BOULANG_TOT
1: 01 21394 401
2: 02 15534 339
3: 03 12216 299
4: 04 8901 185
---
98: 972 19068 370
99: 973 7852 98
100: 974 30767 646
101: 976 7353 101
Note
L’argument by fonctionne également avec l’opérateur :=. Vous pouvez en apprendre davantage
sur l’usage de cet opérateur dans la partie La fonction d’assignation par référence (ou :=).
Pour joindre des données, [Link] propose une fonction merge() plus rapide que la
fonction de base. La syntaxe générale est z <- merge(x, y, [options]). Voici une liste des
principales options (les autres options sont consultables avec ?[Link]::merge) :
Option Signification
Joindre sur la variable var_jointure (présente dans x et
by = var_jointure
dans y)
by.x = "identx", by.y =
"identy"
Joindre sur la condition identx == identy
all.x = TRUE Left join (garder toutes les lignes de x)
all.y = TRUE Right join (garder toutes les lignes de y)
Option Signification
all = TRUE Full join (garder toutes les lignes de x et de y)
Enfin, il est possible de réaliser des jointures plus sophistiquées avec [Link]. Ces méthodes
sont présentées dans la vignette sur le sujet.
L’indexation est une fonctionnalité très puissante pour accélérer les opérations sur les
lignes (filtres, jointures, etc.) en [Link]. Pour indexer une table il faut déclarer les
variables faisant office de clé (appelées key). C’est possible de la manière
suivante : setkey(dt, a) ou setkeyv(dt, "a"). Le [Link] sera réordonné en fonction
de cette variable et l’algorithme de recherche sur les lignes sera ainsi beaucoup plus efficace.
Lorsqu’il y a plusieurs variables-clé, on écrit setkey(dt, a, b) ou setkeyv(dt,
c("a","b")).
Pour savoir si un [Link] est déjà indexé, on peut exécuter la commande key(dt) qui
renvoie le nom des clés s’il y en a, et NULL sinon.
Tip
L’exécution de la fonction [Link]::setkey() peut prendre un peu de temps (parfois
quelques minutes sur une table de plus de 10 millions de lignes), car [Link] trie toute la
table en fonction des variables-clé. Toutefois, c’est une étape vraiment utile car elle accélère
considérablement les opérations ultérieures sur les lignes. Il est vivement recommandé de
l’utiliser si une ou plusieurs variables vont régulièrement servir à filtrer ou combiner des données.
Pour aller plus loin, voir cette vignette.
Le package [Link] permet de réorganiser facilement une table de données avec les
fonctions dcast() et melt(). La fonction melt() réorganise les données dans un format long.
La fonction dcast() réorganise les données dans un format wide.
melt() dcast()
Réorganiser les données dans un format long Réorganise les données dans un format wide
La fonction melt() réorganise les donnée dans un format long. Elle prend les arguments suivants
:
data : les données ;
[Link] : les variables qui identifient les lignes de table d’arrivée ; elles restent
inchangées lors de l’utilisation de melt() ;
[Link] : les variables qui sont transposées ;
[Link] : le nom de la nouvelle colonne qui contient le nom des variables
transposées ;
[Link] : le nom de la nouvelle colonne qui contient la valeur des variables
transposées.
Pour illustrer l’usage de cette fonction, nous allons utiliser les données du répertoire Filosofi
2016 agrégées au niveau des EPCI (table filosofi_epci_2016), et disponibles dans le
package doremifasolData. On convertit cette table en [Link] et on conserve uniquement
certaines variables.
# Charger la table de Filosofi
filosofi_epci_2016 <- doremifasolData::filosofi_epci_2016
# Convertir la table en [Link]
filosofi_epci_2016_dt <- [Link](filosofi_epci_2016)
# Sélectionner des colonnes
filosofi_epci_2016_dt <-
filosofi_epci_2016_dt[, .(CODGEO, TP6016, TP60AGE116, TP60AGE216,
TP60AGE316, TP60AGE416, TP60AGE516, TP60AGE616)]
Nous allons restructurer cette table pour obtenir une nouvelle table, avec une observation par
EPCI et par tranche d’âge. Voici le code qui permet d’obtenir cette table : on indique
dans [Link] le nom des colonnes qui seront transposées, le nom des colonnes
transposées sera indiqué dans la nouvelle colonne “tranche_age” ([Link] =
"tranche_age") et les valeurs des colonnes transposées seront stockées dans la colonne
“taux_pauvrete” ([Link] = "taux_pauvrete").
donnees_pauvrete_long <-
melt(data = filosofi_epci_2016_dt,
[Link] = c("CODGEO"),
[Link] = c("TP6016", "TP60AGE116", "TP60AGE216",
"TP60AGE316", "TP60AGE416", "TP60AGE516",
"TP60AGE616"),
[Link] = "tranche_age",
[Link] = "taux_pauvrete"
)
donnees_pauvrete_long
CODGEO tranche_age taux_pauvrete
1: 200000172 TP6016 8.8
2: 200000438 TP6016 8.0
3: 200000545 TP6016 23.7
4: 200000628 TP6016 20.1
---
8705: 249740085 TP60AGE616 41.5
8706: 249740093 TP60AGE616 43.4
8707: 249740101 TP60AGE616 39.8
8708: 249740119 TP60AGE616 31.7
Tip
Il est recommandé de travailler avec des données en format long plutôt qu’en format wide,
notamment lorsque vous voulez faire des graphiques. En effet, le package de visualisation
graphique ggplot2 est optimisé pour manipuler des données en format long (voir la fiche [Faire
des graphiques avec ggplot2]). Ce conseil est particulièrement important si vous voulez
représenter un graphique avec des groupes : il est préférable que les groupes soient empilés
(format long) plutôt que juxtaposés (format wide), car le code est plus rapide et facile à écrire.
La fonction dcast() réorganise les donnée dans un format large. Elle prend les arguments
suivants :
Dans l’exemple qui suit, on réorganise la table bpe_ens_2018_dt de façon à obtenir une table
qui contient une ligne par type d’équipement et une colonne par région (TYPEQU ~ REG). Ces
colonnes vont contenir la somme ([Link] = sum) du nombre d’équipements
([Link] = "NB_EQUIP").
bpe_ens_2018_wide <- dcast(bpe_ens_2018_dt,
TYPEQU ~ REG,
[Link] = "NB_EQUIP",
[Link] = sum)
head(bpe_ens_2018_wide)
TYPEQU 01 02 03 04 06 11 24 27 28 32 44 52 53 75 76 84 93 94
1: A101 2 2 1 7 0 191 28 23 54 127 80 15 20 66 55 69 34 3
2: A104 20 21 16 28 5 91 153 230 183 214 319 173 157 407 406 423 179 39
3: A105 1 1 1 1 1 2 2 2 2 2 4 1 1 5 3 4 1 1
4: A106 2 1 2 2 1 10 7 12 10 17 17 8 8 19 19 21 11 2
5: A107 2 1 1 4 1 60 9 19 15 26 30 11 12 28 26 34 23 2
6: A108 2 1 1 2 1 19 9 13 13 25 21 8 10 21 20 28 14 2
Il est possible d’utiliser dcast() avec plusieurs variables à transposer et plusieurs fonctions
pour transposer. Dans l’exemple qui suit, on obtient une ligne par type d’équipement, et une
colonne par région et par fonction d’agrégation (mean et sum).
bpe_ens_2018_wide2 <- dcast(bpe_ens_2018_dt,
TYPEQU ~ REG,
[Link] = "NB_EQUIP",
[Link] = list(sum, mean))
bpe_ens_2018_wide2
TYPEQU NB_EQUIP_sum_01 NB_EQUIP_sum_02 NB_EQUIP_sum_03 NB_EQUIP_sum_04
1: A101 2 2 1 7
2: A104 20 21 16 28
3: A105 1 1 1 1
4: A106 2 1 2 2
---
183: G101 105 79 48 136
184: G102 49 49 29 112
185: G103 0 0 0 0
186: G104 110 104 46 98
NB_EQUIP_sum_06 NB_EQUIP_sum_11 NB_EQUIP_sum_24 NB_EQUIP_sum_27
1: 0 191 28 23
2: 5 91 153 230
3: 1 2 2 2
4: 1 10 7 12
---
183: 20 3351 213 256
184: 11 2478 670 890
185: 0 96 238 330
186: 6 2993 293 339
NB_EQUIP_sum_28 NB_EQUIP_sum_32 NB_EQUIP_sum_44 NB_EQUIP_sum_52
1: 54 127 80 15
2: 183 214 319 173
3: 2 2 4 1
4: 10 17 17 8
---
183: 272 495 593 376
184: 845 698 1318 762
185: 378 521 368 646
186: 401 354 533 330
NB_EQUIP_sum_53 NB_EQUIP_sum_75 NB_EQUIP_sum_76 NB_EQUIP_sum_84
1: 20 66 55 69
2: 157 407 406 423
3: 1 5 3 4
4: 8 19 19 21
---
183: 345 720 786 1166
184: 943 1908 1982 2797
185: 751 1408 1437 1265
186: 374 926 932 1099
NB_EQUIP_sum_93 NB_EQUIP_sum_94 NB_EQUIP_mean_01 NB_EQUIP_mean_02
1: 34 3 1.000000 1.000000
2: 179 39 1.000000 1.000000
3: 1 1 1.000000 1.000000
4: 11 2 1.000000 1.000000
---
183: 1016 120 2.282609 1.975000
184: 2111 438 2.450000 2.130435
185: 718 187 NaN NaN
186: 876 182 1.718750 1.575758
NB_EQUIP_mean_03 NB_EQUIP_mean_04 NB_EQUIP_mean_06 NB_EQUIP_mean_11
1: 1.000000 1.000000 NaN 1.091429
2: 1.000000 1.000000 1.000000 1.000000
3: 1.000000 1.000000 1.000000 1.000000
4: 1.000000 1.000000 1.000000 1.000000
---
183: 1.714286 1.837838 5.000000 2.080074
184: 1.318182 2.036364 1.833333 2.250681
185: NaN NaN NaN 1.103448
186: 1.533333 1.400000 1.500000 1.780488
NB_EQUIP_mean_24 NB_EQUIP_mean_27 NB_EQUIP_mean_28 NB_EQUIP_mean_32
1: 1.037037 1.000000 1.018868 1.058333
2: 1.000000 1.004367 1.000000 1.014218
3: 1.000000 1.000000 1.000000 1.000000
4: 1.000000 1.000000 1.000000 1.000000
---
183: 1.601504 1.422222 1.511111 1.633663
184: 1.763158 1.666667 1.978923 1.681928
185: 1.048458 1.103679 1.330986 1.527859
186: 1.140078 1.232727 1.297735 1.156863
NB_EQUIP_mean_44 NB_EQUIP_mean_52 NB_EQUIP_mean_53 NB_EQUIP_mean_75
1: 1.025641 1.000000 1.000000 1.157895
2: 1.009494 1.005814 1.000000 1.007426
3: 1.000000 1.000000 1.000000 1.000000
4: 1.000000 1.000000 1.000000 1.000000
---
183: 1.694286 1.748837 1.674757 1.578947
184: 1.734211 1.836145 2.063457 1.927273
185: 1.153605 2.044304 1.891688 1.733990
186: 1.230947 1.274131 1.307692 1.293296
NB_EQUIP_mean_76 NB_EQUIP_mean_84 NB_EQUIP_mean_93 NB_EQUIP_mean_94
1: 1.145833 1.029851 1.000000 1.000000
2: 1.015000 1.011962 1.028736 1.083333
3: 1.000000 1.000000 1.000000 1.000000
4: 1.000000 1.000000 1.000000 1.000000
---
183: 1.526214 1.707174 1.785589 2.000000
184: 2.045408 2.109351 2.507126 3.369231
185: 1.600223 1.408686 1.681499 2.101124
186: 1.226316 1.345165 1.364486 1.857143
Tip
La fonction dcast() crée une colonne par valeur des variables utilisées dans la partie
droite de la formule. Il faut donc faire attention à ce que ces variables aient un
nombre limité de valeurs, pour ne pas obtenir une table extrêmement large. On peut
éventuellement discrétiser les variables continues, ou regrouper les modalités avant
d’utiliser dcast().
On peut obtenir des noms de colonnes peu significatifs lorsqu’on utilise dcast() avec
une fonction d’agrégation. Il est conseillé de modifier légèrement la partie droite de la
formule pour obtenir des noms plus significatifs. Voici un exemple où on ajoute le
préfixe resultat_region :
bpe_ens_2018_wide2 <- dcast(bpe_ens_2018_dt,
TYPEQU ~ paste0("resultat_region",REG),
[Link] = "NB_EQUIP",
[Link] = sum)
head(bpe_ens_2018_wide2)
TYPEQU resultat_region01 resultat_region02 resultat_region03
1: A101 2 2 1
2: A104 20 21 16
3: A105 1 1 1
4: A106 2 1 2
5: A107 2 1 1
6: A108 2 1 1
resultat_region04 resultat_region06 resultat_region11
resultat_region24
1: 7 0 191
28
2: 28 5 91
153
3: 1 1 2
2
4: 2 1 10
7
5: 4 1 60
9
6: 2 1 19
9
resultat_region27 resultat_region28 resultat_region32
resultat_region44
1: 23 54 127
80
2: 230 183 214
319
3: 2 2 2
4
4: 12 10 17
17
5: 19 15 26
30
6: 13 13 25
21
resultat_region52 resultat_region53 resultat_region75
resultat_region76
1: 15 20 66
55
2: 173 157 407
406
3: 1 1 5
3
4: 8 8 19
19
5: 11 12 28
26
6: 8 10 21
20
resultat_region84 resultat_region93 resultat_region94
1: 69 34 3
2: 423 179 39
3: 4 1 1
4: 21 11 2
5: 34 23 2
6: 28 14 2
Note
Il est conseillé de bien réfléchir avant de restructurer des données en format wide, et de ne
le faire que lorsque cela paraît indispensable. En effet, s’il est tentant de restructurer les
données sous format wide car ce format peut paraître plus intuitif, il est généralement plus simple
et plus rigoureux de traiter les données en format long. Ceci dit, il existe des situations dans
lesquelles il est indiqué de restructurer les données en format wide. Voici deux exemples :
produire un tableau synthétique de résultats, prêt à être diffusé, avec quelques colonnes
donnant des indicateurs par catégorie (exemple : la
table filosofi_epci_2016 du package doremifasolData) ;
produire une table avec une colonne par année, de façon à calculer facilement un taux
d’évolution entre deux dates.
Voici comment créer plusieurs variables à la fois avec :=, en utilisant une notation vectorielle :
bpe_ens_2018_dt[ , c("nouvelle_colonne1", "nouvelle_colonne2") :=
list(NB_EQUIP * 2, NB_EQUIP + 3)]
On peut faire exactement la même chose en utilisant la notation `:=`(). Voici le même exemple
écrit avec `:=`().
bpe_ens_2018_dt[ , `:=`(nouvelle_colonne1 = NB_EQUIP * 2,
nouvelle_colonne2 = NB_EQUIP + 3)]
Note
Si vous utilisez la notation`:=`(), alors il faut utiliser uniquement = à l’intérieur des parenthèses
pour créer ou modifier des variables, et non :=. Par exemple,
dt[ , `:=`(var1 = "Hello", var2 = "world")]
On peut facilement supprimer une colonne en lui assignant la valeur NULL (c’est hyper rapide !).
Voici un exemple :
bpe_ens_2018_dt[ , NB_EQUIP := NULL]
La fonction := peut être utilisée pour modifier une colonne pour certaines lignes seulement, en
fonction d’une condition logique. C’est beaucoup plus efficace qu’un
terme dplyr::if_else() ou dplyr::case_when(). Imaginons qu’on veuille créer une
colonne EQUIP_HORS_CHAUSS égale au nombre d’équipements (NB_EQUIP) sauf pour les lignes
correspondantes à des magasins de chaussures (TYPEQU == "B304") où elle vaut NA. Dans ce
cas, le code dplyr serait :
bpe_ens_2018 %>%
dplyr::mutate(NB_EQUIP_HORS_CHAUSS = dplyr::case_when(
TYPEQU == "B304" ~ NA_real_,
TRUE ~ NB_EQUIP)
)
Deux alternatives existent en [Link] nécessitant toutes deux beaucoup moins de mémoire
vive :
bpe_ens_2018_dt[ , NB_EQUIP_HORS_CHAUSS := NB_EQUIP
][TYPEQU == "B304", NB_EQUIP_HORS_CHAUSS := NA_real_]
Lorsque l’on crée plusieurs variables avec la fonction :=, elles sont créées en même
temps. On ne peut donc pas faire appel dans une formule à une variable qu’on crée
dans le même appel à la fonction :=. Par exemple, le code suivant ne fonctionne pas :
bpe_ens_2018_dt[ , `:=`(nouvelle_colonne1 = NB_EQUIP * 2,
nouvelle_colonne2 = nouvelle_colonne1 + 3)]
Un mauvais usage de la fonction := peut vous amener à écraser par erreur vos
données. En effet, si vous exécuter par erreur la commande dt[ ,
ma_variable_importante := 0], vous écrasez la
variable ma_variable_importante. Vous devez alors recharger vos données… Il faut
donc bien réfléchir à ce que vous voulez faire avant de remplacer ou modifier une variable
existante avec la fonction :=. Si vous modifiez un [Link] dans une fonction, un
filet de sécurité consiste à d’abord copier le [Link] initial et ainsi faire les
modifications sur le nouvel objet, de la manière suivante :
dt_copy <- [Link]::copy(dt)
dt_copy[, ma_variable_importante := "Nouvelle valeur"]
Le mot clé .SD (Subset of Data) permet d’appliquer la même opération sur plusieurs colonnes.
Les colonnes auxquelles l’opération s’applique sont contrôlées par l’argument .SDcols (par
défaut, toutes les colonnes sont traitées). Le mot clé .SD est régulièrement utilisé en conjonction
avec la fonction lapply. Cette syntaxe, très puissante, permet également d’avoir des codes
assez compacts, ce qui les rend plus lisible.
Un usage classique de ce duo lapply+.SD consiste à écrire des fonctions de statistiques
descriptives. Par exemple, imaginons qu’on souhaite calculer la moyenne, l’écart-type et les
quantiles (P25, P50 et P75) de nombreuses colonnes. On peut alors définir la fonction suivante :
mes_statistiques <-
function(x) return(c(mean(x, [Link] = TRUE),
sd(x, [Link] = TRUE),
quantile(x, probs = c(.25,.5,.75), [Link] = TRUE)))
Voici comment on peut appliquer cette fonction aux colonnes NBMENFISC16 (nombre de
ménages fiscaux) et NBPERSMENFISC16 (nombre de personnes dans les ménages fiscaux) de la
table filosofi_com_2016_dt :
data_agregee <-
filosofi_com_2016_dt[ ,
lapply(.SD, mes_statistiques),
.SDcols = c("NBMENFISC16", "NBPERSMENFISC16")]
data_agregee[, 'stat' := c("moyenne","écart-type","P25","P50","P75")]
data_agregee
NBMENFISC16 NBPERSMENFISC16 stat
1: 916.3269 2097.18 moyenne
2: 7382.4628 15245.60 écart-type
3: 105.0000 250.50 P25
4: 218.0000 527.00 P50
5: 526.0000 1278.25 P75
Il est également très simple d’effectuer des calculs par groupe avec la
méthode lapply+.SD. On peut par facilement adapter le code précédent pour calculer des
statistiques descriptives par département (variable departement).
data_agregee <-
filosofi_com_2016_dt[ ,
lapply(.SD, mes_statistiques),
by = departement,
.SDcols = c("NBMENFISC16", "NBPERSMENFISC16")]
data_agregee[, 'stat' := c("moyenne","écart-type","P25","P50","P75"), by =
departement]
data_agregee
Il est très facile d’écrire avec [Link] des fonctions génériques faisant appel à des noms
de variables en arguments. Pour déclarer à [Link] qu’un nom fait référence à une
colonne, la manière la plus simple est d’utiliser la fonction get. Dans l’exemple suivant, on
définit la fonction creation_var qui crée dans la table data une nouvelle variable (dont le nom
est l’argument nouveau_nom) égale à une autre variable incrémentée (dont le nom est
l’argument nom_variable) de 1. L’utilisation de la fonction get permet d’indiquer
à [Link] que la chaîne de caractères nom_variable désigne une colonne de la table data.
creation_var <- function(data, nom_variable, nouveau_nom){
data[, c(nouveau_nom) := get(nom_variable) + 1]
}
head(creation_var(filosofi_com_2016_dt,
nom_variable = "NBMENFISC16",
nouveau_nom = "nouvelle_variable"), 2)
departement CODGEO NBMENFISC16 NBPERSMENFISC16 nouvelle_variable
1: 01 01001 313 795.5 314
2: 01 01002 101 248.0 102
c(nouveau_nom) permet de s’assurer que [Link] crée une nouvelle colonne dont le nom
est défini en argument (et qui ne s’appelle donc pas nouveau_nom).
Note
La version 1.14.1 de [Link] (encore en développement) apporte une syntaxe améliorée dans
ce cas et considère l’utilisation de get comme désuète. Le [...] admet un nouvel
argument env à qui on donne tous les remplacements que l’on souhaite. L’exemple devient :
creation_var <- function(data, nom_variable, nouveau_nom){
data[, nouveau_nom := nom_variable + 1,
env = list(nouveau_nom = nouveau_nom, nom_variable = nom_variable)]
}
head(creation_var(filosofi_com_2016_dt,
nom_variable = "NBMENFISC16",
nouveau_nom = "nouvelle_variable"), 2)
L’avantage c’est qu’on peut effectuer de tels remplacements dans i (dimension ligne) et qu’on
peut même remplacer des fonctions :
creation_var <- function(data, nom_variable, nouveau_nom, fonction){
data[, nouveau_nom := fonction(nom_variable),
env = list(nouveau_nom = nouveau_nom, nom_variable = nom_variable,
fonction= fonction)]
head(creation_var(filosofi_com_2016_dt,
nom_variable = "NBMENFISC16",
nouveau_nom = "nouvelle_variable",
fonction = "sqrt"), 2)
}
Tip
Lorsqu’on définit des fonctions pour effectuer des traitements génériques, une précaution est
nécessaire pour ne pas modifier les données en entrée de la fonction si l’opérateur := est utilisée.
Il est recommandé dans ce cas de créer une copie du dataframe en entrée
([Link]::copy(df)) et d’effectuer les traitements sur cette copie.
Code source