【问题标题】:Fill in data frame with values from rows above用上面行中的值填充数据框
【发布时间】:2012-05-11 15:53:33
【问题描述】:

假设我有一个这样的数据框:

ID,  ID_2, FIRST, VALUE
-----------------------
'a', 'aa', TRUE, 2
'a', 'ab', FALSE, NA
'a', 'ac', FALSE, NA
'b', 'aa', TRUE, 5
'b', 'ab', FALSE, NA

因此,每个 ID 只为 FIRST = TRUE 设置一次 VALUE。 ID_2 可以在 ID 之间重复,但不是必须的。

如何将每个 ID 第一行的数字放入该 ID 的所有行中,使 VALUE 列变为 2、2、2、5、5?

我知道我可以简单地使用 for 循环遍历所有 ID,但我正在寻找一种更有效的方法。

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    与循环相比,该问题要求效率。以下是四种解决方案的比较:

    1. zoo::na.locf,它引入了包依赖关系,尽管它处理许多边缘情况,但要求“空白”值为 NA。其他解决方案很容易适应非 NA 空白。

    2. 基础 R 中的一个简单循环。

    3. 基础 R 中的递归函数。

    4. 我自己在基础 R 中的矢量化解决方案。

    5. tidyr 0.3.0 版中的新 fill() 函数,适用于 data.frames。

    请注意,这些解决方案中的大多数是针对向量而非数据帧的,因此它们不会检查任何 ID 列。如果数据框没有按 ID 分组,而要填充的值在每个组的顶部,那么您可以尝试dplyrdata.table 中的窗口函数

    # A popular solution
    f1 <- zoo::na.locf
    
    # A loop, adapted from https://stat.ethz.ch/pipermail/r-help/2008-July/169199.html
    f2 <- function(x) {
      for(i in seq_along(x)[-1]) if(is.na(x[i])) x[i] <- x[i-1]
      x
    }
    
    # Recursion, also from https://stat.ethz.ch/pipermail/r-help/2008-July/169199.html
    f3 <- function(z) { 
      y <- c(NA, head(z, -1))
      z <- ifelse(is.na(z), y, z)
      if (any(is.na(z))) Recall(z) else z }
    
    # My own effort
    f4 <- function(x, blank = is.na) {
      # Find the values
      if (is.function(blank)) {
        isnotblank <- !blank(x)
      } else {
        isnotblank <- x != blank
      }
      # Fill down
      x[which(isnotblank)][cumsum(isnotblank)]
    }
    
    # fill() from the `tidyr` version 0.3.0
    library(tidyr)
    f5 <- function(y) {
      fill(y, column)
    }
    # Test data, 2600 values, ~58% blanks
    x <- rep(LETTERS, 100)
    set.seed(2015-09-12)
    x[sample(1:2600, 1500)] <- NA
    x <- c("A", x) # Ensure the first element is not blank
    y <- data.frame(column = x, stringsAsFactors = FALSE) # data.frame version of x for tidyr
    
    # Check that they all work (they do)
    identical(f1(x), f2(x))
    identical(f1(x), f3(x))
    identical(f1(x), f4(x))
    identical(f1(x), f5(y)$column)
    
    library(microbenchmark)
    microbenchmark(f1(x), f2(x), f3(x), f4(x), f5(y))
    

    结果:

    Unit: microseconds
      expr      min        lq       mean    median        uq       max neval
     f1(x)  422.762  466.6355  508.57284  505.6760  527.2540   837.626   100
     f2(x) 2118.914 2206.7370 2501.04597 2312.8000 2497.2285  5377.018   100
     f3(x) 7800.509 7832.0130 8127.06761 7882.7010 8395.3725 14128.107   100
     f4(x)   52.841   58.7645   63.98657   62.1410   65.2655   104.886   100
     f5(y)  183.494  225.9380  305.21337  331.0035  350.4040   529.064   100
    

    【讨论】:

    • 我喜欢它。对 f4 进行少量添加以处理前面的 NA。最后一行应为: c(NA, x[which(isnotblank)])[cumsum(isnotblank)+1]
    • 这很棒,但需要一些解释。
    • @C8H10N4O2 你能具体说明一下需要解释的地方吗?
    【解决方案2】:

    如果您只需要继承 VALUE 列中的值,那么我认为您可以使用 zoo 包中的na.lofc() 函数。这是一个例子:

    a<-c(1,NA,NA,2,NA)
    na.locf(a)
    [1] 1 1 1 2 2
    

    【讨论】:

    • 这个解决方案有一点细微差别。如果第一个元素是“NA”,“na.locf()”函数将删除它。并且,结果列表中的元素数将为 -1。请注意这一事实。
    【解决方案3】:

    如果特定 ID 的 VALUE 始终出现在第一条记录中,您的数据似乎就是这种情况,您可以使用 match 查找该记录:

    df <- read.csv(textConnection("
    
    ID,  ID_2, FIRST, VALUE
    'a', 'aa', TRUE, 2
    'a', 'ab', FALSE, NA
    'a', 'ac', FALSE, NA
    'b', 'aa', TRUE, 5
    'b', 'ab', FALSE, NA
    
    "))
    
    df$VALUE <- df$VALUE[match(df$ID, df$ID)]
    df
    #    ID  ID_2  FIRST VALUE
    # 1 'a'  'aa'   TRUE     2
    # 2 'a'  'ab'  FALSE     2
    # 3 'a'  'ac'  FALSE     2
    # 4 'b'  'aa'   TRUE     5
    # 5 'b'  'ab'  FALSE     5
    

    【讨论】:

      【解决方案4】:

      为@nacnudus +1 处理前导空格

      f4 <- function(x, blank = is.na) {
      
        # Find the values
        if (is.function(blank)) {
          isnotblank <- !blank(x)
        } else {
          isnotblank <- x != blank
        }
      
        # Fill down
        xfill <- cumsum(isnotblank) 
        xfill[ xfill == 0 ] <- NA
      
        # Replace Blanks
        xnew <- x[ which(isnotblank) ][ xfill ]
        xnew[is.na(xnew)] <- blank
        return(xnew)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-17
        • 1970-01-01
        • 2022-01-21
        • 2019-03-21
        相关资源
        最近更新 更多