使用 dplyr 将某些值设置为 NA
Set certain values to NA with dplyr
我想找出一种简单的方法来用 dplyr 做这样的事情(数据集 = dat,变量 = x):
day$x[dat$x<0]=NA
应该很简单,但这是我目前能做的最好的。有没有更简单的方法?
dat = dat %>% mutate(x=ifelse(x<0,NA,x))
您可以使用 replace
,它比 ifelse
快一点:
dat <- dat %>% mutate(x = replace(x, x<0, NA))
您可以使用 which
:
为 replace
提供索引来加快速度
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
如果您使用 data.table
,下面的代码会更快
library(data.table)
setDT(dat)[x<0,x:=NA]
基准
使用 data.table_1.9.5
和 dplyr_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.5
和 dplyr_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.5
和 dplyr_0.4.0
再次对他 "new" 版本的 dplyr
进行基准测试。
注意:因为@docendo discimus 代码发生了变化,我将 0
更改为 0L
data.table`
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)
我想找出一种简单的方法来用 dplyr 做这样的事情(数据集 = dat,变量 = x):
day$x[dat$x<0]=NA
应该很简单,但这是我目前能做的最好的。有没有更简单的方法?
dat = dat %>% mutate(x=ifelse(x<0,NA,x))
您可以使用 replace
,它比 ifelse
快一点:
dat <- dat %>% mutate(x = replace(x, x<0, NA))
您可以使用 which
:
replace
提供索引来加快速度
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
如果您使用 data.table
,下面的代码会更快
library(data.table)
setDT(dat)[x<0,x:=NA]
基准
使用 data.table_1.9.5
和 dplyr_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.5
和 dplyr_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.5
和 dplyr_0.4.0
再次对他 "new" 版本的 dplyr
进行基准测试。
注意:因为@docendo discimus 代码发生了变化,我将 0
更改为 0L
data.table`
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)