Замена NA в столбце сгруппированным значением [duplicate]

Похоже, есть еще несколько других, а также DBeaver ..

SQuirreL SQL Client

SQL Workbench / J

Надо сказать, хотя я предпочитают DBeaver, поскольку он загружает необходимые вам драйверы и имеет готовый пакет .deb.

90
задан 22 June 2016 в 14:54

12 ответов

Я попробовал следующее:

nullIdx <- as.array(which(is.na(masterData$RequiredColumn)))
masterData$RequiredColumn[nullIdx] = masterData$RequiredColumn[nullIdx-1]

nullIdx получает номер idx, где когда-либо masterData $ RequiredColumn имеет значение Null / NA. В следующей строке мы заменим его соответствующим значением Idx-1, то есть последним хорошим значением перед каждым NULL / NA

0
ответ дан 15 August 2018 в 17:04
  • 1
    Это не работает, если имеется несколько последовательных пропущенных значений - 1 NA NA превращается в 1 1 NA. Кроме того, я думаю, что as.array() не требуется. – Gregor 20 March 2017 в 03:12

Наличие ведущего NA немного морщин, но я считаю очень читаемым (и векторизованным) способом LOCF, когда главный член не отсутствует:

na.omit(y)[cumsum(!is.na(y))] [ ! d2]

В целом работает немного менее читаемая модификация:

c(NA, na.omit(y))[cumsum(!is.na(y))+1]

дает желаемый результат:

c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)

4
ответ дан 15 August 2018 в 17:04
  • 1
    это довольно элегантно. Не уверен, что он работает во всех случаях, но он наверняка сработал для меня! – ABT 5 February 2018 в 11:31

Бросок моей шляпы в:

library(Rcpp)
cppFunction('IntegerVector na_locf(IntegerVector x) {
  int n = x.size();

  for(int i = 0; i<n; i++) {
    if((i > 0) && (x[i] == NA_INTEGER) & (x[i-1] != NA_INTEGER)) {
      x[i] = x[i-1];
    }
  }
  return x;
}')

Настройте базовый образец и контрольный показатель:

x <- sample(c(1,2,3,4,NA))

bench_em <- function(x,count = 10) {
  x <- sample(x,count,replace = TRUE)
  print(microbenchmark(
    na_locf(x),
    replace_na_with_last(x),
    na.lomf(x),
    na.locf(x),
    repeat.before(x)
  ), order = "mean", digits = 1)
}

И запустите несколько эталонных тестов:

bench_em(x,1e6)

Unit: microseconds
                    expr   min    lq  mean median    uq   max neval
              na_locf(x)   697   798   821    814   821 1e+03   100
              na.lomf(x)  3511  4137  5002   4214  4330 1e+04   100
 replace_na_with_last(x)  4482  5224  6473   5342  5801 2e+04   100
        repeat.before(x)  4793  5044  6622   5097  5520 1e+04   100
              na.locf(x) 12017 12658 17076  13545 19193 2e+05   100

На всякий случай:

all.equal(
     na_locf(x),
     replace_na_with_last(x),
     na.lomf(x),
     na.locf(x),
     repeat.before(x)
)
[1] TRUE

Обновить

Для числового вектора функция немного отличается:

NumericVector na_locf_numeric(NumericVector x) {
  int n = x.size();
  LogicalVector ina = is_na(x);

  for(int i = 1; i<n; i++) {
    if((ina[i] == TRUE) & (ina[i-1] != TRUE)) {
      x[i] = x[i-1];
    }
  }
  return x;
}
12
ответ дан 15 August 2018 в 17:04

Это сработало для меня, хотя я не уверен, что он более эффективен, чем другие предложения.

rollForward <- function(x){
  curr <- 0
  for (i in 1:length(x)){
    if (is.na(x[i])){
      x[i] <- curr
    }
    else{
      curr <- x[i]
    }
  }
  return(x)
}
0
ответ дан 15 August 2018 в 17:04

Существует множество пакетов, предлагающих функции na.locf (NA Last Observation Carried Forward):

xts - xts::na.locf zoo - zoo::na.locf imputeTS - imputeTS::na.locf spacetime - spacetime::na.locf

А также другие пакеты, в которых эта функция называется по-другому.

1
ответ дан 15 August 2018 в 17:04

Следуя за вкладами Брэндона Бертельсена Rcpp. Для меня версия NumericVector не работала: она заменила только первое NA. Это связано с тем, что вектор ina оценивается только один раз в начале функции.

Вместо этого можно использовать тот же подход, что и для функции IntegerVector. Для меня работали следующие:

library(Rcpp)
cppFunction('NumericVector na_locf_numeric(NumericVector x) {
  R_xlen_t n = x.size();
  for(R_xlen_t i = 0; i<n; i++) {
    if(i > 0 && !R_finite(x[i]) && R_finite(x[i-1])) {
      x[i] = x[i-1];
    }
  }
  return x;
}')

Если вам нужна версия CharacterVector, то работает тот же базовый подход:

cppFunction('CharacterVector na_locf_character(CharacterVector x) {
  R_xlen_t n = x.size();
  for(R_xlen_t i = 0; i<n; i++) {
    if(i > 0 && x[i] == NA_STRING && x[i-1] != NA_STRING) {
      x[i] = x[i-1];
    }
  }
  return x;
}')
2
ответ дан 15 August 2018 в 17:04
  • 1
    int n = x.size () и for (int i = 0; i & lt; n; i ++) следует заменить на double. В R вектор может быть больше, чем c ++ int size. – stats0007 19 March 2017 в 01:56
  • 2
    Похоже, эта функция возвращает «R_xlen_t». Если R скомпилировано с длинной векторной поддержкой, это определяется как ptrdiff_t; если это не так, это int. Спасибо за исправление! – Evan Cortens 20 March 2017 в 02:38

Попробуйте эту функцию. Для него не требуется пакет ZOO:

# last observation moved forward
# replaces all NA values with last non-NA values
na.lomf <- function(x) {

    na.lomf.0 <- function(x) {
        non.na.idx <- which(!is.na(x))
        if (is.na(x[1L])) {
            non.na.idx <- c(1L, non.na.idx)
        }
        rep.int(x[non.na.idx], diff(c(non.na.idx, length(x) + 1L)))
    }

    dim.len <- length(dim(x))

    if (dim.len == 0L) {
        na.lomf.0(x)
    } else {
        apply(x, dim.len, na.lomf.0)
    }
}

Пример:

> # vector
> na.lomf(c(1, NA,2, NA, NA))
[1] 1 1 2 2 2
> 
> # matrix
> na.lomf(matrix(c(1, NA, NA, 2, NA, NA), ncol = 2))
     [,1] [,2]
[1,]    1    2
[2,]    1    2
[3,]    1    2
9
ответ дан 15 August 2018 в 17:04
  • 1
    Чтобы улучшить его, вы можете добавить это: if (!anyNA(x)) return(x). – Artem Klevtsov 27 May 2018 в 08:15

Для работы с большим объемом данных, чтобы быть более эффективным, мы можем использовать пакет data.table.

require(data.table)
replaceNaWithLatest <- function(
  dfIn,
  nameColNa = names(dfIn)[1]
){
  dtTest <- data.table(dfIn)
  setnames(dtTest, nameColNa, "colNa")
  dtTest[, segment := cumsum(!is.na(colNa))]
  dtTest[, colNa := colNa[1], by = "segment"]
  dtTest[, segment := NULL]
  setnames(dtTest, "colNa", nameColNa)
  return(dtTest)
}
18
ответ дан 15 August 2018 в 17:04
  • 1
    Может быть добавлено дополнение, которое может непосредственно применить его к нескольким столбцам NA: replaceNaWithLatest <- function( dfIn, nameColsNa = names(dfIn)[1] ){ dtTest <- data.table(dfIn) invisible(lapply(nameColsNa, function(nameColNa){ setnames(dtTest, nameColNa, "colNa") dtTest[, segment := cumsum(!is.na(colNa))] dtTest[, colNa := colNa[1], by = "segment"] dtTest[, segment := NULL] setnames(dtTest, "colNa", nameColNa) })) return(dtTest) } – xclotet 10 January 2017 в 21:28
  • 2
    Сначала я был в восторге от этого решения, но на самом деле он не делал ничего подобного. Речь идет о заполнении 1 набора данных другим. Этот ответ - просто вменение. – Hack-R 10 June 2018 в 23:10

Вот модификация решения @ AdamO. Этот режим работает быстрее, поскольку он обходит функцию na.omit. Это будет перезаписывать значения NA в векторе y (кроме ведущих NA s).

   z  <- !is.na(y)                  # indicates the positions of y whose values we do not want to overwrite
   z  <- z | !cumsum(z)             # for leading NA's in y, z will be TRUE, otherwise it will be FALSE where y has a NA and TRUE where y does not have a NA
   y  <- y[z][cumsum(z)]
0
ответ дан 15 August 2018 в 17:04

Извините, что выкапываете старый вопрос. Я не мог найти функцию, чтобы выполнить эту работу в поезде, поэтому я написал ее сам.

Я с гордостью узнал, что это немного быстрее.

Но он отлично работает с ave, и это то, что мне нужно.

repeat.before = function(x) {   # repeats the last non NA value. Keeps leading NA
    ind = which(!is.na(x))      # get positions of nonmissing values
    if(is.na(x[1]))             # if it begins with a missing, add the 
          ind = c(1,ind)        # first position to the indices
    rep(x[ind], times = diff(   # repeat the values at these indices
       c(ind, length(x) + 1) )) # diffing the indices + length yields how often 
}                               # they need to be repeated

x = c(NA,NA,'a',NA,NA,NA,NA,NA,NA,NA,NA,'b','c','d',NA,NA,NA,NA,NA,'e')  
xx = rep(x, 1000000)  
system.time({ yzoo = na.locf(xx,na.rm=F)})  
## user  system elapsed   
## 2.754   0.667   3.406   
system.time({ yrep = repeat.before(xx)})  
## user  system elapsed   
## 0.597   0.199   0.793   

Edit

Поскольку это стало моим самым Я часто напоминал, что я не использую свою собственную функцию, потому что мне часто нужен аргумент maxgap зоопарка. Потому что у зоопарка есть некоторые странные проблемы в крайних случаях, когда я использую dplyr + даты, которые я не мог отлаживать, я вернулся к этому сегодня, чтобы улучшить свою старую функцию.

Я сравнивал мою улучшенную функцию и все остальные здесь. Для базового набора функций, tidyr::fill является самым быстрым, а также не приводит к краху. Запись Rcpp by @BrandonBertelsen выполняется быстрее, но она негибкая относительно типа ввода (он неправильно проверял случаи кросс-памяти из-за непонимания all.equal).

Если вам нужно maxgap, моя функция ниже быстрее, чем зоопарк (и не имеет странных проблем с датами).

Я представил документацию по моим тестам.

new function

repeat_last = function(x, forward = TRUE, maxgap = Inf, na.rm = FALSE) {
    if (!forward) x = rev(x)           # reverse x twice if carrying backward
    ind = which(!is.na(x))             # get positions of nonmissing values
    if (is.na(x[1]) && !na.rm)         # if it begins with NA
        ind = c(1,ind)                 # add first pos
    rep_times = diff(                  # diffing the indices + length yields how often
        c(ind, length(x) + 1) )          # they need to be repeated
    if (maxgap < Inf) {
        exceed = rep_times - 1 > maxgap  # exceeding maxgap
        if (any(exceed)) {               # any exceed?
            ind = sort(c(ind[exceed] + 1, ind))      # add NA in gaps
            rep_times = diff(c(ind, length(x) + 1) ) # diff again
        }
    }
    x = rep(x[ind], times = rep_times) # repeat the values at these indices
    if (!forward) x = rev(x)           # second reversion
    x
}

Я также поместил эту функцию в мою документацию моего тесты (только Github).

48
ответ дан 15 August 2018 в 17:04
  • 1
    +1, но я предполагаю, что это нужно зацикливать на столбец, если вы хотите применить это к df с несколькими столбцами? – Rhubarb 6 October 2014 в 21:11
  • 2
    @Ruben Еще раз спасибо за ваш отчет. К настоящему времени ошибка исправлена ​​на R-Forge. Кроме того, я изменил и экспортировал функцию рабочей лошади na.locf0, которая теперь похожа по объему и производительности на вашу функцию repeat_last. Ключ должен был использовать diff, а не cumsum и избегать ifelse. Основная функция na.locf.default все еще несколько медленнее, потому что она выполняет несколько проверок и обрабатывает несколько столбцов и т. Д. – Achim Zeileis 2 March 2017 в 19:28

a data.table решение:

> dt <- data.table(y = c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA))
> dt[, y_forward_fill := y[1], .(cumsum(!is.na(y)))]
> dt
     y y_forward_fill
 1: NA             NA
 2:  2              2
 3:  2              2
 4: NA              2
 5: NA              2
 6:  3              3
 7: NA              3
 8:  4              4
 9: NA              4
10: NA              4

этот подход мог бы работать и с нулями прямого заполнения:

> dt <- data.table(y = c(0, 2, -2, 0, 0, 3, 0, -4, 0, 0))
> dt[, y_forward_fill := y[1], .(cumsum(y != 0))]
> dt
     y y_forward_fill
 1:  0              0
 2:  2              2
 3: -2             -2
 4:  0             -2
 5:  0             -2
 6:  3              3
 7:  0              3
 8: -4             -4
 9:  0             -4
10:  0             -4

этот метод очень полезен для данных в масштабе и где вы хотите выполнить форвардную заливку по группам (группам), что тривиально с data.table. просто добавьте группу (ы) в by предложение до логики cumsum.

4
ответ дан 15 August 2018 в 17:04

Это сработало для меня:

  replace_na_with_last<-function(x,a=!is.na(x)){
     x[which(a)[c(1,1:sum(a))][cumsum(a)+1]]
  }


> replace_na_with_last(c(1,NA,NA,NA,3,4,5,NA,5,5,5,NA,NA,NA))

[1] 1 1 1 1 3 4 5 5 5 5 5 5 5 5

> replace_na_with_last(c(NA,"aa",NA,"ccc",NA))

[1] "aa"  "aa"  "aa"  "ccc" "ccc"

скорость тоже разумная:

> system.time(replace_na_with_last(sample(c(1,2,3,NA),1e6,replace=TRUE)))


 user  system elapsed 

 0.072   0.000   0.071 
10
ответ дан 15 August 2018 в 17:04
  • 1
    Эта функция не выполняет то, что вы ожидаете, когда есть ведущие НС. replace_na_with_last(c(NA,1:4,NA)) (т. е. они заполнены следующим значением). Это также поведение по умолчанию imputeTS::na.locf(x, na.remaining = "rev"). – Ruben 12 January 2017 в 22:32

Другие вопросы по тегам:

Похожие вопросы: