从R数据帧中清除Inf值


101

在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的功能。

Answers:


119

选项1

使用a data.frame是列列表的事实,然后使用do.call重新创建a 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会明显减慢速度。


1
时间安排和@mnel修改方面的出色工作。我希望有一种跨帐户转移代表的方式。我想我会出去投票支持您的其他答案。
IRTFM '08年

do.call(train,lapply(train,function(x)replace(x,is.infinite(x),)中的错误:“ what”必须是字符串或函数
Hack-R

60

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

这明显更快。


5
“诀窍”在于实现is.na<-不会接受的结果,lapply而是会接受的结果sapply
IRTFM '08年

我添加了一些时间。我不确定为什么is.na<-解决方案这么慢。
mnel 2012年

进行一些分析,我对您的解决方案进行了编辑,使其速度更快。
mnel 2012年

19

[<-mapply相比要快一些sapply

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

使用mnel的数据,计时是

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

11

这是使用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)))

5

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快。但是语法更长。


时间合适吗?
里卡多'18

@ricardo添加了一些时间安排
davsjob

1

丰迈在上面给出了一个整洁的答案,以得到负面和正面的无穷大:

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

1
接得好!我已在原始评论中添加了对此评论的评论-我认为这是解决问题的一个比新答案更好的地方。还发现了一些值得赞扬的帖子,使您更接近在任何地方发表评论所需的50个声誉。
格雷戈尔·托马斯

谢谢!是的,如果可以的话,我会发表评论。
Mark E.

0

另一个解决方案:

    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,为什么您要编辑我的答案却不添加自己的解决方案?已经有“添加另一个答案”按钮!
学生

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.