【问题标题】:How can I rank observations in-group faster?如何更快地对组内的观察结果进行排名?
【发布时间】:2011-09-03 23:55:01
【问题描述】:

我有一个非常简单的问题,但我可能没有想到足够有效地解决它。我尝试了两种不同的方法,它们已经在两台不同的计算机上循环了很长时间。我希望我可以说比赛让它更令人兴奋,但是...... bleh。

对组中的观察进行排名

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

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

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

【问题讨论】:

    标签: optimization r


    【解决方案1】:

    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
    

    【讨论】:

    • @Marek 也谢谢你。
    • @Marek - 是的!我每周使用该代码 sn-p 几次,并将其保存在工作中的“有用代码 sn-ps”文件中。我应该开始抄下那些代码 sn-ps 的源代码。再次感谢。我会相应地更新Q。我想知道这两个问题现在是否应该合并,因为它们本质上是相同的?
    【解决方案2】:

    软件包的一些替代方案。

    data.table:

    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]
    

    两者都给出:

    > 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
    

    【讨论】:

    • 4 年后我现在也是这样做的。嗯,我当时的代码。我使用了一个循环,手动增加了循环计数器并使用了gc。哇。
    • @Ruben 我认可这次经历 ;-)
    • @Ruben 虽然我不会反对,但没必要接受我的回答。我只是添加了这个答案,因为这个问题经常被用作重复的目标。
    • 我认为这是更好的方法。 ave sn-p 是我有段时间没用过的必须查的东西,dplyr 的方式更快更习惯。
    【解决方案3】:

    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
    

    【讨论】:

    • D'oh 我没有说清楚'够了:我需要 obs 列,我没有。我不需要每人的观察次数,我知道了(用 sqldf 完成的)
    【解决方案4】:

    在基础 R 中使用 aggregaterank 的另一个选项:

    foo$obs <- unlist(aggregate(.~person, foo, rank)[,2])
    
     # person year obs
    # 1  pers1 1999   1
    # 2  pers1 2000   2
    # 3  pers1 2003   3
    # 4  pers2 1998   1
    # 5  pers2 2011   2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-19
      • 2016-07-04
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 2010-12-29
      相关资源
      最近更新 更多