【问题标题】:How to update each column based on previous column using for loop如何使用 for 循环根据前一列更新每一列
【发布时间】:2018-02-02 21:17:45
【问题描述】:

我的数据由一个 id 变量和多个访问变量组成,这些变量会随着时间的推移跟踪一个人的得分。我正在尝试将分数向前推进,将任何后续零更新为该分数。如果有一个 NA 我想留下它(代表没有访问),如果一个人后来得到一个新的分数,我希望新的分数继续下去。

我提供了一个可重复的小示例,但我的实际数据非常大,因此手动更新不是一种选择。我目前的尝试是使用 for 循环遍历每个人(行)的访问列。但是我收到了这个警告:

if ((!is.na(first) & first != 0) & (!is.na(second) & second == 中的错误: 参数长度为零 另外:警告信息: 在 is.na(second) 中:is.na() 应用于 'NULL' 类型的非(列表或向量)

这看起来是因为在环境中(Rstuio)首先有一个 NA_real_ 的值,第二个有一个 NULL(空)的值。

如何正确定义这些? 我在 for 循环方面没有太多经验,所以欢迎所有建议!

id <- c(101, 102, 103, 104)
visit.1 <- c(0, 21, 0, 21)
visit.2 <- c(0, 0, 50, 0)
visit.3 <- c(0, 0, 0, 44)
visit.4 <- c(NA, NA, 0, 0)
dat <- data.frame(id, visit.1, visit.2, visit.3, visit.4)


for(i in 1:nrow(dat)){
  for(j in 2:ncol(dat)){

    first <- dat[i, j]
    second <- dat[i,(j+1)]

    if((!is.na(first) & first != 0) & (!is.na(second) & second == 0)){
      second <- first
      } else {
        second <- second
      }
   }
  }

原始数据集:

id visit.1 visit.2 visit.3 visit.4
1 101       0       0       0      NA
2 102      21       0       0      NA
3 103       0      50       0       0
4 104      21       0      44       0

想要的最终结果:

id visit.1 visit.2 visit.3 visit.4
1 101       0       0       0      NA
2 102      21      21      21      NA
3 103       0      50      50      50
4 104      21      21      44      44

【问题讨论】:

    标签: r for-loop if-statement


    【解决方案1】:

    一个非常简单的方法是消除内部循环并将内部替换循环矢量化。这不是最好的解决方案,但更接近您更熟悉的解决方案:

    id <- c(101, 102, 103, 104)
    visit.1 <- c(0, 21, 0, 21)
    visit.2 <- c(0, 0, 50, 0)
    visit.3 <- c(0, 0, 0, 44)
    visit.4 <- c(NA, NA, 0, 0)
    dat <- data.frame(id, visit.1, visit.2, visit.3, visit.4)
    
    for (index in 3:5){
      dat[[index]]<-ifelse(dat[[index]]==0, dat[[index-1]], dat[[index]])
    }
    

    我正在使用 for 循环从列移动到列。然后 ifelse 正在查看整个列。对于 ==0 的行,使用左侧的值,如果不使用当前值。

    【讨论】:

      【解决方案2】:

      Dave2e 用更少的代码行回答了您的问题,但我稍微修改了您的尝试,以便您可以看到您到底做错了什么:

      for(i in 1:nrow(newdat)){
        for(j in 3:ncol(newdat)) {
      
          first <- newdat[i, j-1]
          second <- newdat[i, j]
      
          if(!is.na(first) & !is.na(second) & second == 0){
            newdat[i,j] <- first
          }
        }
      }
      

      首先使用dat[i,(j+1)],你在for(j in 2:ncol(dat)){ ... } 中计数太多,所以我使用3:ncol(dat) 并设置first &lt;- newdat[i, j-1]。 其次second &lt;- first 不起作用,您需要直接处理数据帧。

      【讨论】:

        【解决方案3】:

        zoo::na.locf 可以在这里使用

        library(zoo)
        data <- tail(t(dat), -1)
        
                # [,1] [,2] [,3] [,4]
        # visit.1    0   21    0   21
        # visit.2    0    0   50    0
        # visit.3    0    0    0   44
        # visit.4   NA   NA    0    0
        
        placeholder_value <- function(mat) {
            head(setdiff(seq(0, max(c(mat), na.rm=TRUE), 1), unique(c(mat))), 1)
        }
        val <- placeholder_value(data)
        

        由于na.locf 搜索NA 值进行插补,NA 值在运行na.locf 之前被更改为占位符值(并且0 被更改为NA),然后这些值被转换恢复原值。

        impute_vals <- function(header, data, val) {
            data <- replace(data, is.na(data), val)      # replace NA with placeholder value
            data <- replace(data, data==0, NA)           # replace 0 with NA for na.locf
            data <- apply(data, 2, function(i) zoo::na.locf(i, na.rm=FALSE))
            data <- rbind(header, data)
            data <- t(data)
            data <- replace(data, is.na(data), 0)        # revert to original values
            data <- replace(data, data==val, NA)
            data <- as.data.frame(data)
            data
        }
        
        impute_vals(head(t(dat), 1), data, val)
        
           # id visit.1 visit.2 visit.3 visit.4
        # 1 101       0       0       0      NA
        # 2 102      21      21      21      NA
        # 3 103       0      50      50      50
        # 4 104      21      21      44      44
        

        【讨论】:

          【解决方案4】:

          另一种基本 R 方法将提供给我们ave

          cbind(dat[1],t(apply(dat[-1],1,function(x)ave(x,cumsum(x),FUN=sum))))
             id visit.1 visit.2 visit.3 visit.4
          1 101       0       0       0      NA
          2 102      21      21      21      NA
          3 103       0      50      50      50
          4 104      21      21      44      44
          

          data.table 格式。不涉及循环:

          library(data.table)
          A=setDT(melt(dat,1))[,.(variable,ave(value,cumsum(value),FUN=sum)),by=id]
          dcast(A,id~variable,value.var = "V2")
             id visit.1 visit.2 visit.3 visit.4
          1 101       0       0       0      NA
          2 102      21      21      21      NA
          3 103       0      50      50      50
          4 104      21      21      44      44
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-06-24
            • 1970-01-01
            • 2018-07-19
            • 2021-05-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多