从R数据帧清除`Inf`值

ric*_*rdo 95 r dataframe data.table

在R中,我有一个操作,Inf当我转换数据帧时会创建一些值.

我想将这些Inf值转换为NA值.我的代码对于大数据来说速度慢,有没有更快的方法呢?

说我有以下数据帧:

dat <- data.frame(a=c(1, Inf), b=c(Inf, 3), d=c("a","b"))
Run Code Online (Sandbox Code Playgroud)

以下适用于单个案例:

 dat[,1][is.infinite(dat[,1])] = NA
Run Code Online (Sandbox Code Playgroud)

所以我用以下循环推广它

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

但我不认为这是真正使用R的力量.

mne*_*nel 111

选项1

使用a data.frame是列表列表的事实,然后用于do.call重新创建a data.frame.

do.call(data.frame,lapply(DT, function(x) replace(x, is.infinite(x),NA)))
Run Code Online (Sandbox Code Playgroud)

选项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)))
Run Code Online (Sandbox Code Playgroud)

或者使用列号(如果有很多列,可能会更快):

for (j in 1:ncol(DT)) set(DT, which(is.infinite(DT[[j]])), j, NA)
Run Code Online (Sandbox Code Playgroud)

计时

# 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 
Run Code Online (Sandbox Code Playgroud)

data.table是最快的.使用sapply会显着降低速度.


42-*_*42- 57

使用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
Run Code Online (Sandbox Code Playgroud)

或者你可以使用(归功于@mnel,其编辑是这样的),

> is.na(dat) <- do.call(cbind,lapply(dat, is.infinite))
Run Code Online (Sandbox Code Playgroud)

这明显更快.

  • "诀窍"在于意识到`is.na <-`不会接受来自`lapply`的结果,但会接受来自`sapply`的结果. (5认同)

Ric*_*ven 18

[<-与... mapply相比有点快sapply.

> dat[mapply(is.infinite, dat)] <- NA
Run Code Online (Sandbox Code Playgroud)

根据mnel的数据,时间是

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


Fen*_*Mai 7

这是使用na_if()函数的dplyr / tidyverse解决方案:

dat %>% mutate_if(is.numeric, list(~na_if(., Inf)))
Run Code Online (Sandbox Code Playgroud)

请注意,这仅用NA替代正无穷大。如果还需要替换负无穷大值,则需要重复。

dat %>% mutate_if(is.numeric, list(~na_if(., Inf))) %>% 
  mutate_if(is.numeric, list(~na_if(., -Inf)))
Run Code Online (Sandbox Code Playgroud)

  • 使用新的“across”函数,现在可以在单个“mutate”调用中进行:“mutate(across(where(is.numeric), ~na_if(., Inf)), across(where(is.numeric) , ~na_if(., -Inf)))` (3认同)
  • 取绝对值~~ na_if(abs(。),Inf)`一次捕获两个无限性 (2认同)

dav*_*job 6

这个问题在 hablar 包中有一个非常简单的解决方案:

library(hablar)

dat %>% rationalize()
Run Code Online (Sandbox Code Playgroud)

其中返回的数据帧与所有 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))
Run Code Online (Sandbox Code Playgroud)

结果:

> 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 
> 
Run Code Online (Sandbox Code Playgroud)

似乎 data.table 比 hablar 快。但有更长的语法。