说我有这样的数据框:
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,但我正在寻找一种更有效的方法。
答案 0 :(得分:23)
如果您只需要继承VALUE列中的值,那么我认为您可以使用 zoo 包中的na.lofc()
函数。这是一个例子:
a<-c(1,NA,NA,2,NA)
na.locf(a)
[1] 1 1 1 2 2
答案 1 :(得分:23)
这个问题要求与循环相比的效率。以下是四种解决方案的比较:
zoo::na.locf
,它引入了一个包依赖,虽然它处理了许多边缘情况,但要求“空白”。值是NA。其他解决方案很容易适应非NA空白。
基础R中的简单循环。
基础R中的递归函数。
我自己在基础R中的载体化解决方案
fill()
版本0.3.0中的新tidyr
函数,适用于data.frames。
请注意,大多数这些解决方案都是针对矢量而非数据帧,因此他们不会检查任何ID列。如果数据框没有按ID分组,并且要填充的值位于每个组的顶部,那么您可以在dplyr
或data.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)
}