【问题标题】:Replace NA values in column with value in row above +1将列中的 NA 值替换为 +1 上方的行中的值
【发布时间】:2017-05-05 19:59:40
【问题描述】:

我有以下数据框:

game <- c('game1','game1','game2','game2','game2','game3','game4', 'game4')
shot_number <- c(1,NA,1,NA,NA,1,1,NA)
df <- data.frame(game, shot_number)

      game     shot_number
      game1              1
      game1             NA
      game2              1
      game2             NA
      game2             NA
      game3              1
      game4              1
      game4             NA

我想通过在上面一行的值上加 1 来填充 NA,所以 df 的内容如下:

      game     shot_number
      game1              1
      game1              2
      game2              1
      game2              2
      game2              3
      game3              1
      game4              1
      game4              2

我不知道是否有某种方法可以使用“zoo”库和 na.locf 来执行此操作,或者我是否需要编写一个 for 循环或某种函数。

【问题讨论】:

    标签: r for-loop na zoo


    【解决方案1】:

    使用 dplyr 组和 cumsum:

    library(dplyr)
    
    df1 %>% 
      group_by(game) %>% 
      mutate(shot_number_new = cumsum(is.na(shot_number)) + 1)
    
    # Source: local data frame [8 x 3]
    # Groups: game [4]
    # 
    #     game shot_number shot_number_new
    #   <fctr>       <dbl>           <dbl>
    # 1  game1           1               1
    # 2  game1          NA               2
    # 3  game2           1               1
    # 4  game2          NA               2
    # 5  game2          NA               3
    # 6  game3           1               1
    # 7  game4           1               1
    # 8  game4          NA               2
    

    【讨论】:

    • 这个逻辑比我的更干净,效率也会更高。使用ave 的基本R 翻译是ave(df$shot_number, df$game, FUN=function(i) cumsum(is.na(i)) + 1))
    【解决方案2】:

    这是适用于您的示例的基本 R 方法。

    df$shot_number <- ave(df$shot_number, df$game,
                          FUN=function(i) pmin(tail(cumsum(c(1, is.na(i))), -1), i, na.rm=TRUE))
    

    这里,ave 按组运行函数 (df$game)。对于每场比赛,计算 NA 的累积总和,在开始前加上 1。使用tail 删除最终值,因为结果将是 1 个元素长。然后将其最小值作为实际向量,去除任何 NA。

    返回

    df
       game shot_number
    1 game1           1
    2 game1           2
    3 game2           1
    4 game2           2
    5 game2           3
    6 game3           1
    7 game4           1
    8 game4           2
    

    数据

    df <-
    structure(list(game = structure(c(1L, 1L, 2L, 2L, 2L, 3L, 4L, 
    4L), .Label = c("game1", "game2", "game3", "game4"), class = "factor"), 
        shot_number = c(1, NA, 1, NA, NA, 1, 1, NA)), .Names = c("game", 
    "shot_number"), row.names = c(NA, -8L), class = "data.frame")
    

    【讨论】:

      【解决方案3】:

      以下解决方案都处理问题中的示例数据,但假设越来越复杂的一般情况。 (4) 是最一般的,但如果实际情况不需要完全一般性,则可能出于简单的原因优先选择其他。没有使用任何包。

      1) 在示例数据中,每组中的基行为 1,其余数字为 NA,因此如果这是一般模式,那么我们可以像这样使用 aveseq_along .

      transform(df, shot_number = ave(shot_number, game, FUN = seq_along))
      

      2) 如果基数不一定是 1,则将 (1) 中的 seq_along 替换为 f,如下所示:

      f <- function(x) seq(x[1], length = length(x))
      transform(df, shot_number = ave(shot_number, game, FUN = f))
      

      2a) 这也适用于与 (2) 相同的假设。它将每个 NA 替换为 1,然后在 game 组中使用 cumsum

      NAtoN <- function(x, N) replace(x, is.na(x), N)
      transform(df, shot_number = ave(NAtoN(shot_number, 1), game, FUN = cumsum))
      

      3) 如果一般情况是数字和 NA 的任何混合,但每个游戏组的第一个元素已知不是 NA,那么我们可以从非 NA 组成组连同其后的 NA:

      transform(df, shot_number = ave(shot_number, cumsum(!is.na(shot_number)), FUN = f))
      

      4) 如果游戏组的第一个元素也可以是 NA,则如果游戏组以 NA 开头,则处理由非 NA 后跟 NA 或所有 NA 定义的子组。在前导 NA 的情况下使用 0 作为基值(或将 f2 中的 0 替换为其他数字。)

       f2 <- function(x) ave(NAtoN(x, 0), cumsum(!is.na(x)), FUN = f)
       transform(df, shot_number = ave(shot_number, game, FUN = f2))
      

      【讨论】:

        【解决方案4】:

        您可以使用 group_by()row_number() 而不显式使用原始的 shot_number 列:

        df %>%
          group_by(game) %>%
          mutate(shot_number2 = row_number())
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-03-15
          • 2018-09-11
          • 2015-01-21
          • 2018-11-17
          • 1970-01-01
          • 2022-10-01
          • 1970-01-01
          相关资源
          最近更新 更多