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の力を使っているとは思いません。
data.frame
が列のリストであるという事実を使用し、do.call
を使用してdata.frame
を再作成します。
do.call(data.frame,lapply(DT, function(x) replace(x, is.infinite(x),NA)))
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
を使用すると、処理速度が著しく低下します。
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))
これは非常に高速です。
[<-
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
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)))
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よりも速いようです。しかし、より長い構文があります。
また、便利なreplace_na関数を使用することもできます。 https://tidyr.tidyverse.org/reference/replace_na.html
別の解決策:
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
@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