R: подсчет последовательных вхождений значений в одном столбце
Я хочу создать последовательный номер в каждом запуске равных значений, например счетчик вхождений, который перезапускается, как только значение в текущей строке отличается от предыдущей строки.
пожалуйста, найдите пример ввода и ожидаемого вывода ниже.
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
мой вопрос очень похож на этот: совокупная последовательность вхождений значений.
3 ответов
вам нужно использовать sequence
и rle
:
> sequence(rle(as.character(dataset$input))$lengths)
[1] 1 1 2 1 2 1 1 2 3 4 1 1
эффективная и более простая версия функции, написанная ниже, теперь доступна в data.пакет таблицы, вызванный rleid
. Используя это, это просто:
setDT(dataset)[, counter := seq_len(.N), by=rleid(input)]
посмотреть ?rleid
подробнее об использовании и примерах. Спасибо @Henrik за предложение обновить этот пост.
rle
это, безусловно, самый удобный способ сделать это (+1 @Ananda). Но можно было бы сделать лучше (с точки зрения скорости) на больших данных. Вы можете использовать duplist
и vecseq
функции (не экспортируются) из data.table
следующим образом:
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
бенчмаркинг по большим данным:
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
пакета бегун имеет специальное решение для вычисления того, что необходимо. streak_run
быстрый решение и принимает вектор в качестве входных данных.
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