【问题标题】:Set certain values to NA with dplyr使用 dplyr 将某些值设置为 NA
【发布时间】:2015-03-10 15:11:48
【问题描述】:

我正在尝试找出一种简单的方法来使用 dplyr (data set = dat, variable = x) 做这样的事情:

day$x[dat$x<0]=NA

应该很简单,但这是我目前能做的最好的。有没有更简单的方法?

dat =  dat %>% mutate(x=ifelse(x<0,NA,x))

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    直接在x 列上使用replace 而不使用mutate 也可以。

    dat$x <- replace(dat$x, dat$x<0, NA)
    

    【讨论】:

      【解决方案2】:

      dplyr 中最自然的方法是使用na_if 函数。

      对于一个变量:

      dat %<>% mutate(x = na_if(x, x < 0))
      

      对于所有变量:

      dat %<>% mutate_all(~ na_if(., . < 0))
      

      如果有兴趣替换特定值,而不是所有变量的范围:

      dat %<>% mutate_all(na_if, 0)
      

      请注意,我使用的是 magrittr 包中的 %&lt;&gt;% 运算符。

      【讨论】:

      • 谢谢,很高兴知道!当我第一次提出问题时,我认为此功能不可用。
      • na_if(x, y) 在这个 y 是包含 x 的条件的示例中似乎不起作用。比较:quakes %&gt;% mutate(depth = na_if(depth, depth &gt; 610)) 不会改变任何东西,但以下会改变:quakes %&gt;% mutate(depth = replace(depth, depth &gt; 610))
      • 正在寻找一个示例,其中我根据其他一些条件将一个变量设置为 NA ...此页面似乎都没有这样做?
      【解决方案3】:

      如果你使用data.table,下面的代码会更快

      library(data.table)
      setDT(dat)[x<0,x:=NA]
      

      基准测试

      使用data.table_1.9.5dplyr_0.3.0.9000

      library(microbenchmark)
      set.seed(285)
      dat <- data.frame(x=sample(-5:5, 1e7, replace=TRUE), y=rnorm(1e7))
      
      dtbl1 <- function() {as.data.table(dat)[x<0,x:=NA]}
      dplr1 <- function() {dat %>% mutate(x = replace(x, x<0, NA))}
      
      microbenchmark(dtbl1(), dplr1(), unit='relative', times=20L)
      #Unit: relative
      #expr     min       lq     mean   median       uq      max neval cld
      #dtbl1() 1.00000 1.000000 1.000000 1.000000 1.000000 1.000000    20  a 
      #dplr1() 2.06654 2.064405 1.927762 1.795962 1.881821 1.885655    20   b
      

      更新的基准

      使用data.table_1.9.5dplyr_0.4.0。我使用了一个稍大的数据集并将as.data.table 替换为setDT(还包括@Sven Hohenstein 的更快功能。)

      set.seed(285)
      dat <- data.frame(x=sample(-5:5, 1e8, replace=TRUE), y=rnorm(1e8))
      dat1 <- copy(dat)
      dtbl1 <- function() {setDT(dat)[x<0,x:=NA]}
      dplr1 <- function() {dat1 %>% mutate(x = replace(x, x<0, NA))}
      dplr2 <- function() {dat1 %>% mutate(x = NA ^ (x < 0) * x)} 
      
      microbenchmark(dtbl1(), dplr1(), dplr2(), unit='relative', times=20L)
      #Unit: relative
      #  expr      min       lq     mean   median       uq      max neval cld
      #dtbl1() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    20  a 
      #dplr1() 2.523945 2.542412 2.536255 2.579379 2.518336 2.486757    20   b
      #dplr2() 1.139216 1.089992 1.088753 1.058653 1.093906 1.100690    20  a 
      

      更新的基准2

      应@docendo discimus 的要求,使用data.table_1.9.5dplyr_0.4.0 再次对他的dplyr“新”版本进行基准测试。

      注意:因为@docendo discimus 代码有变化,我把data.table`的0改成了0L

      set.seed(285)
      dat <- data.frame(x=sample(-5:5, 1e8, replace=TRUE), y=rnorm(1e8))
      dat1 <- copy(dat)
      dtbl1 <- function() {setDT(dat)[x<0L, x:= NA]}
      dplr1 <- function() {dat1 %>% mutate(x = replace(x, which(x<0L), NA))}
      dplr2 <- function() {dat1 %>% mutate(x = NA ^ (x < 0) * x)} 
      
      microbenchmark(dtbl1(), dplr1(), dplr2(), unit='relative', times=20L)
      #Unit: relative
      #expr      min       lq     mean   median       uq      max neval cld
      #dtbl1() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    20 a  
      #dplr1() 2.186055 2.183432 2.142293 2.222458 2.194450 1.442444    20  b 
      #dplr2() 2.919854 2.925795 2.852528 2.942700 2.954657 1.904249    20   c
      

      数据

      set.seed(24)
      dat <- data.frame(x=sample(-5:5, 25, replace=TRUE), y=rnorm(25))
      

      【讨论】:

      • 在我的电脑上,dplyr 快一点。我有data.table_1.9.5dplyr_0.4.0
      • @Khashaa 我使用了data.table_1.9.5dplyr_0.3.0.9000。所以,可能存在版本差异。
      • data.table_1.9.4dplyr_0.3.0.2 与 @Akrun 的结果相似。然后我升级到dplyr_0.4.0dplyr 仍然快 2 倍。
      • @Arun 感谢您的评论。经过一番思考,我得出的结论是,这可能是因为我针对 dplyr 特定问题发布了一个 data.table 解决方案。
      • @akrun,它也被标记为“r”。他们没有写笔记的事实已经说明了很多。就是这样。不是 dplyr/data.table 论坛。
      【解决方案4】:

      你可以使用replace,比ifelse快一点:

      dat <-  dat %>% mutate(x = replace(x, x<0, NA))
      

      您可以通过使用whichreplace 提供索引来加快速度:

      dat <- dat %>% mutate(x = replace(x, which(x<0L), NA))
      

      在我的机器上,这将时间缩短到三分之一,见下文。

      以下是不同答案的一些比较,当然这只是指示性的:

      set.seed(24)
      dat <- data.frame(x=rnorm(1e6))
      system.time(dat %>% mutate(x = replace(x, x<0, NA)))
             User      System     elapsed
             0.03        0.00        0.03 
      system.time(dat %>% mutate(x=ifelse(x<0,NA,x)))
             User      System     elapsed
             0.30        0.00        0.29 
      system.time(setDT(dat)[x<0,x:=NA])
             User      System     elapsed
             0.01        0.00        0.02 
      system.time(dat$x[dat$x<0] <- NA)
             User      System     elapsed
             0.03        0.00        0.03 
      system.time(dat %>% mutate(x = "is.na<-"(x, x < 0)))
             User      System     elapsed
             0.05        0.00        0.05 
      system.time(dat %>% mutate(x = NA ^ (x < 0) * x))
             User      System     elapsed
             0.01        0.00        0.02 
      system.time(dat %>% mutate(x = replace(x, which(x<0), NA)))
             User      System     elapsed
             0.01        0.00        0.01 
      

      (我正在使用 dplyr_0.3.0.2 和 data.table_1.9.4)


      由于我们一直对基准测试非常感兴趣,尤其是在 data.table-vs-dplyr 讨论过程中,我使用 microbenchmark 和 akrun 的数据提供了 3 个答案的另一个基准测试。请注意,我将dplyr1 修改为我的答案的更新版本:

      set.seed(285)
      dat1 <- dat <- data.frame(x=sample(-5:5, 1e8, replace=TRUE), y=rnorm(1e8))
      dtbl1 <- function() {setDT(dat)[x<0,x:=NA]}
      dplr1 <- function() {dat1 %>% mutate(x = replace(x, which(x<0L), NA))}
      dplr2 <- function() {dat1 %>% mutate(x = NA ^ (x < 0) * x)}
      microbenchmark(dtbl1(), dplr1(), dplr2(), unit='relative', times=20L)
      #Unit: relative
      #    expr      min       lq   median       uq      max neval
      # dtbl1() 1.091208 4.319863 4.194086 4.162326 4.252482    20
      # dplr1() 1.000000 1.000000 1.000000 1.000000 1.000000    20
      # dplr2() 6.251354 5.529948 5.344294 5.311595 5.190192    20
      

      【讨论】:

      • 也许 akrun 愿意更新他的答案。他似乎正在运行这两个软件包的最新版本。
      • 这似乎是基本方法在语法方面要简单得多的情况。
      • 无法重现您的基准! data.table 更快。
      【解决方案5】:

      你可以使用is.na&lt;-函数:

      dat %>% mutate(x = "is.na<-"(x, x < 0))
      

      或者你可以使用数学运算符:

      dat %>% mutate(x = NA ^ (x < 0) * x)
      

      【讨论】:

      • 正在寻找一个示例,其中我根据其他条件将一个变量设置为 NA ...此页面似乎都没有这样做?
      猜你喜欢
      • 2021-09-24
      • 2016-04-23
      • 2019-08-07
      • 2021-08-24
      • 1970-01-01
      • 1970-01-01
      • 2019-01-29
      相关资源
      最近更新 更多