提问人:Richard 提问时间:11/15/2013 最后编辑:HenrikRichard 更新时间:7/5/2021 访问量:31213
在连续运行值中创建计数器
Create counter within consecutive runs of values
问:
我希望在每次运行中创建一个相等值的序列数,就像发生次数计数器一样,一旦当前行中的值与前一行不同,它就会重新启动。
请在下面找到输入和预期输出的示例。
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
我的问题与这个问题非常相似:值出现的累积序列。
答:
52赞
A5C1D2H2I1M1N2O1R2T1
11/15/2013
#1
您需要使用 和 :sequence
rle
> sequence(rle(as.character(dataset$input))$lengths)
[1] 1 1 2 1 2 1 1 2 3 4 1 1
评论
0赞
Richard
11/15/2013
干杯,这就像一个魅力!你怎么知道$lengths部分?还有其他属性吗?(不要在 R Docs 中看到它们)。
2赞
A5C1D2H2I1M1N2O1R2T1
11/15/2013
@Richard,请参阅文档的“值”部分。返回的两个值(在“rle”的 a 中)是 和 。?rle
list
class
lengths
values
0赞
Pake
6/18/2022
与 group_by() 配合得很好。
27赞
Arun
11/15/2013
#2
从 v1.9.8(新闻第 16 项)开始,使用rowid
rleid
dataset[, counter := rowid(rleid(input))]
定时代码:
set.seed(1L)
library(data.table)
DT <- data.table(input=sample(letters, 1e6, TRUE))
DT1 <- copy(DT)
bench::mark(DT[, counter := seq_len(.N), by=rleid(input)],
DT1[, counter := rowid(rleid(input))])
计时:
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:t> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 DT[, `:=`(counter, seq_len(.N)), by = rleid(input)] 613.8ms 613.8ms 1.63 18.8MB 8.15 1 5 614ms
2 DT1[, `:=`(counter, rowid(rleid(input)))] 60.5ms 71.4ms 12.7 26.4MB 14.5 7 8 553ms
下面编写的函数的高效且更直接的版本现在在 data.table 包中可用,称为 。使用它,它只是:rleid
setDT(dataset)[, counter := seq_len(.N), by=rleid(input)]
有关用法和示例的更多信息,请参阅。感谢 @Henrik 对更新这篇文章的建议。?rleid
rle
绝对是最方便的方法(+1 @Ananda)。但是,在更大的数据上,人们可以做得更好(在速度方面)。您可以按如下方式使用 and 函数(未导出):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
评论
0赞
Richard
11/15/2013
@Arun,谢谢这是我正在处理的一个较小的数据集,但它肯定会在未来派上用场!对不起,我只能接受一个答案!:(
3赞
vrajs5
7/15/2014
嗨,@Arun - 我认为最新版本的“data.table”版本 1.9.2 中没有 duplist
0赞
Matifou
9/16/2018
似乎此代码不再适用于最新版本的 data.table?谢谢!
1赞
chinsoon12
9/19/2019
还有另一个 MTD:setDT(dataset)[, cnt := rowid(rleid(input))]
0赞
Arun
9/27/2019
@chinsoon12好多了!随意将其编辑到答案中。
4赞
GoGonzo
9/18/2018
#3
Package runner 有专门的解决方案来计算所需的内容。 是最快的解决方案,并接受向量作为输入。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
评论
0赞
jmich738
10/5/2018
这个包还可用吗?我似乎无法下载它
0赞
GoGonzo
10/5/2018
是的,它是。用。您使用什么系统?我刚刚在 Linux 和 MacOS 上检查了一下,它可以正常工作install.packages("runner")
0赞
jmich738
10/11/2018
我在 Windows 上有 R 3.4.1。当我尝试安装它时,它说package ‘runner’ is not available (for R version 3.4.1)
0赞
GoGonzo
10/12/2018
尝试更新到最新的 R 版本或从 github 安装devtools::install_github("gogonzo/runner")
0赞
jmich738
10/15/2018
那一定是问题所在。这是一台工作电脑,我拿不到.我正在等待我的 R 更新,然后希望我能得到这个包devtools
评论