我正在尝试使用300万行数据集中的非NA先前填充NA值。目前我能够做到,但需要大约3小时。
约束 - 我不能使用任何库,它必须用R basic
完成数据 - 我的数据如下(摘录)
目前我一直在使用以下代码
CHARDIF <- diff(VERINDEX_VEC)
k = 1
for (j in VERINDEX_VEC){
#when value is in vector calculate difference to next value and copy VER.
Special cases for First and Last value
ifelse(j == 1, ALL_POS$C01[j:CHARDIF[k]] <- ALL_POS$C01[j],
ifelse(j == max(VERINDEX_VEC), ALL_POS$C01[j:max(as.numeric
(row.names(ALL_POS)))] <- ALL_POS$C01[j],ALL_POS$C01[j:(j+CHARDIF[k]-1)] <-
ALL_POS$C01[j]))
k = k + 1
}
正如你所看到的,我有一个非NA位置的向量,然后我计算位置之间的差异,这有助于我选择我想要粘贴的范围,因为我知道下一个非NA值正在发生。
有没有人有更好的解决方案?尤其是速度更快的
答案 0 :(得分:1)
首先,我将生成随机数据来测试这个
# generate random data
test_data <- data.frame(x = 1:100, y = rnorm(100))
# add random NAs
test_data$y[sample(1:100, 50)] <- NA
现在试试这个:
# locate non NAs in the wanted column
not_na <- which(!is.na(test_data$y))
# define the function replace_NAs_custom
replace_NAs_custom <- function(i, col){
if(is.na(col[i])){
col[i] <- col[max(not_na[not_na < i] )]
}
return(col[i] )
}
test_data$y_2 <- unlist(lapply(1:nrow(test_data), replace_NAs_custom, test_data$y))
答案 1 :(得分:1)
看起来你的代码在每次循环时都会进行大量的计算和内存分配。为了减少时间,我们希望减少循环每次迭代的工作量。
我不是100%明确你的问题,但我认为我已经掌握了它的要点。听起来您只想获取最后一个非NA值并将其复制到具有NA值的行中。我们可以使用一对或多个索引来执行此操作。
在以下方法中,在进入循环之前,所有内存都已预先分配。唯一的内存操作是将值(NA)替换为另一个值。除了该操作之外,还检查该值是否为NA并且对索引进行了加法运算。为了更快地解决这个问题,您需要使用c优化的向量函数(可能来自包/库)。
使用前一个值填充NA:
# Fill with previous non-NA value
VERINDEX_VEC <- c(NA,"A1","A2",NA,NA,"A3",NA)
VERINDEX_VEC
# [1] NA "A1" "A2" NA NA "A3" NA
non_na_positions <- which(!is.na(VERINDEX_VEC))
# If the first value is NA we need to fill with NA until we hit a known value...
if(is.na(VERINDEX_VEC[1])){
non_na_positions <- c(NA,non_na_positions)
}
index = 1
for(i in 1:length(VERINDEX_VEC)){
if(is.na(VERINDEX_VEC[i])) {
VERINDEX_VEC[i] <- VERINDEX_VEC[non_na_positions[index]]
} else {
index <- index + 1
}
}
VERINDEX_VEC
# [1] NA "A1" "A2" "A2" "A2" "A3" "A3"
使用下一个值填充NA:
# Fill with next non-NA Value
VERINDEX_VEC <- c(NA,"A1","A2",NA,NA,"A3",NA)
VERINDEX_VEC
# [1] NA "A1" "A2" NA NA "A3" NA
non_na_positions <- which(!is.na(VERINDEX_VEC))
# Never need the first position of the vector if we are looking-ahead...
index <- ifelse(non_na_positions[1]==1,2,1)
for(i in 1:length(VERINDEX_VEC)){
if(is.na(VERINDEX_VEC[i])) {
VERINDEX_VEC[i] <- VERINDEX_VEC[non_na_positions[index]]
} else {
index <- index + 1
}
}
VERINDEX_VEC
# [1] "A1" "A1" "A2" "A3" "A3" "A3" NA
答案 2 :(得分:0)
我相信我可能找到了一种更快的方式,至少比我的上一个答案快得多,但是我无法将它与你的代码进行比较,因为我无法重现输出。
(参见下面的基准测试结果)
你可以试试这个:
set.seed(223)
# generate random data
test_data <- data.frame(x = 1:1000, y = rnorm(1000))
# add random NAs
test_data$y[sample(1:1000, 500)] <- NA
# which records are filled
not_na <- which(!is.na(test_data$y))
# calculate the distance from the previous filled value
# this is to identify how many times should each value be repeated
dist <- unlist(lapply(1:(length(not_na) - 1),
function(i){
not_na[i+1] - not_na[i]
}))
# compine both to create a kind of "look-up table"
not_na <- data.frame(idx = not_na,
rep_num = c(dist, nrow(test_data) - not_na[length(not_na)] + 1))
test_data$y_3 <- unlist(lapply(1:nrow(not_na),
function(x){
rep(test_data[not_na$idx[x], "y"], times = not_na$rep_num[x])
}))
基准测试:
f1()
是最后的答案
f2()
就是这个答案
对于test_data中的100.000行
# microbenchmark(f1(), times = 10)
# Unit: seconds
# expr min lq mean median uq max neval
# f1() 39.54495 39.72853 40.38092 40.7027 40.76339 41.29006 10
# microbenchmark(f2(), times = 10)
# Unit: seconds
# expr min lq mean median uq max neval
# f2() 1.578852 1.610565 1.666488 1.645821 1.736301 1.755673 10
对于1.000.000行,新方法需要大约16秒
# microbenchmark(f2(), times = 1)
# Unit: seconds
# expr min lq mean median uq max neval
# f2() 16.33777 16.33777 16.33777 16.33777 16.33777 16.33777 1