Bir R veri çerçevesinden "Inf" değerlerini temizleme


103

R'de, Infbir veri çerçevesini dönüştürdüğümde bazı değerler oluşturan bir işlemim var.

Bu Infdeğerleri değerlere dönüştürmek isterim NA. Sahip olduğum kod büyük veriler için yavaş, bunu yapmanın daha hızlı bir yolu var mı?

Aşağıdaki veri çerçevesine sahip olduğumu varsayalım:

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

Aşağıdaki tek bir durumda çalışır:

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

Bu yüzden aşağıdaki döngü ile genelleştirdim

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

Ama bunun gerçekten R'nin gücünü kullandığını düşünmüyorum.

Yanıtlar:


121

seçenek 1

A'nın bir data.framesütun listesi olduğu gerçeğini kullanın , ardından do.calla'yı yeniden oluşturmak için kullanın data.frame.

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

Seçenek 2 -- data.table

Sen kullanabilirsiniz data.tableve set. Bu, bazı dahili kopyalamayı önler.

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

Veya sütun numaraları kullanarak (çok sayıda sütun varsa muhtemelen daha hızlıdır):

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

Zamanlamalar

# 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.tableen hızlısıdır. Kullanmak sapplyişleri belirgin şekilde yavaşlatır.


1
Zamanlamalar ve @ mnel değişikliği üzerinde harika iş çıkardınız. Temsilciyi hesaplar arasında aktarmanın SO bir yolu olsaydı. Sanırım dışarı çıkıp diğer bazı cevaplarınıza olumlu oy vereceğim.
IRTFM

do.call'da hata (train, lapply (train, function (x) replace (x, is.infinite (x)),: 'what' must string or a function
Hack-R

60

Kullanım sapplyveis.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

Veya (düzenlemesi olan @ mnel'e atıfta bulunarak),

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

önemli ölçüde daha hızlıdır.


5
"Hile", is.na<-bir sonucu kabul etmeyeceğini, lapplyancak gelen bir sonucu kabul etmekti sapply.
IRTFM

Bazı zamanlamalar ekledim. is.na<-Çözümün neden bu kadar yavaş olduğundan emin değilim .
mnel

biraz profil oluşturma ve çözümünüzü çok daha hızlı olacak şekilde düzenledim.
mnel

19

[<-ile mapplybiraz daha hızlıdır sapply.

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

Mnel verileriyle, zamanlama

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

11

Na_if () işlevini kullanan bir dplyr / tidyverse çözümü :

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

Bunun yalnızca pozitif sonsuzluğu NA ile değiştirdiğini unutmayın. Negatif sonsuz değerlerinin de değiştirilmesi gerekiyorsa tekrarlanması gerekir.

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

5

Hablar paketinde bu soruna çok basit bir çözüm var:

library(hablar)

dat %>% rationalize()

Tüm Inf ile bir veri çerçevesi döndüren, NA'ya dönüştürülür.

Yukarıdaki bazı çözümlere kıyasla zamanlamalar. Kod: kütüphane (hablar) kütüphanesi (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))

Sonuç:

> 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'dan daha hızlı gibi görünüyor. Ancak daha uzun sözdizimi var.


Zamanlamalar lütfen?
ricardo

@ricardo bazı zamanlamaları ekledi
davsjob

1

Feng Mai'nin, olumsuz ve olumlu sonsuzluklar elde etmek için yukarıda düzenli bir yanıtı var:

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

Bu iyi çalışıyor, ancak burada bir uyarı, olumlu oylanan bir yorumda önerildiği gibi her iki satırı aynı anda yapmak için abs (.) Çalışıyor gibi görünecek, ancak veri kümesindeki tüm negatif değerleri pozitif olarak değiştirecek! Bununla onaylayabilirsiniz:

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

Bir satır için bu işe yarar:

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

1
İyi yakalama! Orijinal yoruma bu etkiye bir yorum ekledim - konuyu ele almak için yeni bir cevaptan daha iyi bir yer olduğunu düşünüyorum. Ayrıca, sizi herhangi bir yerde yorum yapmak için gereken 50 itibara biraz daha yaklaştırmak için olumlu oy almaya değer bazı yayınlarınızı da buldum.
Gregor Thomas

Teşekkürler! Evet, yapabilseydim bir yorum bırakırdım.
Mark E.

0

Başka bir çözüm:

    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, neden cevabımı düzenleyip kendi çözümünüzü eklemiyorsunuz? Zaten "başka bir cevap ekle" düğmesi var!
Öğrenci

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.