Заполните NA предыдущим доступным значением не-NA - PullRequest
0 голосов
/ 29 мая 2018

Я пытаюсь заполнить значения NA предыдущими доступными значениями, отличными от NA, в наборе данных из 3 миллионов строк.На данный момент я могу сделать это, но это занимает около 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.

У кого-нибудь есть лучшее решение?в частности, более быстрый

Ответы [ 3 ]

0 голосов
/ 29 мая 2018

Похоже, ваш код выполняет много вычислений и выделяет память каждый раз, когда он зацикливается.Чтобы уменьшить время, мы хотим уменьшить объем работы, выполняемой циклом на каждой итерации.

Я не на 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  
0 голосов
/ 01 июня 2018

Полагаю, я мог бы найти более быстрый путь, по крайней мере, намного быстрее, чем мой последний ответ, однако я не смог сравнить его с вашим кодом, поскольку не смог воспроизвести вывод.

(см. ниже результаты сравнительного анализа)

Можете ли вы попробовать это:

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() - это ответ

  • Для 100.000 строк в test_data

    # 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
    
0 голосов
/ 29 мая 2018

Сначала я сгенерирую случайные данные, чтобы проверить это

# 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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...