【问题标题】:More efficient ways to use R than 'for' loops使用 R 比“for”循环更有效的方法
【发布时间】:2015-11-30 12:44:41
【问题描述】:

我是 R 的新手,所以如果有一个明显的答案,我很抱歉。我查看了其他问题,我认为“申请”是答案,但我不知道在这种情况下如何使用它。

我进行了一项纵向调查,每年都会邀请参与者。在某些年份,他们未能参加,有时他们会死去。我需要确定自调查开始以来哪些参与者参与了一致的“连续”(即,如果他们停止,他们将永远停止)。

我使用“for”循环完成了这项工作,在下面的示例中效果很好。但是我有很多年和很多参与者,而且循环很慢。我可以使用更快的方法吗?

在示例中,TRUE 表示他们参加了那一年。该循环创建两个向量 - 'finalyear' 表示他们参与的最后一年,'streak' 表示他们是否在最后一年之前完成了所有年份(即案例 1、3 和 5)。

dat <- data.frame(ids = 1:5, "1999" = c(T, T, T, F, T), "2000" = c(T, F, T, F, T), "2001" = c(T, T, T, T, T), "2002" = c(F, T, T, T, T), "2003" = c(F, T, T, T, F))
finalyear <- NULL
streak <- NULL
for (i in 1:nrow(dat)) {
    x <- as.numeric(dat[i,2:6])
    y <- max(grep(1, x))
    finalyear[i] <- y
    streak[i] <- sum(x) == y
}
dat$finalyear <- finalyear
dat$streak <- streak

谢谢!

【问题讨论】:

  • 很多答案 - 有人想创建更大的数据集并对其进行基准测试吗?数据集有多大,是否有可能制作一个具有代表性的基准测试集?
  • 大约有25万例,25年。以下所有答案都解决了我的问题-谢谢大家!如果人们有兴趣,我可以制作一个有代表性的数据集来测试不同的方法。

标签: r for-loop apply survey


【解决方案1】:

这是dplyrtidyr 的解决方案。

gather(data = dat,year,value,-ids) %>%
  mutate(year=as.integer(gsub("X","",year))) %>%
  group_by(ids) %>%
  summarize(finalyear=last(year[value]),
            streak=!any(value[first(year):finalyear] == FALSE))

输出

  ids finalyear streak
1   1      2001   TRUE
2   2      2003  FALSE
3   3      2003   TRUE
4   4      2003  FALSE
5   5      2002   TRUE

【讨论】:

    【解决方案2】:

    这是一个使用apply 循环遍历行和rle 以查看状态更改频率的基本版本。您的条件似乎等同于以 TRUE 开头的状态,并且最多只更改为 FALSE 一次,所以我测试 rle 小于 3 并且第一个值为 TRUE

    > dat$streak = apply(dat[,2:6],1,function(r){r[1] & length(rle(r)$length)<=2})
    > 
    > dat
      ids X1999 X2000 X2001 X2002 X2003 streak
    1   1  TRUE  TRUE  TRUE FALSE FALSE   TRUE
    2   2  TRUE FALSE  TRUE  TRUE  TRUE  FALSE
    3   3  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE
    4   4 FALSE FALSE  TRUE  TRUE  TRUE  FALSE
    5   5  TRUE  TRUE  TRUE  TRUE FALSE   TRUE
    

    可能有很多方法可以计算finalyear,这只是找到每行的最后一个元素TRUE

    > dat$finalyear = apply(dat[,2:6], 1, function(r){max(which(r))})
    > dat
      ids X1999 X2000 X2001 X2002 X2003 streak finalyear
    1   1  TRUE  TRUE  TRUE FALSE FALSE   TRUE         3
    2   2  TRUE FALSE  TRUE  TRUE  TRUE  FALSE         5
    3   3  TRUE  TRUE  TRUE  TRUE  TRUE   TRUE         5
    4   4 FALSE FALSE  TRUE  TRUE  TRUE  FALSE         5
    5   5  TRUE  TRUE  TRUE  TRUE FALSE   TRUE         4
    

    【讨论】:

      【解决方案3】:

      For 循环在 R 中并不是天生不好,但如果你迭代地增长向量(就像你正在做的那样),它们会很慢。通常有更好的方法来做事。仅具有应用功能的解决方案示例:

      dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))})
      dat$streak <-  apply(dat[,2:7],MARGIN=1,function(x){sum(x[1:5])==x[6]})
      

      或选项 2,基于 @Spacedman 的评论:

      dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))})
      dat$streak <-  apply(dat[,2:6],MARGIN=1,function(x){max(which(x))==sum(x)})
      
      > dat
        ids X1999 X2000 X2001 X2002 X2003 finalyear streak
      1   1  TRUE  TRUE  TRUE FALSE FALSE         3   TRUE
      2   2  TRUE FALSE  TRUE  TRUE  TRUE         5  FALSE
      3   3  TRUE  TRUE  TRUE  TRUE  TRUE         5   TRUE
      4   4 FALSE FALSE  TRUE  TRUE  TRUE         5  FALSE
      5   5  TRUE  TRUE  TRUE  TRUE FALSE         4   TRUE
      

      【讨论】:

      • 整洁,但要注意它取决于在真/假数据之后直接添加finalyear,在这种情况下在第 7 列中。
      • 谢谢。我怀疑我是否应该这样做,或者调用 max(which(x)) 两次。将编辑。
      【解决方案4】:

      我们可以使用max.colrowSums 作为vectorized 方法。

      dat$finalyear <- max.col(dat[-1], 'last')
      

      如果存在没有TRUE 值的行,我们可以通过乘以rowSums 的双重否定来确保为该行返回0。 FALSE 将被强制为 0,乘​​以 0 返回该行的 0。

      dat$finalyear <- max.col(dat[-1], 'last')*!!rowSums(dat[-1])
      

      然后,我们通过将 2:6 列的 rowSums 与 'finalyear' 的比较来创建 'streak' 列

      dat$streak <-  rowSums(dat[,2:6])==dat$finalyear
      dat
      #   ids X1999 X2000 X2001 X2002 X2003 finalyear streak
      #1   1  TRUE  TRUE  TRUE FALSE FALSE         3   TRUE
      #2   2  TRUE FALSE  TRUE  TRUE  TRUE         5  FALSE
      #3   3  TRUE  TRUE  TRUE  TRUE  TRUE         5   TRUE
      #4   4 FALSE FALSE  TRUE  TRUE  TRUE         5  FALSE
      #5   5  TRUE  TRUE  TRUE  TRUE FALSE         4   TRUE
      

      或@ColonelBeauvel 建议的单行代码(它可以放在一行中,但决定通过 2-lines 使其显而易见)

      library(dplyr)
      mutate(dat, finalyear=max.col(dat[-1], 'last'), 
                  streak=rowSums(dat[-1])==finalyear)
      

      【讨论】:

      • 最佳简洁和矢量化答案。 +1
      • 一个带mutate(dat, finalyear=max.col(dat[-1], 'last'), streak=rowSums(dat[-1])==finalyear)的衬里
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-21
      • 2018-11-01
      • 2016-04-15
      • 2022-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多