【问题标题】:Cleaning `Inf` values from an R dataframe从 R 数据框中清除“Inf”值
【发布时间】:2012-08-24 16:30:04
【问题描述】:

在 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 的力量。

【问题讨论】:

    标签: r dataframe data.table


    【解决方案1】:

    选项 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 会明显减慢速度。

    【讨论】:

    • 在时间安排和修改方面做得很好@mnel。我希望有一种跨帐户转移代表的方式。我想我会出去支持你的其他答案。
    • do.call(train, lapply(train, function(x) replace(x, is.infinite(x), : 'what' 必须是字符串或函数) 中的错误
    • 如果您愿意就地替换,那么以下简化将起作用:dat[]
    【解决方案2】:

    使用sapplyis.na&lt;-

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

    这明显更快。

    【讨论】:

    • “诀窍”在于实现is.na&lt;- 不会接受来自lapply 的结果,但会接受来自sapply 的结果。
    • 我添加了一些时间。我不确定为什么 is.na&lt;- 解决方案要慢得多。
    • 一些分析,我已经编辑了您的解决方案以更快。
    【解决方案3】:

    这是使用na_if() function 的 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)))
    

    【讨论】:

      【解决方案4】:

      [&lt;-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 
      

      【讨论】:

        【解决方案5】:

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

        library(hablar)
        
        dat %>% rationalize()
        

        其中返回一个数据框,所有Inf都转换为NA。

        与上述一些解决方案相比的时间。代码: 图书馆(哈布拉) 库(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 快。但语法较长。

        【讨论】:

        • 请时间?
        • @ricardo 添加了一些时间
        【解决方案6】:

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

        【讨论】:

        • 好收获!我已经在原始评论中添加了对此影响的评论——我认为这是一个比新答案更好的解决问题的地方。还发现您的一些帖子值得点赞,让您更接近在任何地方发表评论所需的 50 名声望。
        • 谢谢!是的,如果可以的话,我会留下评论。
        • 你知道为什么它在最后一个代码中不能使用 if_else 而不是 ifelse 吗?
        【解决方案7】:

        在 dplyr 管道链中,您可以这样做。

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

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

        【讨论】:

          【解决方案8】:

          另一种解决方案:

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

          另外,如果有人需要 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]]]))
          }
          

          【讨论】:

            【解决方案10】:

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

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

            【讨论】:

              【解决方案11】:

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

              【讨论】:

              • 这是一个边界线link-only answer。您应该在此处扩展您的答案以包含尽可能多的信息,并仅将链接用作参考。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-11-10
              • 1970-01-01
              • 1970-01-01
              • 2021-05-07
              • 1970-01-01
              • 2017-08-06
              • 2011-12-16
              相关资源
              最近更新 更多