web-dev-qa-db-fra.com

R: compte les occurrences consécutives de valeurs dans une seule colonne

Je souhaite créer un numéro séquentiel dans chaque série de valeurs égales, comme un compteur d'occurrences, qui redémarre une fois que la valeur de la ligne en cours diffère de la ligne précédente.

Veuillez trouver un exemple d’entrée et de sortie attendue ci-dessous.

dataset <- data.frame(input = c("a","b","b","a","a","c","a","a","a","a","b","c"))
dataset$counter <- c(1,1,2,1,2,1,1,2,3,4,1,1)
dataset

#    input counter
# 1      a       1
# 2      b       1
# 3      b       2
# 4      a       1
# 5      a       2
# 6      c       1
# 7      a       1
# 8      a       2
# 9      a       3
# 10     a       4
# 11     b       1
# 12     c       1

Ma question est très similaire à celle-ci: Séquence cumulative d'occurrences de valeurs .

16
Richard

Vous devez utiliser sequence et rle:

> sequence(rle(as.character(dataset$input))$lengths)
 [1] 1 1 2 1 2 1 1 2 3 4 1 1
33

Une version efficace et plus simple de la fonction écrite ci-dessous est maintenant disponible dans le package data.table, appelée rleid. En utilisant cela, c'est juste:

setDT(dataset)[, counter := seq_len(.N), by=rleid(input)]

Voir ?rleid pour plus d'informations sur l'utilisation et les exemples. Merci à @Henrik pour la suggestion de mettre à jour ce post.


rle est certainement le moyen le plus pratique de le faire (+1 @ Ananda's). Mais on pourrait faire mieux (en termes de vitesse) sur des données plus volumineuses. Vous pouvez utiliser les fonctions duplist et vecseq (non exportées) à partir de data.table comme suit:

require(data.table)
arun <- function(y) {
    w = data.table:::duplist(list(y))
    w = c(diff(w), length(y)-tail(w,1L)+1L)
    data.table:::vecseq(rep(1L, length(w)), w, length(y))
}

x <- c("a","b","b","a","a","c","a","a","a","a","b","c")
arun(x)
# [1] 1 1 2 1 2 1 1 2 3 4 1 1

Benchmarking sur Big Data:

set.seed(1)
x <- sample(letters, 1e6, TRUE)
# rle solution
ananda <- function(y) {
    sequence(rle(y)$lengths)
}

require(microbenchmark)
microbenchmark(a1 <- arun(x), a2<-ananda(x), times=100)
Unit: milliseconds
            expr       min        lq    median       uq       max neval
   a1 <- arun(x)  123.2827  132.6777  163.3844  185.439  563.5825   100
 a2 <- ananda(x) 1382.1752 1899.2517 2066.4185 2247.233 3764.0040   100

identical(a1, a2) # [1] TRUE
17
Arun

Le paquet runner a une solution dédiée pour calculer ce qui est nécessaire. streak_run est la solution la plus rapide et accepte le vecteur en tant qu'entrée.

library(microbenchmark); library(runner)

x      <- sample(letters, 1e6, TRUE)
ananda <- function(y) sequence(rle(y)$lengths)

microbenchmark( a2<-ananda(x), runner <- streak_run(x), times=100)

#Unit: milliseconds
#                expr     min      lq     mean  median       uq      max neval
#     a2 <- ananda(x) 580.744 718.117 1059.676 944.073 1399.649 1699.293    10
#run <- streak_run(x)  37.682  39.568   42.277  40.591   43.947   52.917    10

identical(a2, run)
#[1] TRUE
0
GoGonzo