web-dev-qa-db-ja.com

Rデータフレームからの `Inf`値のクリーニング

Rでは、データフレームを変換するときにいくつかのInf値を作成する操作があります。

これらの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の力を使っているとは思いません。

94
ricardo

オプション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.tablesetを使用できます。これにより、一部の内部コピーが回避されます。

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を使用すると、処理速度が著しく低下します。

109
mnel

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

これは非常に高速です。

57
42-

[<- with 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 
18
Rich Scriven

na_if()関数 を使用したdplyr/tidyverseソリューションを次に示します。

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)))
6
Feng Mai

Hablarパッケージには、この問題に対する非常に簡単な解決策があります。

library(hablar)

dat %>% rationalize()

すべてのInfを含むデータフレームを返すものはNAに変換されます。

上記のソリューションと比較したタイミング。コード:library(hablar)library(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よりも速いようです。しかし、より長い構文があります。

4
davsjob

また、便利なreplace_na関数を使用することもできます。 https://tidyr.tidyverse.org/reference/replace_na.html

0
Gang Su

別の解決策:

    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

@mnelのベンチマーク設定でdata.tableのrapplyをわずかに上回るsetを持つ別のベースRソリューションを次に示します。

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

0
Joris Chau