114

Inf在 R 中,我有一个在转换数据框时 创建一些值的操作。

我想把这些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 的力量。

4

11 回答 11

128

选项1

data.frame使用 a是列列表这一事实,然后用于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会显着减慢速度。

于 2012-08-30T01:04:43.167 回答
61

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

这明显更快。

于 2012-08-30T01:12:49.920 回答
22

这是使用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)))
于 2019-01-08T02:29:14.247 回答
19

[<-withmapplysapply.

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

有了mnel的数据,时间是

> system.time(dat[mapply(is.infinite, dat)] <- NA)
#   user  system elapsed 
# 15.281   0.000  13.750 
于 2014-09-18T07:36:31.377 回答
7

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

于 2018-10-31T07:52:06.533 回答
3

Feng Mai 在上面有一个 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,.))
于 2020-04-28T16:39:48.953 回答
1

在 dplyr 管道链中,您可以执行此操作。

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

我觉得它简单、优雅、快速。

于 2021-06-03T21:34:07.040 回答
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
于 2018-09-19T01:52:31.137 回答
0

另外,如果有人需要 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]]]))
}
于 2020-12-20T00:46:00.660 回答
0

已经有很多答案了,但想补充一点,对我来说,这个tidyverse解决方案总是很有效:

%>% mutate_all(function(x) ifelse(is.nan(x) | is.infinite(x), NA, x)) %>%
于 2021-07-09T18:14:03.760 回答
-1

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

于 2018-11-12T21:44:32.933 回答