使用上面的行中的值填充数据框

时间:2012-05-11 15:53:33

标签: r dataframe

说我有这样的数据框:

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,但我正在寻找一种更有效的方法。

4 个答案:

答案 0 :(得分:23)

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

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

答案 1 :(得分:23)

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

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

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

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

  4. 我自己在基础R中的载体化解决方案

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

  6. 请注意,大多数这些解决方案都是针对矢量而非数据帧,因此他们不会检查任何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
    

答案 2 :(得分:4)

如果特定ID的值始终显示在第一条记录中(您的数据似乎就是这种情况),则可以使用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

答案 3 :(得分:0)

+1 @nacnudus 处理领先的空白

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