假设我有这样的数据框:
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
Run Code Online (Sandbox Code Playgroud)
因此,每个ID仅为FIRST = TRUE设置VALUE.ID_2可能在ID之间重复,但不是必须的.
如何将每个ID的第一行中的数字放入该ID的所有行中,以使VALUE列变为2,2,2,5,5?
我知道我可以简单地用for循环遍历所有ID,但我正在寻找一种更有效的方法.
nac*_*dus 25
这个问题要求与循环相比的效率.以下是四种解决方案的比较:
zoo::na.locf,它引入了一个包依赖,虽然它处理了许多边缘情况,但要求'blank'值为NA.其他解决方案很容易适应非NA空白.
基础R中的简单循环
基础R中的递归函数
基础R中我自己的矢量化解决方案
版本0.3.0中的新fill()功能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))
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
Joy*_*Joy 23
如果您只需要继承VALUE列中的值,那么我认为您可以使用zoo包中的na.lofc()函数.这是一个例子:
a<-c(1,NA,NA,2,NA)
na.locf(a)
[1] 1 1 1 2 2
Run Code Online (Sandbox Code Playgroud)
如果特定 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
Run Code Online (Sandbox Code Playgroud)