web-dev-qa-db-ger.com

Reinigen von "Inf" -Werten aus einem R-Datenrahmen

In R habe ich eine Operation, die einige Inf-Werte erstellt, wenn ich einen Datenrahmen transformiere. 

Ich möchte diese Inf-Werte in NA-Werte umwandeln. Mein Code ist langsam für große Daten. Gibt es einen schnelleren Weg, dies zu tun? 

Angenommen, ich habe den folgenden Datenrahmen: 

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

Folgendes funktioniert in einem einzigen Fall: 

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

Also verallgemeinerte ich es mit folgender Schleife

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

Aber ich glaube nicht, dass dies die Kraft von R wirklich nutzt. 

83
ricardo

Option 1

Verwenden Sie die Tatsache, dass ein data.frame eine Liste von Spalten ist, und verwenden Sie do.call, um einen data.frame neu zu erstellen.

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

Option 2 - data.table

Sie können data.table und set verwenden. Dadurch werden einige interne Kopien vermieden.

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

Oder mit Spaltennummern (möglicherweise schneller, wenn viele Spalten vorhanden sind):

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

Timings

# 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 ist am schnellsten. Die Verwendung von sapply verlangsamt die Arbeit merklich.

103
mnel

Verwenden Sie sapply und 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

Oder Sie können verwenden (gebührt @mnel, dessen Bearbeitung dies ist),

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

das ist deutlich schneller. 

52
42-

[<- mit mapply ist etwas schneller als sapply.

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

Mit den Daten von mnel ist das Timing

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

Im hablar-Paket gibt es eine sehr einfache Lösung für dieses Problem:

library(hablar)

dat %>% rationalize()

Die einen Datenrahmen mit allen Inf zurückgeben, werden in NA umgewandelt.

Timings im Vergleich zu einigen der oben genannten Lösungen. Code: Bibliothek (hablar) Bibliothek (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))

Ergebnis:

> 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 
> 

Scheint, als wäre data.table schneller als hablar. Hat aber längere Syntax.

3
davsjob

Sie können auch die praktische Funktion replace_na verwenden: https://tidyr.tidyverse.org/reference/replace_na.html

0
Gang Su

Eine andere Lösung:

    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
0
Student

Hier ist eine dplyr/tidyverse-Lösung, die die Funktion na_if () verwendet :

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

Beachten Sie, dass dies nur die positive Unendlichkeit durch NA ersetzt. Muss wiederholt werden, wenn auch negative Unendlichwerte ersetzt werden müssen. 

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

Hier ist eine weitere Basis-R-Lösung mit rapply, die die set von data.table im @ mnel-Benchmark-Setup leicht übertrifft.

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

Benchmarks

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

0
Joris Chau