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

时间:2011-05-28 15:48:18

标签: optimization r

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

在组中排名观察

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

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

person  wave   obs
pers1   1999   1
pers1   2000   2
pers1   2003   3
pers2   1998   1
pers2   2001   2

现在我正在使用两种循环方法。两者都非常缓慢(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()
}

3 个答案:

答案 0 :(得分:14)

Marek在this question中的答案在过去证明非常有用。我把它写下来并且几乎每天都使用它,因为它快速而有效。我们将使用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

使用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

答案 1 :(得分:4)

使用个套餐的一些替代方案。

<强> data.table:

library(data.table)
setDT(foo)[, rn := 1:.N, by = person]  # setDT(foo) is needed to convert to a data.table

或使用新的rowid函数( v1.9.7 + ,目前仅在the development version中可用)

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

都给出了:

> foo
   person year rn
1:  pers1 1999  1
2:  pers1 2000  2
3:  pers1 2003  3
4:  pers2 1998  1
5:  pers2 2011  2

如果您想要真正的排名,则应使用frank函数:

setDT(foo)[, rn := frank(year, ties.method = 'dense'), by = person]

<强> 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())

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

> 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

答案 2 :(得分:2)

by可以解决这个问题吗?

> foo <-data.frame(person=c(rep("pers1",3),rep("pers2",2)),year=c(1999,2000,2003,1998,2011),obs=c(1,2,3,1,2))
> foo
  person year obs
1  pers1 1999   1
2  pers1 2000   2
3  pers1 2003   3
4  pers2 1998   1
5  pers2 2011   2
> by(foo, foo$person, nrow)
foo$person: pers1
[1] 3
------------------------------------------------------------ 
foo$person: pers2
[1] 2