2012-05-11 53 views
8

说与值的数据帧我有这样一个数据帧:填充在从行上述

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 

所以值仅对FIRST = TRUE每ID设置一次。 ID_2可能在ID之间重复,但不一定。

我如何把每个ID的第一行中的数字到该ID的所有行,使得值列变为2,2,2,5,5?

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

回答

16

如果您只需要从VALUE列中继承值,那么我认为您可以使用函数从动物园包。这里有一个例子:

a<-c(1,NA,NA,2,NA) 
na.locf(a) 
[1] 1 1 1 2 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 
19

的问题问的效率与循环比较。下面是四个解决方案的比较:

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

  2. 在基地R.

  3. 在基地R.递归函数

  4. 在基地R.我自己向量化的解决方案

  5. fill()功能在0.3版本tidyr一个简单的循环.0,它适用于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 
+1

我喜欢它。对f4进行小补充以处理先前的NAs。 最后行应为: C(NA,X [其中(isnotblank)])[cumsum(isnotblank)+1] – DangerMouse

+0

大的答案。 f4也适用于角色。 – BCC

+0

这很棒,但会从一些解释中受益。 – C8H10N4O2

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