create new variable defined only for a subset of the data using `dplyr`
日期:考虑这个exampleP></
1 2 | set.seed(1234567) mydf <- data.frame(var1 = runif(10), var2 = c(runif(5), rep(NA, 5))) |
矢量函数的this和that example,不幸的是他/她,当一个触发器安Arguments of the error is
1 2 3 4 | myfn <- function(x, y){ sum(x:y) } myfn <- Vectorize(myfn) |
现在,在链的中间
我知道最常见的溶液
1 2 3 4 5 | mydf %>% mutate(var3 = ifelse( test = is.na(var2), yes = NA, no = myfn(var1, var2))) |
but this does not work在我的房子,因为
我是聪明的,
它可以帮助我occurred to that
1 2 3 4 5 6 7 8 9 10 | mydf %>% filter(!is.na(var2)) %>% mutate(var3 = myfn(var1, var2)) var1 var2 var3 1 0.56226084 0.62588794 0.56226084 2 0.72649850 0.24145251 0.72649850 3 0.91524985 0.03768974 0.91524985 4 0.02969437 0.51659297 0.02969437 5 0.76750970 0.81845788 0.76750970 |
但我会保存在这个临时对象中创建,然后在与原始数据的
我只是想说明to the输出队列,这produces恩(不使用在所有的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | mydf$var3 <- NA index <- !is.na(mydf$var2) mydf$var3[index] <- myfn(mydf$var1[index], mydf$var2[index]) mydf > mydf var1 var2 var3 1 0.56226084 0.62588794 0.56226084 2 0.72649850 0.24145251 0.72649850 3 0.91524985 0.03768974 0.91524985 4 0.02969437 0.51659297 0.02969437 5 0.76750970 0.81845788 0.76750970 6 0.48005398 NA NA 7 0.08837960 NA NA 8 0.86294587 NA NA 9 0.49660306 NA NA 10 0.85350403 NA NA |
编辑:P></
的解决方案,因为它的krlmlr' accepted"就是我寻找:清晰,简明和可读的代码,easily effortlessly集成在
1 2 3 | mydf %>% rowwise %>% mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2)) |
不管一个人多,As在他的答案krlmlr角@超时操作模式,行行有在条款的成本和性能。它可能不重要集合的日期或时间为小单操作空气日期2010年1月17日,but for millions of the operation sets or语言时代,它是considerable。对了,这是比较使用的解决方案和三
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | library(data.table) library(dplyr) set.seed(1234567) mydf <- data.frame(var1 = runif(1000), var2 = c(runif(500), rep(NA, 500))) myfn <- function(x, y){ sum(x:y) } myfn <- Vectorize(myfn) using_base <- function(){ mydf$var3 <- NA index <- !is.na(mydf$var2) mydf$var3[index] <- myfn(mydf$var1[index], mydf$var2[index]) } using_dplyr <- function(){ mydf <- mydf %>% rowwise %>% mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2)) } using_datatable <- function(){ setDT(mydf)[!is.na(var2), var3 := myfn(var1, var2)] } library(microbenchmark) mbm <- microbenchmark( using_base(), using_dplyr(), using_datatable(), times = 1000) library(ggplot2) autoplot(mbm) |
P></
和As You can see,the solution is using
你可以考虑使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | library(data.table) setDT(mydf)[!is.na(var2), var3 := myfn(var1, var2)] # var1 var2 var3 # 1: 0.56226084 0.62588794 0.56226084 # 2: 0.72649850 0.24145251 0.72649850 # 3: 0.91524985 0.03768974 0.91524985 # 4: 0.02969437 0.51659297 0.02969437 # 5: 0.76750970 0.81845788 0.76750970 # 6: 0.48005398 NA NA # 7: 0.08837960 NA NA # 8: 0.86294587 NA NA # 9: 0.49660306 NA NA #10: 0.85350403 NA NA |
以下是可以在dplyr管道中使用的其他两个选项:
a)带有临时变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | mutate(mydf, temp = !(is.na(var1) | is.na(var2)), var3 = replace(NA, temp, myfn(var1[temp], var2[temp])), temp = NULL) # var1 var2 var3 #1 0.56226084 0.62588794 0.56226084 #2 0.72649850 0.24145251 0.72649850 #3 0.91524985 0.03768974 0.91524985 #4 0.02969437 0.51659297 0.02969437 #5 0.76750970 0.81845788 0.76750970 #6 0.48005398 NA NA #7 0.08837960 NA NA #8 0.86294587 NA NA #9 0.49660306 NA NA #10 0.85350403 NA NA |
。
b)具有包装功能(不改变原来的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | myfn2 <- function(x, y) { i <- !(is.na(x) | is.na(y)) res <- rep(NA, length(x)) res[i] <- myfn(x[i], y[i]) res } mutate(mydf, var3 = myfn2(var1, var2)) # var1 var2 var3 #1 0.56226084 0.62588794 0.56226084 #2 0.72649850 0.24145251 0.72649850 #3 0.91524985 0.03768974 0.91524985 #4 0.02969437 0.51659297 0.02969437 #5 0.76750970 0.81845788 0.76750970 #6 0.48005398 NA NA #7 0.08837960 NA NA #8 0.86294587 NA NA #9 0.49660306 NA NA #10 0.85350403 NA NA |
如果您的原始函数没有向量化,并且不能处理某些输入,那么使用
1 2 3 4 | iris %>% rowwise %>% mutate(x = if (Sepal.Length < 5) 1 else NA) %>% ungroup |
号
注意,这里使用
这是一个采用Python式乞求宽恕而不是请求许可的伟大案例。
您可以使用
1 2 3 | myfn <- function(x, y){ tryCatch(sum(x:y), error = function(e) NA) } |
。
然后
1 2 3 | myfn <- Vectorize(myfn) mydf %>% mutate(var3 = myfn(var1, var2)) |
。
给出所需的结果
1 2 3 4 5 6 7 8 9 10 11 | var1 var2 var3 1 0.56226084 0.62588794 0.56226084 2 0.72649850 0.24145251 0.72649850 3 0.91524985 0.03768974 0.91524985 4 0.02969437 0.51659297 0.02969437 5 0.76750970 0.81845788 0.76750970 6 0.48005398 NA NA 7 0.08837960 NA NA 8 0.86294587 NA NA 9 0.49660306 NA NA 10 0.85350403 NA NA |
附录
当然,最好只将NA传递给正确的错误类型,即
1 2 3 4 5 6 | > tryCatch(sum(NA:NA), error = function(e) print(str(e))) List of 2 $ message: chr"NA/NaN argument" $ call : language NA:NA - attr(*,"class")= chr [1:3]"simpleError""error""condition" NULL |
。
您可以在完整的行上运行该函数,然后用
1 2 3 | mydf %>% filter(complete.cases(.)) %>% mutate(var3 = myfn(var1, var2)) %>% bind_rows(mydf %>% filter(!complete.cases(.))) |
1
2
3
4
5
6
7
8
9
10
11
12 var1 var2 var3
(dbl) (dbl) (dbl)
1 0.56226084 0.62588794 0.56226084
2 0.72649850 0.24145251 0.72649850
3 0.91524985 0.03768974 0.91524985
4 0.02969437 0.51659297 0.02969437
5 0.76750970 0.81845788 0.76750970
6 0.48005398 NA NA
7 0.08837960 NA NA
8 0.86294587 NA NA
9 0.49660306 NA NA
10 0.85350403 NA NA
号