Bereinigen von Inf-Werten aus einem R-Datenrahmen


101

In R habe ich eine Operation, die Infbeim Transformieren eines Datenrahmens einige Werte erstellt.

Ich möchte diese InfWerte in NAWerte verwandeln . Der Code, den ich habe, ist für große Datenmengen langsam. Gibt es eine schnellere Möglichkeit, dies zu tun?

Angenommen, ich habe den folgenden Datenrahmen:

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

Folgendes funktioniert in einem Einzelfall:

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

Also habe ich es mit folgender Schleife verallgemeinert

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

Aber ich denke nicht, dass dies wirklich die Kraft von R nutzt.

Antworten:


119

Option 1

Verwenden Sie die Tatsache, dass a data.frameeine Liste von Spalten ist, und do.callerstellen Sie dann a neu data.frame.

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

Option 2 -- data.table

Sie könnten data.tableund verwenden set. Dies vermeidet internes Kopieren.

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

Oder Spaltennummern verwenden (möglicherweise schneller, wenn viele Spalten vorhanden sind):

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

Timings

# 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.tableist das schnellste. Verwenden sapplyverlangsamt die Dinge spürbar.


1
Tolle Arbeit an den Timings und der Modifikation @mnel. Ich wünschte, es gäbe eine SO-Möglichkeit, Repräsentanten über Konten hinweg zu übertragen. Ich denke, ich werde rausgehen und einige andere Antworten von dir positiv bewerten.
IRTFM

Fehler in do.call (train, lapply (train, function (x) replace (x, is.infinite (x) ,: 'what' muss eine Zeichenkette oder eine Funktion sein
Hack-R

60

Verwenden Sie sapplyundis.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

Oder Sie können verwenden (@mnel gutschreiben, dessen Bearbeitung dies ist),

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

das ist deutlich schneller.


5
Der "Trick" bestand darin, zu erkennen is.na<-, dass ein Ergebnis von nicht akzeptiert, lapplyaber eines von akzeptiert werden würde sapply.
IRTFM

Ich habe einige Timings hinzugefügt. Ich bin mir nicht sicher, warum die is.na<-Lösung so viel langsamer ist.
mnel

Ein bisschen Profilerstellung, und ich habe Ihre Lösung so bearbeitet, dass sie viel schneller ist.
mnel

19

[<-mit mapplyist etwas schneller als sapply.

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

Mit den Daten von mnel ist das Timing

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

11

Hier ist eine dplyr / tidyverse-Lösung mit der Funktion na_if () :

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

Beachten Sie, dass dies nur die positive Unendlichkeit durch NA ersetzt. Muss wiederholt werden, wenn auch negative Unendlichkeitswerte ersetzt werden müssen.

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

5

Es gibt eine sehr einfache Lösung für dieses Problem im Hablar-Paket:

library(hablar)

dat %>% rationalize()

Welche einen Datenrahmen mit allen Inf zurückgeben, werden in NA konvertiert.

Timings im Vergleich zu einigen der oben genannten Lösungen. Code: Bibliothek (hablar) Bibliothek (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))

Ergebnis:

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

Scheint, als ob data.table schneller als hablar ist. Hat aber längere Syntax.


Timings bitte?
Ricardo

@ricardo fügte einige Timings hinzu
Davsjob

1

Feng Mai hat oben eine ordentliche Antwort, um negative und positive Unendlichkeiten zu erhalten:

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

Dies funktioniert gut, aber ein Wort der Warnung ist, hier nicht abs (.) Zu tauschen, um beide Zeilen gleichzeitig zu machen, wie in einem positiv bewerteten Kommentar vorgeschlagen. Es sieht so aus, als würde es funktionieren, ändert jedoch alle negativen Werte im Datensatz in positive! Sie können dies bestätigen:

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

Für eine Zeile funktioniert dies:

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

1
Guter Fang! Ich habe diesem Effekt einen Kommentar zum ursprünglichen Kommentar hinzugefügt. Ich denke, dies ist ein besserer Ort, um das Problem zu beheben, als eine neue Antwort. Ich habe auch einige Ihrer Posts gefunden, die es wert sind, positiv bewertet zu werden, um Sie ein wenig näher an die 50 Reputation heranzuführen, die erforderlich sind, um irgendwo Kommentare abzugeben.
Gregor Thomas

Vielen Dank! Ja, ich hätte einen Kommentar hinterlassen, wenn ich dazu in der Lage gewesen wäre.
Mark E.

0

Eine andere Lösung:

    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, warum sollten Sie meine Antwort bearbeiten, aber keine eigene Lösung hinzufügen? Es gibt bereits die Schaltfläche "Weitere Antwort hinzufügen"!
Student

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.