使用 dplyr 将某些值设置为 NA

我正在尝试找到一种简单的方法来使用 dplyr (data set = dat,variable = x)来完成类似的操作:

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

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

dat =  dat %>% mutate(x=ifelse(x<0,NA,x))
53097 次浏览

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

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 的讨论过程中,我使用微基准测试和 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

如果使用 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。我使用了稍微大一点的数据集,并用 setDT代替了 as.data.table(包括@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))

你可以使用 is.na<-函数:

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

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

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

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

一个变量是:

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

对于所有变量:

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

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

dat %<>% mutate_all(na_if, 0)

注意,我正在使用 magrittr包中的 %<>%运算符。

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

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