如何更快地对群内观察进行排名?

Rub*_*ben 9 optimization r

我有一个非常简单的问题,但我可能没有想到vector-y足以有效地解决它.我尝试了两种不同的方法,现在他们已经在两台不同的计算机上循环了很长时间.我希望我可以说比赛让比赛变得更加精彩,但是... ......

在群体中排名观察

我有很长的数据(每人多行,每人观察一行),我基本上想要一个变量,告诉我这个人已经被观察过的频率.

我有前两列,想要第三列:

person  wave   obs
pers1   1999   1
pers1   2000   2
pers1   2003   3
pers2   1998   1
pers2   2001   2
Run Code Online (Sandbox Code Playgroud)

现在我正在使用两种循环方法.两者都非常缓慢(150k行).我确定我错过了一些东西,但我的搜索查询对我来说并没有真正帮助我(很难说出问题).

感谢您的任何指示!

# ordered dataset by persnr and year of observation
person.obs <- person.obs[order(person.obs$PERSNR,person.obs$wave) , ]

person.obs$n.obs = 0

# first approach: loop through people and assign range
unp = unique(person.obs$PERSNR)
unplength = length(unp)
for(i in 1:unplength) {
   print(unp[i])
   person.obs[which(person.obs$PERSNR==unp[i]),]$n.obs = 
1:length(person.obs[which(person.obs$PERSNR==unp[i]),]$n.obs)
    i=i+1
   gc()
}

# second approach: loop through rows and reset counter at new person
pnr = 0
for(i in 1:length(person.obs[,2])) {
  if(pnr!=person.obs[i,]$PERSNR) { pnr = person.obs[i,]$PERSNR
  e = 0
  }
  e=e+1
  person.obs[i,]$n.obs = e
  i=i+1
  gc()
}
Run Code Online (Sandbox Code Playgroud)

Cha*_*ase 14

Marek在这个问题上的答案在过去证明非常有用.我把它写下来并且几乎每天都使用它,因为它快速而有效.我们将使用ave()seq_along().

foo <-data.frame(person=c(rep("pers1",3),rep("pers2",2)),year=c(1999,2000,2003,1998,2011))

foo <- transform(foo, obs = ave(rep(NA, nrow(foo)), person, FUN = seq_along))
foo

  person year obs
1  pers1 1999   1
2  pers1 2000   2
3  pers1 2003   3
4  pers2 1998   1
5  pers2 2011   2
Run Code Online (Sandbox Code Playgroud)

另一个选择使用 plyr

library(plyr)
ddply(foo, "person", transform, obs2 = seq_along(person))

  person year obs obs2
1  pers1 1999   1    1
2  pers1 2000   2    2
3  pers1 2003   3    3
4  pers2 1998   1    1
5  pers2 2011   2    2
Run Code Online (Sandbox Code Playgroud)


Jaa*_*aap 5

包的一些替代方案。

数据表:

library(data.table)
# setDT(foo) is needed to convert to a data.table

# option 1:
setDT(foo)[, rn := rowid(person)]   

# option 2:
setDT(foo)[, rn := 1:.N, by = person]
Run Code Online (Sandbox Code Playgroud)

都给:

> foo
   person year rn
1:  pers1 1999  1
2:  pers1 2000  2
3:  pers1 2003  3
4:  pers2 1998  1
5:  pers2 2011  2
Run Code Online (Sandbox Code Playgroud)

如果你想要一个真正的排名,你应该使用这个frank函数:

setDT(foo)[, rn := frank(year, ties.method = 'dense'), by = person]
Run Code Online (Sandbox Code Playgroud)

dplyr:

library(dplyr)
# method 1
foo <- foo %>% group_by(person) %>% mutate(rn = row_number())
# method 2
foo <- foo %>% group_by(person) %>% mutate(rn = 1:n())
Run Code Online (Sandbox Code Playgroud)

两者都给出了类似的结果:

> foo
Source: local data frame [5 x 3]
Groups: person [2]

  person  year    rn
  (fctr) (dbl) (int)
1  pers1  1999     1
2  pers1  2000     2
3  pers1  2003     3
4  pers2  1998     1
5  pers2  2011     2
Run Code Online (Sandbox Code Playgroud)