Les opérateurs classiques tels que + ou - fonctionnent ligne à ligne et s’utilisent pour du calcul classique par exemple avec dplyr::mutate().
Dans le cadre des opérations groupées avec dplyr::summarise() (voir Section 8.3.2), nous avons abordé les fonctions d’agrégation telles que sum() ou mean() qui prennent un ensemble de valeurs et n’en renvoient qu’une seule.
Les fonctions à fenêtre, quant à elles, renvoient autant de valeurs que de valeurs en entrées, mais le calcul, au lieu de se faire ligne à ligne, tient compte des valeurs précédentes et suivantes. Ces fonctions sont donc sensibles au tri du tableau de données.
On peut distinguer les fonctions permettant d’accéder aux valeurs précédentes et suivantes, les fonctions de calcul d’un rang et les fonctions cumulatives.
38.1 Rappels à propos du tri
La fonction dplyr::arrange() permet de trier les valeurs d’un tableau de données. Par exemple, pour trier sur la longueur des pétales :
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Il est possible de fournir plusieurs variables de tri. Par exemple, pour trier sur l’espèce puis, pour les observations d’une même espace, selon la longueur du sépale de manière décroissante :
La fonction dplyr::lag() permet d’accéder à la valeur précédente d’un vecteur et la fonction dplyr::lead() à la valeur suivante. Il est donc prudent de toujours bien trier son tableau en amont. L’argument n permet d’accéder à la seconde valeur suivante, ou la troisième, etc.
# A tibble: 5 × 6
nom score groupe precedent suivant sur_suivant
<chr> <dbl> <chr> <chr> <chr> <chr>
1 marie 182 a <NA> dominique antoine
2 dominique 167 b marie antoine michelle
3 antoine 144 a dominique michelle marc
4 michelle 144 b antoine marc <NA>
5 marc 122 a michelle <NA> <NA>
À noter, cela génère des valeurs manquantes (NA) au début ou à la fin de la nouvelle variable.
38.3 Fonctions de rang
Les fonctions de rang vise à calculer le rang d’un individu, c’est-à-dire sa position quand le vecteur est trié d’une certaine manière. La fonction de base sous R est rank() qui propose plusieurs options. Mais l’on pourra se référer plus facilement aux différentes fonctions disponibles dans dplyr.
La première est dplyr::row_number() qui par défaut va numéroter les lignes du tableau selon le tri actuel.
d |>mutate(rang =row_number())
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marc 122 a 1
2 marie 182 a 2
3 antoine 144 a 3
4 dominique 167 b 4
5 michelle 144 b 5
On peut optionnellement lui passer une variable de tri pour le calcul du rang.
d |>mutate(rang =row_number(desc(score)))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marc 122 a 5
2 marie 182 a 1
3 antoine 144 a 3
4 dominique 167 b 2
5 michelle 144 b 4
Ou encore trier notre tableau en amont.
d |>arrange(desc(score)) |>mutate(rang =row_number())
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marie 182 a 1
2 dominique 167 b 2
3 antoine 144 a 3
4 michelle 144 b 4
5 marc 122 a 5
Chaque rang est ici unique. En cas d’égalité, les individus sont classés selon l’ordre du tableau. Mais dans cet exemple, il semble injuste de classer Michelle derrière Antoine dans la mesure où ils ont eu le même score. On pourra alors utiliser dplyr::min_rank() qui attribue aux observations égales le premier rang. Ici, Michelle et Antoine seront tous les deux classés 3e et Marc classé 5e.
d |>arrange(desc(score)) |>mutate(rang =min_rank(desc(score)))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marie 182 a 1
2 dominique 167 b 2
3 antoine 144 a 3
4 michelle 144 b 3
5 marc 122 a 5
Pour éviter la présence de sauts dans le classement et considéré Marc comme 4e, on utilisera dplyr::dense_rank().
d |>arrange(desc(score)) |>mutate(rang =dense_rank(desc(score)))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marie 182 a 1
2 dominique 167 b 2
3 antoine 144 a 3
4 michelle 144 b 3
5 marc 122 a 4
Pour plus d’options, on aura recours à rank(), qui par défaut attribue un rang moyen.
d |>arrange(desc(score)) |>mutate(rang =rank(desc(score)))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <dbl>
1 marie 182 a 1
2 dominique 167 b 2
3 antoine 144 a 3.5
4 michelle 144 b 3.5
5 marc 122 a 5
Mais il est possible d’indiquer d’autres méthodes de traitement des égalités, par exemple l’utilisation du rang maximum (l’inverse de min_rank()).
d |>arrange(desc(score)) |>mutate(rang =rank(desc(score), ties.method ="max"))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marie 182 a 1
2 dominique 167 b 2
3 antoine 144 a 4
4 michelle 144 b 4
5 marc 122 a 5
La fonction dplyr::percent_risk() renvoie un rang en pourcentage, c’est-à-dire une valeur numérique entre 0 et 1 où 0 représente le plus petit rang et 1 le plus grand.
d |>arrange(desc(score)) |>mutate(rang =percent_rank(desc(score)))
# A tibble: 5 × 4
nom score groupe rang
<chr> <dbl> <chr> <dbl>
1 marie 182 a 0
2 dominique 167 b 0.25
3 antoine 144 a 0.5
4 michelle 144 b 0.5
5 marc 122 a 1
Enfin, les rangs peuvent être calculés par groupe.
d |>group_by(groupe) |>mutate(rang =min_rank(desc(score))) |>arrange(groupe, rang)
# A tibble: 5 × 4
# Groups: groupe [2]
nom score groupe rang
<chr> <dbl> <chr> <int>
1 marie 182 a 1
2 antoine 144 a 2
3 marc 122 a 3
4 dominique 167 b 1
5 michelle 144 b 2
38.4 Fonctions cumulatives
R propose nativement plusieurs fonctions cumulatives comme la somme (cumsum()), le minimum (cummin()), le maximum (cummax()) ou encore le produit (cumprod()). dplyr fournit la moyenne cumulée (dplyr::cummean()). Le calcul s’effectue à chaque fois sur les premières lignes du tableau jusqu’à la ligne considérée.
d |>mutate(sum =cumsum(score),mean =cummean(score),min =cummin(score),max =cummax(score),prod =cumprod(score) )
# A tibble: 5 × 8
nom score groupe sum mean min max prod
<chr> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 marc 122 a 122 122 122 122 122
2 marie 182 a 304 152 122 182 22204
3 antoine 144 a 448 149. 122 182 3197376
4 dominique 167 b 615 154. 122 182 533961792
5 michelle 144 b 759 152. 122 182 76890498048
Le résultat est de facto fortement dépendant du tri du tableau.
d |>arrange(score) |>mutate(sum =cumsum(score),mean =cummean(score),min =cummin(score),max =cummax(score),prod =cumprod(score) )
# A tibble: 5 × 8
nom score groupe sum mean min max prod
<chr> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 marc 122 a 122 122 122 122 122
2 antoine 144 a 266 133 122 144 17568
3 michelle 144 b 410 137. 122 144 2529792
4 dominique 167 b 577 144. 122 167 422475264
5 marie 182 a 759 152. 122 182 76890498048
# A tibble: 5 × 4
nom score groupe cumany
<chr> <dbl> <chr> <lgl>
1 marc 122 a FALSE
2 marie 182 a TRUE
3 antoine 144 a TRUE
4 dominique 167 b TRUE
5 michelle 144 b TRUE
On peut, notamment dans des analyses longitudinales, avoir besoin de repérer chaque changement d’une certaine valeur. Dans le chapitre sur les conditions logiques, nous avions proposé une fonction is_different() permettant de comparer deux valeurs tout en tenant compte des valeurs manquantes (voir Section 37.2). Nous proposons ici une fonction cumdifferent() permettant de compter les changements de valeurs (et donc d’identifier les lignes continues ayant les mêmes valeurs). Cela est particulièrement utile dans le cadre d’analyses longitudinales.
d <- d |>arrange(score) |>mutate(sous_groupe =cumdifferent(groupe))d
# A tibble: 5 × 4
nom score groupe sous_groupe
<chr> <dbl> <chr> <int>
1 marc 122 a 1
2 antoine 144 a 1
3 michelle 144 b 2
4 dominique 167 b 2
5 marie 182 a 3
Dans la cas présent, cela permet d’identifier des sous-groupes, i.e. des lignes contiguës ayant le même groupe : 1 est le sous-groupe de tête du groupe a, 2 le sous-groupe b et 3 le deuxième sous-groupe issu de a.
Une variante est la fonction num_cycle() ci-après1. On doit lui passer une condition / vecteur logique en entrée. Il numérote uniquement les sous-groupes remplissant la condition et renvoie NA sinon.
1 Ne pas oublier de copier également la fonction is_different().
num_cycle <-function(x) {if (!is.logical(x))stop("'x' should be logical.") res <-cumsum(x &is_different(x, lag(x))) res[!x] <-NA res}
d |>mutate(sous_groupe_a =num_cycle(groupe =="a"),sous_groupe_b =num_cycle(groupe =="b") )
# A tibble: 5 × 6
nom score groupe sous_groupe sous_groupe_a sous_groupe_b
<chr> <dbl> <chr> <int> <int> <int>
1 marc 122 a 1 1 NA
2 antoine 144 a 1 1 NA
3 michelle 144 b 2 NA 1
4 dominique 167 b 2 NA 1
5 marie 182 a 3 2 NA