Membersihkan nilai `Inf` dari kerangka data R.

101

Di R, saya memiliki operasi yang membuat beberapa Infnilai ketika saya mengubah kerangka data.

Saya ingin mengubah Infnilai - nilai ini menjadi NAnilai. Kode yang saya miliki lambat untuk data besar, apakah ada cara yang lebih cepat untuk melakukan ini?

Katakanlah saya memiliki dataframe berikut:

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

Pekerjaan berikut dalam satu kasus:

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

Jadi saya menggeneralisasikannya dengan loop berikut

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

Tapi saya rasa ini tidak benar-benar menggunakan kekuatan R.

Ricardo
sumber

Jawaban:

119

Pilihan 1

Gunakan fakta bahwa a data.frameadalah daftar kolom, lalu gunakan do.calluntuk membuat kembali a data.frame.

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

Pilihan 2 -- data.table

Anda bisa menggunakan data.tabledan set. Ini menghindari beberapa penyalinan internal.

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

Atau menggunakan nomor kolom (mungkin lebih cepat jika ada banyak kolom):

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

Pengaturan waktu

# 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.tableadalah yang tercepat. Menggunakan sapplymemperlambat segalanya secara nyata.

mnel
sumber
1
Kerja bagus pada pengaturan waktu dan modifikasi @mnel. Saya berharap ada cara SO untuk mentransfer perwakilan lintas akun. Saya pikir saya akan keluar dan memberikan suara positif untuk beberapa jawaban Anda yang lain.
IRTFM
kesalahan dalam do.call (train, lapply (train, function (x) replace (x, is.infinite (x),: 'what' harus berupa string karakter atau fungsi
Hack-R
60

Gunakan sapplydanis.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

Atau Anda dapat menggunakan (memberikan kredit kepada @mnel, yang hasil editnya ini),

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

yang jauh lebih cepat.

IRTFM
sumber
5
"Trik" adalah menyadari is.na<-tidak akan menerima hasil dari lapplytetapi akan menerima satu dari sapply.
IRTFM
Saya telah menambahkan beberapa pengaturan waktu. Saya tidak yakin mengapa is.na<-solusinya jauh lebih lambat.
mnel
sedikit profil, dan saya telah mengedit solusi Anda agar lebih cepat.
mnel
19

[<-dengan mapplysedikit lebih cepat dari sapply.

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

Dengan data mnel, waktunya adalah

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

Berikut adalah solusi dplyr / tidyverse menggunakan fungsi na_if () :

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

Perhatikan bahwa ini hanya menggantikan tak terhingga positif dengan NA. Perlu diulang jika nilai tak terhingga negatif juga perlu diganti.

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

Ada solusi yang sangat sederhana untuk masalah ini di paket hablar:

library(hablar)

dat %>% rationalize()

Yang mengembalikan bingkai data dengan semua Inf diubah menjadi NA.

Pengaturan waktu dibandingkan dengan beberapa solusi di atas. Kode: 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))

Hasil:

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

Sepertinya data.table lebih cepat dari hablar. Tetapi memiliki sintaks yang lebih panjang.

davsjob
sumber
Timing tolong?
ricardo
@ricardo menambahkan beberapa pengaturan waktu
davsjob
1

Feng Mai memiliki jawaban yang rapi di atas untuk mendapatkan infinitas negatif dan positif:

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

Ini berfungsi dengan baik, tetapi peringatan untuk tidak menukar in abs (.) Di sini untuk melakukan kedua baris sekaligus seperti yang diusulkan dalam komentar yang diberi suara positif. Ini akan terlihat seperti berfungsi, tetapi mengubah semua nilai negatif dalam kumpulan data menjadi positif! Anda dapat mengonfirmasi dengan ini:

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

Untuk satu baris, ini berfungsi:

  mutate_if(is.numeric, ~ifelse(abs(.) == Inf,NA,.))
Mark E.
sumber
1
Tangkapan yang bagus! Saya telah menambahkan komentar untuk pengaruh ini pada komentar asli - Saya pikir itu adalah tempat yang lebih baik untuk mengatasi masalah daripada jawaban baru. Juga menemukan beberapa kiriman Anda yang layak mendapat suara positif untuk membawa Anda sedikit lebih dekat ke 50 reputasi yang diperlukan untuk berkomentar di mana saja.
Gregor Thomas
Terima kasih! Ya, saya akan meninggalkan komentar jika saya bisa.
Mark E.
0

Solusi lain:

    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
Siswa
sumber
MusTheDataGuy, mengapa Anda mengedit jawaban saya tetapi tidak menambahkan solusi Anda sendiri? Sudah ada tombol "tambahkan jawaban lain"!
Pelajar