Чистка значений `Inf` из фрейма данных R

В R у меня есть операция, которая создает некоторые значения Inf, когда я преобразую dataframe.

Я хотел бы превратить эти значения Inf в значения NA. Код, который у меня есть, медленный для больших данных, есть ли более быстрый способ сделать это?

Скажем, у меня есть следующий фреймворк данных:

dat <- data.frame(a=c(1, Inf), b=c(Inf, 3), d=c("a","b"))

В одном случае работает следующее:

 dat[,1][is.infinite(dat[,1])] = NA

Итак, я обобщил его с помощью следующего цикла

cf_DFinf2NA <- function(x)
{
    for (i in 1:ncol(x)){
          x[,i][is.infinite(x[,i])] = NA
    }
    return(x)
}

Но я не думаю, что это действительно использует силу R.

Ответ 1

Вариант 1

Используйте тот факт, что data.frame - это список столбцов, затем используйте do.call для воссоздания data.frame.

do.call(data.frame,lapply(DT, function(x) replace(x, is.infinite(x),NA)))

Вариант 2 - data.table

Вы можете использовать data.table и set. Это позволяет избежать некоторого внутреннего копирования.

DT <- data.table(dat)
invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA)))

Или используя номера столбцов (возможно, быстрее, если имеется много столбцов):

for (j in 1:ncol(DT)) set(DT, which(is.infinite(DT[[j]])), j, NA)

Задержки

# some `big(ish)` data
dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                  c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                  e = rep(c(Inf,2), 1e6))
# create data.table
library(data.table)
DT <- data.table(dat)

# replace (@mnel)
system.time(na_dat <- do.call(data.frame,lapply(dat, function(x) replace(x, is.infinite(x),NA))))
## user  system elapsed 
#  0.52    0.01    0.53 

# is.na (@dwin)
system.time(is.na(dat) <- sapply(dat, is.infinite))
# user  system elapsed 
# 32.96    0.07   33.12 

# modified is.na
system.time(is.na(dat) <- do.call(cbind,lapply(dat, is.infinite)))
#  user  system elapsed 
# 1.22    0.38    1.60 


# data.table (@mnel)
system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
# user  system elapsed 
# 0.29    0.02    0.31 

data.table является самым быстрым. Использование sapply заметно замедляет работу.

Ответ 2

Используйте sapply и is.na<-

> dat <- data.frame(a=c(1, Inf), b=c(Inf, 3), d=c("a","b"))
> is.na(dat) <- sapply(dat, is.infinite)
> dat
   a  b d
1  1 NA a
2 NA  3 b

Или вы можете использовать (давая кредит @mnel, чье редактирование это),

> is.na(dat) <- do.call(cbind,lapply(dat, is.infinite))

что значительно быстрее.

Ответ 3

[<- с mapply немного быстрее, чем sapply.

> dat[mapply(is.infinite, dat)] <- NA

С данными mnel время

> system.time(dat[mapply(is.infinite, dat)] <- NA)
#   user  system elapsed 
# 15.281   0.000  13.750 

Ответ 4

Вот решение dplyr/tidyverse с использованием функции na_if():

dat %>% mutate_if(is.numeric, list(~na_if(., Inf)))

Обратите внимание, что это заменяет только положительную бесконечность на NA. Необходимо повторить, если отрицательные значения бесконечности также необходимо заменить.

dat %>% mutate_if(is.numeric, list(~na_if(., Inf))) %>% 
  mutate_if(is.numeric, list(~na_if(., -Inf)))

Ответ 5

В пакете hablar есть очень простое решение этой проблемы:

library(hablar)

dat %>% rationalize()

Которые возвращают фрейм данных со всеми Inf, преобразуются в NA.

Сроки по сравнению с некоторыми выше решениями. Код: библиотека (хаблар) библиотека (data.table)

dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                  c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                  e = rep(c(Inf,2), 1e6))
DT <- data.table(dat)

system.time(dat[mapply(is.infinite, dat)] <- NA)
system.time(dat[dat==Inf] <- NA)
system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
system.time(rationalize(dat))

Результат:

> system.time(dat[mapply(is.infinite, dat)] <- NA)
   user  system elapsed 
  0.125   0.039   0.164 
> system.time(dat[dat==Inf] <- NA)
   user  system elapsed 
  0.095   0.010   0.108 
> system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
   user  system elapsed 
  0.065   0.002   0.067 
> system.time(rationalize(dat))
   user  system elapsed 
  0.058   0.014   0.072 
> 

Похоже, data.table быстрее, чем hablar. Но имеет более длинный синтаксис.

Ответ 6

Другое решение:

    dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                      c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                      e = rep(c(Inf,2), 1e6))
    system.time(dat[dat==Inf] <- NA)

#   user  system elapsed
#  0.316   0.024   0.340

Ответ 7

Вот еще одно базовое решение R с rapply которое немного превосходит data.table, set в настройке бенчмарка @mnel.

dat <- data.frame(a = c(1, Inf), b = c(Inf, 3), d = c("a", "b"))
rapply(dat, f = function(x) replace(x, is.infinite(x), NA), classes = "numeric", how = "replace")
#>    a  b d
#> 1  1 NA a
#> 2 NA  3 b

Ориентиры

library(data.table) #v1.12.2
getDTthreads()
#> [1] 4

## rapply approach
replace_inf_rapply <- function(dat) {
  rapply(dat, function(x) replace(x, is.infinite(x), NA), classes = "numeric", how = "replace")
}

## data.table approach
replace_inf_dt <- function(dat) {
  setDT(dat)
  for (j in 1:ncol(dat)) set(dat, which(is.infinite(dat[[j]])), j, NA)
  dat
}

## direct subsetting
replace_inf_index <- function(dat) {
  dat[dat == Inf] <- NA
  dat
}

## benchmarks several data.frame sizes
bnch <- bench::press(
    df_nrows = c(100, 1E4, 1E6),
    {
      dat <- data.frame(a = rep(c(1,Inf), df_nrows), b = rep(c(Inf,2), df_nrows), 
          c = rep(c('a','b'), df_nrows),d = rep(c(1,Inf), df_nrows),  
          e = rep(c(Inf,2), df_nrows))
      bench::mark(
          data.table = replace_inf_dt(dat),
          rapply = replace_inf_rapply(dat),
          index = replace_inf_index(dat)
      )
    }  
)
bnch
#> # A tibble: 9 x 7
#>   expression df_nrows      min   median 'itr/sec' mem_alloc 'gc/sec'
#>   <bch:expr>    <dbl> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 data.table      100   74.6µs   99.9µs   9922.    609.91KB    15.3 
#> 2 rapply          100   18.4µs     21µs  45179.      6.66KB    13.6 
#> 3 index           100  112.5µs    137µs   6997.    320.59KB    11.0 
#> 4 data.table    10000  305.2µs  421.4µs   2309.      1.01MB    80.3 
#> 5 rapply        10000  202.3µs  222.7µs   4384.    625.41KB   102.  
#> 6 index         10000  917.4µs  982.6µs    968.      1.64MB    41.7 
#> 7 data.table  1000000   24.6ms   29.2ms     29.7     99.2MB    29.7 
#> 8 rapply      1000000   14.7ms   20.5ms     48.4    61.04MB    32.9 
#> 9 index       1000000    116ms  151.7ms      6.46   152.6MB     9.69