簡體   English   中英

從 R 數據框中清除 Inf 值

[英]Cleaning `Inf` values from an R dataframe

在 R 中,我有一個在轉換數據幀時創建一些Inf值的操作。

我想將這些Inf值轉換為NA值。 我的代碼對於大數據很慢,有沒有更快的方法?

假設我有以下數據框:

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

以下在單個案例中起作用:

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

所以我用以下循環概括了它

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

但我不認為這真的是在利用 R 的力量。

選項1

使用data.frame是列列表這一事實,然后使用do.call重新創建data.frame

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

選項 2 -- data.table

您可以使用data.tableset 這避免了一些內部復制。

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

或者使用列號(如果有很多列可能會更快):

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

時間安排

# 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是最快的。 使用sapply會明顯減慢速度。

使用sapplyis.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

或者你可以使用(感謝@mnel,他的編輯是),

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

這是明顯更快。

[<-使用mapplysapply快一點。

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

有了mnel的數據,時間是

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

這是使用na_if() 函數的 dplyr/tidyverse 解決方案:

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

請注意,這僅用 NA 替換正無窮大。 如果還需要替換負無窮大值,則需要重復。

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

在 hablar 包中有一個非常簡單的解決方案:

library(hablar)

dat %>% rationalize()

其中返回的數據幀與所有 Inf 都轉換為 NA。

與上述一些解決方案相比的時間。 代碼:庫(hablar) 庫(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))

結果:

> 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 快。 但有更長的語法。

馮麥上面有一個tidyverse答案來得到負無窮和正無窮:

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

這很有效,但有一個警告是不要在此處交換 abs(.) 以像在贊成的評論中建議的那樣同時執行兩行。 看起來它可以工作,但是將數據集中的所有負值更改為正值! 您可以通過以下方式確認:

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

對於一行,這有效:

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

另一種解決方案:

    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

這里是另一個基礎R解決方案rapply略優於data.table真實set在@ MNEL的基准設置。

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

基准

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

另外,如果有人需要 Infs 的坐標,可以這樣做:

library(rlist)
list.clean(apply(df, 2, function(x){which(is.infinite(x))}), function(x) length(x) == 0L, TRUE)

結果:

$colname1
[1] row1 row2 ...
$colname2
[2] row1 row2 ... 

有了這些信息,您可以用平均值、中位數或您想要的任何運算符替換特定位置的 Inf 值。

例如(對於元素 01):

repInf = list.clean(apply(df, 2, function(x){which(is.infinite(x))}), function(x) length(x) == 0L, TRUE)
df[repInf[[1]], names(repInf)[[1]]] = median or mean(is.finite(df[ ,names(repInf)[[1]]]), na.rm = TRUE)

在循環中:

for (nonInf in 1:length(repInf)) {
df[repInf[[nonInf]], names(repInf)[[nonInf]]] = mean(is.finite(df[ , names(repInf)[[nonInf]]]))
}

在 dplyr 管道鏈中,您可以執行此操作。

%>% mutate_all(.,.funs = function(x){ifelse(is.infinite(x),NA,x)}) %>%

我覺得它簡單、優雅、快速。

已經有很多答案,但想補充一點,對我來說,這個tidyverse解決方案總是很好用:

%>% mutate_all(function(x) ifelse(is.nan(x) | is.infinite(x), NA, x)) %>%

您也可以使用方便的 replace_na 功能: https : //tidyr.tidyverse.org/reference/replace_na.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM