web-dev-qa-db-fra.com

Nettoyage des valeurs `Inf` d'une image R

Dans R, j'ai une opération qui crée des valeurs Inf lorsque je transforme une image de données. 

Je voudrais transformer ces valeurs Inf en valeurs NA. Le code que j'ai est lent pour les données volumineuses, existe-t-il un moyen plus rapide de le faire? 

Disons que j'ai le dataframe suivant: 

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

Ce qui suit fonctionne dans un seul cas: 

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

Alors je l'ai généralisé avec la boucle suivante

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

Mais je ne pense pas que cela utilise vraiment le pouvoir de R. 

83
ricardo

Option 1

Utilisez le fait qu'un data.frame est une liste de colonnes, puis utilisez do.call pour recréer un data.frame.

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

Option 2 - data.table

Vous pouvez utiliser data.table et set. Cela évite certaines copies internes.

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

Ou en utilisant des numéros de colonne (éventuellement plus rapidement s'il y a beaucoup de colonnes):

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

Les horaires

# 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 est le plus rapide. Utiliser sapply ralentit considérablement les choses.

103
mnel

Utilisez sapply et 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

Ou vous pouvez utiliser (en donnant crédit à @mnel, dont il s'agit),

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

ce qui est nettement plus rapide. 

52
42-

[<- avec mapply est un peu plus rapide que sapply.

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

Avec les données de Mnel, le timing est

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

Il y a une solution très simple à ce problème dans le paquetage hablar:

library(hablar)

dat %>% rationalize()

Ceux qui retournent une trame de données avec tous les Inf sont convertis en NA.

Timings par rapport à certaines des solutions ci-dessus. Code: Bibliothèque (hablar) Bibliothèque (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))

Résultat:

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

On dirait que data.table est plus rapide que hablar. Mais a une syntaxe plus longue.

3
davsjob

Vous pouvez également utiliser la fonction pratique replace_na: https://tidyr.tidyverse.org/reference/replace_na.html

0
Gang Su

Une autre solution:

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

Voici une solution dplyr/tidyverse utilisant la fonction na_if () :

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

Notez que cela ne remplace que l'infini positif par NA. Besoin de répéter si les valeurs infinies négatives doivent également être remplacées. 

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

Voici une autre solution de base R avec rapply qui surpasse légèrement la configuration de référence de set dans @ mnel de data.table.

dat <- data.frame(a = c(1, Inf), b = c(Inf, 3), d = c("a", "b"))
rapply(dat, f = function(x) replace(x, is.infinite(x), NA), classes = "numeric", how = "replace")
#>    a  b d
#> 1  1 NA a
#> 2 NA  3 b

Des repères

library(data.table) #v1.12.2
getDTthreads()
#> [1] 4

## rapply approach
replace_inf_rapply <- function(dat) {
  rapply(dat, function(x) replace(x, is.infinite(x), NA), classes = "numeric", how = "replace")
}

## data.table approach
replace_inf_dt <- function(dat) {
  setDT(dat)
  for (j in 1:ncol(dat)) set(dat, which(is.infinite(dat[[j]])), j, NA)
  dat
}

## direct subsetting
replace_inf_index <- function(dat) {
  dat[dat == Inf] <- NA
  dat
}

## benchmarks several data.frame sizes
bnch <- bench::press(
    df_nrows = c(100, 1E4, 1E6),
    {
      dat <- data.frame(a = rep(c(1,Inf), df_nrows), b = rep(c(Inf,2), df_nrows), 
          c = rep(c('a','b'), df_nrows),d = rep(c(1,Inf), df_nrows),  
          e = rep(c(Inf,2), df_nrows))
      bench::mark(
          data.table = replace_inf_dt(dat),
          rapply = replace_inf_rapply(dat),
          index = replace_inf_index(dat)
      )
    }  
)
bnch
#> # A tibble: 9 x 7
#>   expression df_nrows      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>    <dbl> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 data.table      100   74.6µs   99.9µs   9922.    609.91KB    15.3 
#> 2 rapply          100   18.4µs     21µs  45179.      6.66KB    13.6 
#> 3 index           100  112.5µs    137µs   6997.    320.59KB    11.0 
#> 4 data.table    10000  305.2µs  421.4µs   2309.      1.01MB    80.3 
#> 5 rapply        10000  202.3µs  222.7µs   4384.    625.41KB   102.  
#> 6 index         10000  917.4µs  982.6µs    968.      1.64MB    41.7 
#> 7 data.table  1000000   24.6ms   29.2ms     29.7     99.2MB    29.7 
#> 8 rapply      1000000   14.7ms   20.5ms     48.4    61.04MB    32.9 
#> 9 index       1000000    116ms  151.7ms      6.46   152.6MB     9.69

0
Joris Chau