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


101

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

これらのInf価値観をNA価値観に変えていきたい。私が持っているコードは大きなデータに対して遅いですが、これを行うより速い方法はありますか?

次のデータフレームがあるとします。

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

以下は1つのケースで機能します。

 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の力を使用しているとは思いません。

回答:


119

オプション1

a 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すると物事が著しく遅くなります。


1
タイミングと@mnelの変更に関するすばらしい作業。アカウント間で担当者を転送するためのSOの方法があったらいいのにと思います。私は出て行き、あなたの他のいくつかの答えに賛成票を投じると思います。
IRTFM 2012

do.call(train、lapply(train、function(x)replace(x、is.infinite(x))のエラー: 'what'は文字列または関数でなければなりません
Hack-R

60

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

これは大幅に高速です。


5
「トリック」はis.na<-からの結果を受け入れないlapplyがからの結果を受け入れることを実現することでしたsapply
IRTFM 2012

タイミングをいくつか追加しました。なぜis.na<-解決が非常に遅いのかわかりません。
mnel 2012

少しプロファイリングし、私はあなたのソリューションをより速くなるように編集しました。
mnel 2012

19

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

11

以下は、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)))

5

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より速いようです。ただし、構文が長くなります。


タイミングはどうですか?
リカルド2018年

@ricardoがタイミングを追加しました
davsjob

1

風水舞は、負の無限大と正の無限大を取得するための上記の整然とした答えを持っています:

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

これは適切に機能しますが、警告されたコメントで提案されているように、ここでabs(。)を入れ替えて両方の行を一度に実行することはできません。動作するように見えますが、データセット内のすべての負の値が正に変更されます!これで確認できます:

data(iris)
#The last line here is bad - it converts all negative values to positive
iris %>% 
  mutate_if(is.numeric, ~scale(.)) %>%
  mutate(infinities = Sepal.Length / 0) %>%
  mutate_if(is.numeric, list(~na_if(abs(.), Inf)))

これは1行で機能します。

  mutate_if(is.numeric, ~ifelse(abs(.) == Inf,NA,.))

1
良いキャッチ!元のコメントに対するこの影響にコメントを追加しました。問題を解決するには、新しい回答よりも適切な場所だと思います。また、コメントを投稿するのに必要な50の評判に少し近づくために、賛成票に値する投稿がいくつか見つかりました。
Gregor Thomas

ありがとう!はい、できればコメントを残しておきます。
マークE.

0

別の解決策:

    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

MusTheDataGuy、なぜ私の答えを編集して独自のソリューションを追加しないのですか?「別の回答を追加」ボタンがすでにあります!
学生

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.