假设我有这个向量:

v=c(1,1,1,1,2,2,2,3,3,3)

如何返回每个不同值前两次出现的索引?我只发现了一种非常复杂的方式:

> (data.frame(v=v,n=1:length(v))%>%group_by(v)%>%slice_max(v,n=2,with_ties=F))[,2]%>%unlist%>%unname
[1] 1 2 5 6 8 9

还有这个:

> seen=setNames(numeric(length(unique(v))),unique(v))
> o=c();n=1;for(i in v){x=as.character(i);s=seen[x];if(s<2){o[n]=i;n=n+1};seen[x]=s+1};o
[1] 1 2 5 6 8 9

编辑:在我的基准测试中,使用data.table::rowid的解决方案是最快的:

bench=function(times,...){
  arg=match.call(expand.dots=F)$...
  l=length(arg)
  out=double(times*l)
  rand=sample(rep(1:l,times))
  n=1
  for(x in arg[rand]){t1=Sys.time();eval.parent(x);out[n]=Sys.time()-t1;n=n+1}
  setNames(out,sapply(arg[rand],function(x)unfo(paste(deparse(x),collapse="\n"))))
}

s=function(x,...,ignore.case=F,perl=F,fixed=F,useBytes=F){
  a=match.call(expand.dots=F)$...
  l=length(a)
  for(i in seq(1,l,2))x=gsub(a[[i]],if(i==l)""else a[[i+1]],x,ignore.case=ignore.case,perl=perl,fixed=fixed,useBytes=useBytes)
  x
}

unfo=function(x)s(x,"\\{\\n","\\{","\n *\\}","\\}",",\\n",",","\\n",";"," *([[:punct:]]+) *","\\1")

sizes=10^c(3:5)
r=sapply(sizes,function(i){
  v=round(i*runif(i))
  b=bench(10,
    unname(unlist(tapply(seq_along(v),v,head,2))),
    c(sapply(split(seq_along(v),v),head,2)),
    which(ave(v,v,FUN=seq_along)<3),
    which(data.table::rowid(v)<3),
    seq_along(v)[data.table::rowid(v)<3],
    purrr::map(unique(v),~which(.x==v)[1:2])%>%unlist,
    tibble(v)%>%mutate(row=row_number())%>%group_by(v)%>%slice(1:2)%>%pull(row),
    {seen=setNames(numeric(length(unique(v))),unique(v));o=c();n=1;for(i in v){x=as.character(i);s=seen[x];if(s<2){o[n]=i;n=n+1};seen[x]=s+1};o}
  )
  tapply(b,names(b),median)
})

r2=r[order(r[,3]),]
r3=apply(r2,2,function(x)formatC(x,max(0,2-ceiling(log10(min(x)))),format="f"))
r4=apply(rbind(paste0("1e",log10(sizes)),r3),2,function(x)formatC(x,max(nchar(x)),format="s"))
writeLines(apply(cbind(r4,c("",rownames(r2))),1,paste,collapse=" "))

这显示了10次运行的中位数时间(秒):

0.00014 0.00099  0.012 which(rowid(v)<3)
0.00015 0.00104  0.012 seq_along(v)[rowid(v)<3]
0.00146 0.01473  0.177 which(ave(v,v,FUN=seq_along)<3)
0.00352 0.03635  0.398 c(sapply(split(seq_along(v),v),head,2))
0.00381 0.03804  0.447 unname(unlist(tapply(seq_along(v),v,head,2)))
0.01764 2.43060  3.772 {seen=setNames(numeric(length(unique(v))),unique(v));o=c();n=1;for(i in v){x=as.character(i);s=seen[x];if(s<2){o[n]=i;n=n+1};seen[x]=s+1};o}
0.04189 0.38720  4.074 tibble(v)%>%mutate(row=row_number())%>%group_by(v)%>%;slice(1:2)%>%pull(row)
0.00315 0.35728 31.102 map(unique(v),~which(.x==v)[1:2])%>%unlist

推荐答案

我们可以在base R中用tapply,用‘v’作为分组,用输入作为‘v’的序列,得到前两个,用headunlistunnameit

unname(unlist(tapply(seq_along(v), v, head, 2)))
[1] 1 2 5 6 8 9

或者用‘v’代替split,通过用sapplylist上循环得到head

c(sapply(split(seq_along(v), v), head, 2))
[1] 1 2 5 6 8 9

或略微紧凑的选项,有rowid

library(data.table)
seq_along(v)[rowid(v) < 3]
[1] 1 2 5 6 8 9

或者就像@Henrik提到的那样,直接使用which

which(rowid(id) < 3)

R相关问答推荐

如何在四进制仪表板值框中显示值(使用shiny 的服务器计算)

对lme 4对象运行summary()时出错(diag中的错误(from,names = RST):对象unpackedMatrix_diag_get找不到)

如果列中存在相同的字符串,则对行值进行总和

使用R中相同值创建分组观测指标

警告:lmdif:info = 0. nls. lm()函数的输入参数不正确

如何使用tryCatch执行语句并忽略警告?

无法正确设置动态创建的Quarto标注的格式

try 将 colored颜色 编码添加到ggploly的标题中

如何将SAS数据集的列名和列标签同时包含在r中GT表的表首?

如何在R中平滑地绘制线图(不拟合)?

计算直线上点到参考点的总距离

从多面条形图中删除可变部分

从多个可选列中选取一个值到一个新列中

R中Gamma回归模型均方误差的两种计算方法不一致

如何使用FormC使简单算术运算得到的数字是正确的?

使用ifElse语句在ggploy中设置aes y值

如何在GALT包的函数&geom_x样条线中调整线宽

如果满足条件,则替换列的前一个值和后续值

如何在刻面和翻转堆叠条形图中对齐geom_text()

使用显式二元谓词子集化sfc对象时出错