Ustaw pewne wartości Na NA za pomocą dplyr

Próbuję wymyślić prosty sposób, aby zrobić coś takiego z dplyr (data set = dat, zmienna = x):

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

Powinno być proste, ale to najlepsze, co mogę zrobić w tej chwili. Jest jakiś łatwiejszy sposób?

dat =  dat %>% mutate(x=ifelse(x<0,NA,x))
 42
Author: Glen, 2015-01-12

3 answers

Możesz użyć replace, który jest nieco szybszy niż ifelse:

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

Możesz go nieco przyspieszyć, dostarczając indeks do replace za pomocą which:

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

Na mojej maszynie, to skrócić czas do trzeciej, patrz poniżej.

Oto małe porównanie różnych odpowiedzi, które jest oczywiście tylko orientacyjne:

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 

(używam dplyr_0.3.0.2 i data.table_1.9. 4)


Ponieważ zawsze jesteśmy bardzo zainteresowani benchmarkingiem, szczególnie w trakcie data.table-vs - dplyr dyskusje podaję kolejny benchmark z 3 odpowiedzi za pomocą microbenchmark i danych przez akrun. Zauważ, że zmodyfikowałem dplyr1 na zaktualizowaną wersję mojej odpowiedzi:

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
 73
Author: docendo discimus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-12 20:56:10

Możesz użyć funkcji is.na<-:

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

Lub możesz użyć operatorów matematycznych:

dat %>% mutate(x = NA ^ (x < 0) * x)
 13
Author: Sven Hohenstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-12 19:23:04

Jeśli używasz data.table, poniższy kod jest szybszy

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

Benchmarki

Za pomocą data.table_1.9.5 i 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

Zaktualizowane Benchmarki

Za pomocą data.table_1.9.5 i dplyr_0.4.0. Użyłem nieco większego zestawu danych i zamieniłem as.data.table na setDT (W tym szybszą funkcję @ Sven Hohenstein również.)

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 

Zaktualizowane Benchmarks2

Na prośbę @docendo discimus, porównując ponownie jego "nową" wersję dplyrużywając data.table_1.9.5 i dplyr_0.4.0.

UWAGA: Ponieważ nastąpiła zmiana w kodzie @ docendo discimus, zmieniłem 0 na 0L dla danych.tabela "

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

Dane

set.seed(24)
dat <- data.frame(x=sample(-5:5, 25, replace=TRUE), y=rnorm(25))
 6
Author: akrun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 03:20:41