Principe de dplyr::across
La fonction across est apparue dans la version 1.0 de {dplyr}, en juin 2020. Son but est de répéter une action sur plusieurs colonnes. On la retrouve principalement dans les fonctions mutate et summarise, mais elle peut aussi survenir avec une syntaxe légèrement adaptée dans group_by et arrange.
Usage courant dans mutate et summarise
La fonction across est une fonction-boucle au même titre que base::lapply ou que la famille des fonctions map du package {purrr}. C’est à dire qu’elle prend principalement deux arguments : le premier indique les éléments sur lesquels répéter l’action (ici les colonnes du data.frame), le second est le nom ou le code d’une fonction qui est l’action à répéter.
across(qui, quoi)
Nous allons compliquer les exemples petit à petit mais pour le moment, nous allons supposer que le “quoi” est une fonction qui ne prend d’autre argument que la colonne sur laquelle opérer : dans ce cas il suffit de nommer ladite fonction, sans parenthèses à sa suite.
library(dplyr)
mtcars %>%
mutate(across(c(am, vs, cyl),
as.factor)) %>%
glimpse()
Rows: 32
Columns: 11
$ mpg <dbl> 21.0, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8, 19.2, 17.8,…
$ cyl <fct> 6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 8,…
$ disp <dbl> 160.0, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146.7, 140.8, 16…
$ hp <dbl> 110, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, 180, 180, 180…
$ drat <dbl> 3.90, 3.90, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92,…
$ wt <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.190, 3.150, 3.…
$ qsec <dbl> 16.46, 17.02, 18.61, 19.44, 17.02, 20.22, 15.84, 20.00, 22.90, 18…
$ vs <fct> 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,…
$ am <fct> 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,…
$ gear <dbl> 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3,…
$ carb <dbl> 4, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2,…
Comme on le voit, les 3 colonnes indiquées dans le “qui” ont été converties en factors.
Si les données ne contiennent pas de valeurs manquantes on peut également utiliser across dans un summarise pour obtenir la même statistique sur toutes les colonnes.
mtcars %>%
summarise(across(everything(),
median))
mpg cyl disp hp drat wt qsec vs am gear carb
1 19.2 6 196.3 123 3.695 3.325 17.71 0 0 4 2
Ici, appliquer la fonction statistique sur toutes les colonnes est probablement assez brutal. On pourrait être plus délicats et se limiter aux colonnes numériques (en l’occurrence dans mtcars elles le sont toutes).
mtcars %>%
summarise(across(where(is.numeric),
median))
mpg cyl disp hp drat wt qsec vs am gear carb
1 19.2 6 196.3 123 3.695 3.325 17.71 0 0 4 2
Avec des arguments ou des calculs complexes comme fonction
La syntaxe précédente ne laisse la place à aucune option pour la fonction du “quoi”, autre que le nom de la colonne à traiter du “qui”. Si on a un besoin d’options, le plus rapide est de définir à la volée une fonction ad hoc qui inclura ces options. On parle dans ce cas de fonction anonyme ou de lambda fonction.
across(qui,
~ quoi(.x, options))
Dans cette syntaxe, la fonction du “quoi” peut être enrichie d’autant d’options que nécessaire. Le tilde (~) qui précède le nom de la fonction appelée est un raccourci pour la syntaxe R plus usuelle function(.x) {...}. L’argument du “qui” se nomme impérativement .x dans la syntaxe qui suit le tilde.
Par exemple on peut vouloir s’assurer de calculer des médianes qu’il y ait ou non des données manquantes dans le data.frame.
mtcars %>%
summarise(across(where(is.numeric),
~ median(.x, na.rm=TRUE)))
mpg cyl disp hp drat wt qsec vs am gear carb
1 19.2 6 196.3 123 3.695 3.325 17.71 0 0 4 2
On peut aussi construire une fonction nouvelle après le tilde, mais il faut qu’elle reste assez simple pour que le code soit lisible. (Si votre fonction devient trop touffue, créez-la comme un objet séparé et appelez-la ensuite dans across.)
Ici on compte les données manquantes de chacune des colonnes du data.frame.
mtcars %>%
summarise(across(everything(),
~ sum(is.na(.x))))
mpg cyl disp hp drat wt qsec vs am gear carb
1 0 0 0 0 0 0 0 0 0 0 0
Avec plusieurs fonctions à appliquer
Le deuxième argument de across peut être une fonction, comme on l’a vu, ou une liste de fonctions. Il est conseillé de donner des noms à ces fonctions.
across(qui,
list(
f1 = quoi1,
f2 = quoi2
))
# ou avec des options, via des fonctions anonymes
across(qui,
list(
f1 = ~ quoi1(.x, options1),
f2 = ~ quoi2(.x, options2)
))
L’usage le plus courant de cette syntaxe est de calculer d’un coup différentes statistiques sur différentes colonnes sans devoir copier/coller le code pour créer toutes les combinaisons.
mtcars %>%
summarise(across(c(mpg, hp),
list(
"moyenne" = ~ mean(.x, na.rm=TRUE),
"mediane" = ~ median(.x, na.rm=TRUE),
"nb_obs" = ~ sum( ! is.na(.x))
)))
mpg_moyenne mpg_mediane mpg_nb_obs hp_moyenne hp_mediane hp_nb_obs
1 20.09062 19.2 32 146.6875 123 32
Avec des noms personnalisés pour les colonnes créées
On le voit dans ce dernier exemple, les colonnes créées sont automatiquement nommées selon le modèle qui_quoi en reprenant le nom de la colonne d’origine (“qui”) et le nom de la fonction ou de l’élément de liste qui lui est appliqué (“quoi”) avec un _ comme séparateur. C’est le choix par défaut mais il est personnalisable via l’option .names de across.
La valeur de l’option.names est un simple texte entre guillemets. Dans ce texte on décrit la manière de nommer les colonnes créées, avec deux mots-clés qui sont {.col} et {.fn} pour indiquer respectivement le “qui” et le “quoi”.
Vous aurez peut-être reconnu dans la logique de cette syntaxe celle de la fonction glue::glue à laquelle ce texte est automatiquement transmis. Comme dans un appel à glue on peut ajouter des appels à des fonctions à condition qu’ils se trouvent à l’intérieur de la paire d’accolades : par exemple on peut écrire {toupper(.col)} au lieu de {.col}.
mtcars %>%
summarise(across(c(mpg, hp),
list(
"MOY" = ~ mean(.x, na.rm=TRUE),
"N" = ~ sum( ! is.na(.x))
),
.names = "{.fn}_{toupper(.col)}"))
MOY_MPG N_MPG MOY_HP N_HP
1 20.09062 32 146.6875 32
Si nécessaire on peut également utiliser des informations externes comme un paramètre (un vecteur de longueur 1 disponible dans l’Environnement) à condition, là encore, de l’encadrer entre accolades.
annee <- 2026 # un paramètre qu'on veut intégrer aux noms
mtcars %>%
summarise(across(c(mpg, hp),
list(
"MOY" = ~ mean(.x, na.rm=TRUE),
"N" = ~ sum( ! is.na(.x))
),
.names = "{.fn}_{.col}_{annee}"))
MOY_mpg_2026 N_mpg_2026 MOY_hp_2026 N_hp_2026
1 20.09062 32 146.6875 32
Avec différentes versions du “qui”
On a vu que le “qui”, le premier argument de across, pouvait être un vecteur de noms sans guillemets, un appel à la fonction everything() ou à la fonction where. Toutes les syntaxes qui fonctionnent dans dplyr::select sont en fait acceptées dans cet argument.
Parmi elles, il y a le cas particulier des paramètres, très utile quand on crée une fonction à l’intérieur de laquelle across est appelé. Si le paramètre est un vecteur texte de noms de colonnes (cette fois avec des guillemets) il est nécessaire d’appeler ce vecteur via la fonction all_of.
noms <- c("nom_var1", "nom_var2")
df %>%
mutate/summarise(across(all_of(noms), ...))
En toute prudence on devrait même écrire all_of({{noms}}) pour bien indiquer la nature extérieure du vecteur noms : ce n’est pas une colonne du data.frame en entrée du pipeline mais un objet distinct de l’Environnement. Cependant dans ce contexte, aucune confusion n’est possible : c’est d’abord un objet de l’Environnement qui est recherché, et pas une colonne. Inutile d’alourdir la syntaxe avec ces doubles accolades, au contraire d’une fonction comme dplyr::filter par exemple où c’est très vivement conseillé.
variables <- c("mpg", "hp")
# sans all_of : fonctionne mais génère un warning
mtcars %>%
summarise(across(variables,
~ max(.x, na.rm=TRUE)))
Warning: There was 1 warning in `summarise()`.
ℹ In argument: `across(variables, ~max(.x, na.rm = TRUE))`.
Caused by warning:
! Using an external vector in selections was deprecated in tidyselect 1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
# Was:
data %>% select(variables)
# Now:
data %>% select(all_of(variables))
See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
mpg hp
1 33.9 335
# avec all_of : impeccable
mtcars %>%
summarise(across(all_of(variables),
~ max(.x, na.rm=TRUE)))
mpg hp
1 33.9 335
L’intérêt est plus net quand on construit une fonction. Ici l’idée est de fournir un nom de data.frame sans guillemets, puis deux vecteurs (le second optionnel) de texte pour les noms des colonnes à résumer et les fonctions statistiques à leur appliquer.
statistiques <- function(data,
var,
stats=c("mean","max")){
# construction de la partie "quoi" à partir du paramètre
quoi <- lapply(stats,
function(fn)
as.formula(paste("~", fn,
"(.x, na.rm=TRUE)")))
names(quoi) <- stats
res <- data %>%
summarise(across(all_of(var),
quoi))
return(res)
}
statistiques(mtcars, "hp")
hp_mean hp_max
1 146.6875 335
statistiques(mtcars, c("wt","mpg"))
wt_mean wt_max mpg_mean mpg_max
1 3.21725 5.424 20.09062 33.9
statistiques(mtcars, c("wt","mpg"), stats=c("mean","sd"))
wt_mean wt_sd mpg_mean mpg_sd
1 3.21725 0.9784574 20.09062 6.026948
Usage “détourné”
Dans arrange ou group_by
Dans une syntaxe basique, il n’y absolument pas besoin d’utiliser across à l’intérieur des fonctions arrange ou group_by puisqu’il suffit d’y énumérer les colonnes sur lesquelles on veut trier ou grouper. L’intérêt de across se limite à une syntaxe paramétrée, comme dans les exemples précédents, et particulièrement dans une fonction.
Dans ce cas, on utilise across avec un seul argument, le “qui”. Dans ce cas, il a le sens de “sur chacune de ces colonnes” ; sous-entendu, c’est sur chacune de ces colonnes qu’on trie / qu’on groupe.
arrange(across(all_of(noms)))
group_by(across(all_of(noms)))
variables <- c("mpg", "hp")
mtcars %>%
arrange(across(all_of(variables))) %>%
relocate(all_of(variables), .before=everything())
mpg hp cyl disp drat wt qsec vs am gear carb
Cadillac Fleetwood 10.4 205 8 472.0 2.93 5.250 17.98 0 0 3 4
Lincoln Continental 10.4 215 8 460.0 3.00 5.424 17.82 0 0 3 4
Camaro Z28 13.3 245 8 350.0 3.73 3.840 15.41 0 0 3 4
Duster 360 14.3 245 8 360.0 3.21 3.570 15.84 0 0 3 4
Chrysler Imperial 14.7 230 8 440.0 3.23 5.345 17.42 0 0 3 4
Maserati Bora 15.0 335 8 301.0 3.54 3.570 14.60 0 1 5 8
AMC Javelin 15.2 150 8 304.0 3.15 3.435 17.30 0 0 3 2
Merc 450SLC 15.2 180 8 275.8 3.07 3.780 18.00 0 0 3 3
Dodge Challenger 15.5 150 8 318.0 2.76 3.520 16.87 0 0 3 2
Ford Pantera L 15.8 264 8 351.0 4.22 3.170 14.50 0 1 5 4
Merc 450SE 16.4 180 8 275.8 3.07 4.070 17.40 0 0 3 3
Merc 450SL 17.3 180 8 275.8 3.07 3.730 17.60 0 0 3 3
Merc 280C 17.8 123 6 167.6 3.92 3.440 18.90 1 0 4 4
Valiant 18.1 105 6 225.0 2.76 3.460 20.22 1 0 3 1
Hornet Sportabout 18.7 175 8 360.0 3.15 3.440 17.02 0 0 3 2
Merc 280 19.2 123 6 167.6 3.92 3.440 18.30 1 0 4 4
Pontiac Firebird 19.2 175 8 400.0 3.08 3.845 17.05 0 0 3 2
Ferrari Dino 19.7 175 6 145.0 3.62 2.770 15.50 0 1 5 6
Mazda RX4 21.0 110 6 160.0 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 110 6 160.0 3.90 2.875 17.02 0 1 4 4
Volvo 142E 21.4 109 4 121.0 4.11 2.780 18.60 1 1 4 2
Hornet 4 Drive 21.4 110 6 258.0 3.08 3.215 19.44 1 0 3 1
Toyota Corona 21.5 97 4 120.1 3.70 2.465 20.01 1 0 3 1
Datsun 710 22.8 93 4 108.0 3.85 2.320 18.61 1 1 4 1
Merc 230 22.8 95 4 140.8 3.92 3.150 22.90 1 0 4 2
Merc 240D 24.4 62 4 146.7 3.69 3.190 20.00 1 0 4 2
Porsche 914-2 26.0 91 4 120.3 4.43 2.140 16.70 0 1 5 2
Fiat X1-9 27.3 66 4 79.0 4.08 1.935 18.90 1 1 4 1
Honda Civic 30.4 52 4 75.7 4.93 1.615 18.52 1 1 4 2
Lotus Europa 30.4 113 4 95.1 3.77 1.513 16.90 1 1 5 2
Fiat 128 32.4 66 4 78.7 4.08 2.200 19.47 1 1 4 1
Toyota Corolla 33.9 65 4 71.1 4.22 1.835 19.90 1 1 4 1
On peut adapter la fonction construite précédemment pour y intégrer autant de variables de groupement qu’on souhaite, là encore sous forme de vecteur texte de noms entre guillemets.
statistiques <- function(data,
var,
groupes=NULL,
stats=c("mean","max")){
# construction de la partie "quoi" à partir du paramètre
quoi <- lapply(stats,
function(fn)
as.formula(paste("~", fn,
"(.x, na.rm=TRUE)")))
names(quoi) <- stats
res <- data %>%
group_by(across(all_of(groupes))) %>%
summarise(across(all_of(var),
quoi)) %>%
ungroup()
return(res)
}
statistiques(mtcars, "hp", groupes="cyl")
# A tibble: 3 × 3
cyl hp_mean hp_max
<dbl> <dbl> <dbl>
1 4 82.6 113
2 6 122. 175
3 8 209. 335
statistiques(mtcars, "hp") # sans groupes
# A tibble: 1 × 2
hp_mean hp_max
<dbl> <dbl>
1 147. 335
statistiques(mtcars,
var=c("hp","wt"),
groupes = c("cyl","am"),
stats = "median")
`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by cyl and am.
ℹ Output is grouped by cyl.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(cyl, am))` for per-operation grouping
(`?dplyr::dplyr_by`) instead.
# A tibble: 6 × 4
cyl am hp_median wt_median
<dbl> <dbl> <dbl> <dbl>
1 4 0 95 3.15
2 4 1 78.5 2.04
3 6 0 116. 3.44
4 6 1 110 2.77
5 8 0 180 3.81
6 8 1 300. 3.37
Dans rowSums et autres fonctions “horizontales”
Comme dans arrange ou group_by, l’utilisation de la syntaxe across(qui) sans autre argument peut également intervenir à l’intérieur de fonctions de calculs “horizontaux”. Par ce terme je veux désigner l’équivalent de fonctions de statistiques pour résumer un vecteur (une somme, une moyenne) mais qui s’appliquent à une série de valeurs dans une même ligne d’une matrice / d’un data.frame plutôt qu’au contenu d’une colonne. Par exemple l’équivalent horizontal de sum est rowSums, l’équivalent de mean est rowMeans.
mtcars %>%
mutate(total = rowSums(across(everything()))) %>%
relocate(total, .before = everything())
total mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 328.980 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 329.795 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 259.580 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 426.135 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 590.310 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Valiant 385.540 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Duster 360 656.920 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 240D 270.980 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 299.570 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 350.460 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 349.660 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Merc 450SE 510.740 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
Merc 450SL 511.500 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
Merc 450SLC 509.850 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
Cadillac Fleetwood 728.560 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
Lincoln Continental 726.644 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
Chrysler Imperial 725.695 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
Fiat 128 213.850 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 195.165 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 206.955 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 273.775 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Dodge Challenger 519.650 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
AMC Javelin 506.085 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
Camaro Z28 646.280 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
Pontiac Firebird 631.175 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
Fiat X1-9 208.215 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 272.570 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 273.683 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ford Pantera L 670.690 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
Ferrari Dino 379.590 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Maserati Bora 694.710 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
Volvo 142E 288.890 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
Si rowMeans et rowSums sont deux fonctions du package {base}, on n’y trouve pas des variantes pour toutes les fonctions de statistique descriptive verticales. Des packages comme {rje} ou {MatrixGenerics} proposent d’ajouter rowMins et rowMaxs mais on n’est jamais certain de trouver une version horizontale de la fonction statistique qui nous intéresse, même si {MatrixGenerics} est très généreuse de ce point de vue.
Les alternatives sont :
- la fonction
base::apply(qui s’applique normalement à une matrice, il importe donc que les types des colonnes renvoyées paracrosssoient bien tous les mêmes) avec l’argumentMARGIN=1qui indique un calcul par lignemtcars %>% mutate(tot = apply(across(everything()), MARGIN=1, FUN=sum)) %>% relocate(tot, .before = everything())tot mpg cyl disp hp drat wt qsec vs am gear carb Mazda RX4 328.980 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 329.795 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 259.580 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 426.135 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 590.310 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 Valiant 385.540 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 Duster 360 656.920 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 Merc 240D 270.980 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 Merc 230 299.570 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 Merc 280 350.460 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 Merc 280C 349.660 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 Merc 450SE 510.740 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 Merc 450SL 511.500 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3 Merc 450SLC 509.850 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3 Cadillac Fleetwood 728.560 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4 Lincoln Continental 726.644 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4 Chrysler Imperial 725.695 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4 Fiat 128 213.850 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1 Honda Civic 195.165 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2 Toyota Corolla 206.955 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1 Toyota Corona 273.775 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1 Dodge Challenger 519.650 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2 AMC Javelin 506.085 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2 Camaro Z28 646.280 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4 Pontiac Firebird 631.175 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2 Fiat X1-9 208.215 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1 Porsche 914-2 272.570 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2 Lotus Europa 273.683 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2 Ford Pantera L 670.690 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4 Ferrari Dino 379.590 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 Maserati Bora 694.710 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 Volvo 142E 288.890 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2 - la fonction
dplyr::rowwisequi indique également des calculs par ligne jusqu’au prochainungroupet permet dans ce cas d’utiliser la fonction “verticale” appliquée auacrosspour un effet horizontal.mtcars %>% rowwise() %>% mutate(tot = sum(across(everything()))) %>% ungroup() %>% relocate(tot, .before = everything())# A tibble: 32 × 12 tot mpg cyl disp hp drat wt qsec vs am gear carb <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 329. 21 6 160 110 3.9 2.62 16.5 0 1 4 4 2 330. 21 6 160 110 3.9 2.88 17.0 0 1 4 4 3 260. 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1 4 426. 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1 5 590. 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2 6 386. 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1 7 657. 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4 8 271. 24.4 4 147. 62 3.69 3.19 20 1 0 4 2 9 300. 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2 10 350. 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4 # ℹ 22 more rows
Si les trois approchent donnent des résultats équivalents, les deux premières ont quasiment les mêmes temps d’exécution, tandis que rowwise est beaucoup plus lente (d’un facteur 10 !). Globalement le recours à rowwise doit être vu comme une solution de la dernière chance, très lente, et dans laquelle il ne faut jamais omettre le ungroup().
Remarques
- La fonction
acrosspermet de se dispenser des “vieilles” fonctions commemutate_if,mutate_atetmutate_all, et les mêmes variantes poursummarise. Ces fonctions existent toujours dans {dplyr} (en tout cas dans la version 1.2.1 utilisée pour rédiger cet article) mais sont en voie d’abandon (deprecated) et sont donc susceptibles de disparaître d’une version future. Si vous avez des*_if/at/alldans votre code, remplacez-les par un recours àacross. - Dans les fonctions du “quoi” on peut réaliser des calculs plus sophistiqués que les opérations sur la colonne en cours. Les fonctions
cur_columnetcur_grouppermettent des calculs complexes impliquant plusieurs colonnes mais sont plus difficiles à manier. On dépasse ici le cadre de cette petite explication. - Vous vous demandez comment utiliser
acrossdans votre contexte particulier ? Nous pouvons organiser une formation : olivier.decourt@od-datamining.com
sessionInfo()
R version 4.5.2 (2025-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19045)
Matrix products: default
LAPACK version 3.12.1
locale:
[1] LC_COLLATE=French_France.utf8 LC_CTYPE=French_France.utf8
[3] LC_MONETARY=French_France.utf8 LC_NUMERIC=C
[5] LC_TIME=French_France.utf8
time zone: Europe/Paris
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] dplyr_1.2.1
loaded via a namespace (and not attached):
[1] digest_0.6.37 R6_2.6.1 fastmap_1.2.0 tidyselect_1.2.1
[5] xfun_0.59 magrittr_2.0.3 glue_1.8.1 tibble_3.3.0
[9] knitr_1.51 pkgconfig_2.0.3 htmltools_0.5.8.1 generics_0.1.4
[13] rmarkdown_2.29 lifecycle_1.0.5 cli_3.6.5 vctrs_0.7.3
[17] withr_3.0.3 compiler_4.5.2 rstudioapi_0.19.0 tools_4.5.2
[21] pillar_1.11.0 evaluate_1.0.5 yaml_2.3.12 otel_0.2.0
[25] rlang_1.2.0 jsonlite_2.0.0 htmlwidgets_1.6.4