为相邻相同数值创建分组编号

14

有没有比使用循环更快的方法来创建计数器索引?对于每个相邻的相同值运行,索引应该是相同的。当数据非常大时,我发现循环非常慢。

以下是输入和期望输出的示例:

x <- c(2, 3, 9, 2, 4, 4, 3, 4, 4, 5, 5, 5, 1)

期望得到的计数器:

c(1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9)
注意,不连续的序列具有不同的索引。例如,请查看值24的期望索引。

我的低效代码如下:

group[1]<-1
counter<-1
for (i in 2:n){
if (x[i]==x[i-1]){
    group[i]<-counter
}else{
    counter<-counter+1
    group[1]<-counter}
}
5个回答

13
如果您有像这样的数字值,您可以使用 diffcumsum 来累加数值的变化。
x <- c(2,3,9,2,4,4,3,4,4,5,5,5,1)
cumsum(c(1,diff(x)!=0))
# [1] 1 2 3 4 5 5 6 7 7 8 8 8 9

肯定比我的答案快。目前无法与Arun的data.table答案进行评估。 - Jota
感谢Frank的评论。我现在将使用MrFlick的建议。看起来需要安装Arun的data.table建议。 - Rens
1
如果您想尝试Arun的解决方案,请参考以下链接以获取安装帮助:https://github.com/Rdatatable/data.table/wiki/Installation - Jota
2
@Frank,昨天推出了一个更快、更节省内存的rleid()版本。在这里,diff(x)c(...)!=cumsum()都会分配新的内存空间,这意味着它需要原始数据的大约4倍的空间! - Arun

13

使用具有函数rleid()data.table:

require(data.table) # v1.9.5+
rleid(x)
#  [1] 1 2 3 4 5 5 6 7 7 8 8 8 9

7

这个适用于数字或字符值:

rep(1:length(rle(x)$values), times = rle(x)$lengths)
#[1] 1 2 3 4 5 5 6 7 7 8 8 8 9

您还可以通过仅调用rle一次来提高效率(大约快2倍),并且可以使用rep.int而不是rep来进行非常轻微的速度提升:

y <- rle(x)
rep.int(1:length(y$values), times = y$lengths)

3

上面Jota的答案可以进一步简化,这样会更快

with(rle(x), rep(1:length(lengths), lengths))

 [1] 1 2 3 4 5 5 6 7 7 8 8 8 9

1

使用 dplyr,您可以使用consecutive_id

library(dplyr) #1.1.0+
consecutive_id(x)
# [1] 1 2 3 4 5 5 6 7 7 8 8 8 9

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接