Pulizia dei valori "Inf" da un dataframe R.


101

In R, ho un'operazione che crea alcuni Infvalori quando trasformo un dataframe.

Vorrei trasformare questi Infvalori in NAvalori. Il codice che ho è lento per i dati di grandi dimensioni, esiste un modo più veloce per farlo?

Diciamo che ho il seguente dataframe:

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

Quanto segue funziona in un unico caso:

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

Quindi l'ho generalizzato con il seguente ciclo

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

Ma non penso che questo stia davvero usando la potenza di R.

Risposte:


119

opzione 1

Usa il fatto che a data.frameè un elenco di colonne, quindi usa do.callper ricreare un file data.frame.

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

Opzione 2 -- data.table

Potresti usare data.tablee set. Ciò evita alcune copie interne.

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

O usando i numeri di colonna (possibilmente più veloce se ci sono molte colonne):

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

Tempi

# 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è il più veloce. L'uso sapplyrallenta le cose notevolmente.


1
Ottimo lavoro sui tempi e sulla modifica @mnel. Vorrei che ci fosse un modo COSÌ per trasferire il rappresentante tra gli account. Penso che uscirò e voterò altre tue risposte.
IRTFM

errore in do.call (train, lapply (train, function (x) replace (x, is.infinite (x),: 'what' must be a character string or a function
Hack-R

60

Usa sapplyeis.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

Oppure puoi usare (dando credito a @mnel, la cui modifica è),

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

che è significativamente più veloce.


5
Il "trucco" stava nel rendersi conto is.na<-che non avrebbe accettato un risultato da lapplyma ne avrebbe accettato uno da sapply.
IRTFM

Ho aggiunto alcune tempistiche. Non sono sicuro del motivo per cui la is.na<-soluzione sia molto più lenta.
mnel

un po 'di profilazione e ho modificato la tua soluzione per essere molto più veloce.
mnel

19

[<-with mapplyè un po 'più veloce di sapply.

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

Con i dati di Mnel, il tempismo è

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

11

Ecco una soluzione dplyr / tidyverse che utilizza la funzione na_if () :

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

Nota che questo sostituisce solo l'infinito positivo con NA. È necessario ripetere se è necessario sostituire anche i valori di infinito negativo.

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

5

C'è una soluzione molto semplice a questo problema nel pacchetto hablar:

library(hablar)

dat %>% rationalize()

Che restituiscono un frame di dati con tutte le Inf vengono convertiti in NA.

Tempistiche rispetto ad alcune soluzioni sopra. Codice: 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))

Risultato:

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

Sembra che data.table sia più veloce di hablar. Ma ha una sintassi più lunga.


Tempi per favore?
ricardo

@ricardo ha aggiunto alcuni tempi
davsjob

1

Feng Mai ha una risposta ordinata sopra per ottenere infiniti negativi e positivi:

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

Funziona bene, ma una parola di avvertimento è di non scambiare qui abs (.) Per eseguire entrambe le righe contemporaneamente, come proposto in un commento con voto positivo. Sembrerà che funzioni, ma cambia tutti i valori negativi nel set di dati in positivi! Puoi confermare con questo:

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

Per una riga, funziona:

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

1
Buona pesca! Ho aggiunto un commento a questo effetto sul commento originale: penso che sia un posto migliore per affrontare il problema rispetto a una nuova risposta. Ho anche trovato alcuni tuoi post degni di voti positivi per avvicinarti un po 'alla reputazione 50 necessaria per commentare ovunque.
Gregor Thomas,

Grazie! Sì, avrei lasciato un commento se fossi stato in grado.
Mark E.

0

Un'altra soluzione:

    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, perché dovresti modificare la mia risposta ma non aggiungere la tua soluzione? C'è già il pulsante "aggiungi un'altra risposta"!
Studente

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.