[英]Fill NAs with Previous non-NA available value R
我正在嘗試使用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值發生時。
有誰有更好的解決方案? 特別是更快的
首先,我將生成隨機數據以對此進行測試
# 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))
看起來您的代碼每次循環都在執行大量計算和內存分配。 為了減少時間,我們想減少循環執行每次迭代的工作量。
對於您的問題,我不是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
我相信我可能找到了一種更快的方法,至少比上一個答案要快得多,但是由於我無法重現輸出,因此我無法將其與您的代碼進行比較。
(請參閱下面的基准化結果)
你可以嘗試一下:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.