10赞
954
当前位置:  开发笔记 > 编程语言 > 正文

使用`dplyr`创建仅为数据子集定义的新变量

如何解决《使用`dplyr`创建仅为数据子集定义的新变量》经验,为你挑选了1个好方法。

考虑这个示例数据:

set.seed(1234567)
mydf <- data.frame(var1 = runif(10), var2 = c(runif(5), rep(NA, 5)))

这个示例矢量化函数,不幸的是,只要其中一个参数发生错误就会触发错误 NA

myfn <- function(x, y){
    sum(x:y)
}
myfn <- Vectorize(myfn)

现在,在dplyr链的中间我需要使用创建一个新变量myfn.这个新变种(var3)只在定义var1var2NA.

因此,类似情况的最常见解决方案是使用ifelse.像这样的东西.

mydf %>%
    mutate(var3 = ifelse(
        test = is.na(var2), 
        yes = NA, 
        no = myfn(var1, var2)))

但是,这并不在我的情况下工作,因为ifelse反正实际上通过全矢量var1var2,以myfn和而不仅仅是当子向量testFALSE.而这一切都休息,因为myfn每当接收到一个断裂NA.

那么,这个聪明的dplyr解决方案是什么?(我可以在不使用的情况下考虑许多解决方案dplyr,但我只是对dplyr友好的解决方案感兴趣)

在我看来,它filter可以提供帮助,并且确实可以使用非常易读的dplyr代码

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

但是后来我必须将它保存在一个临时对象中,然后var3在原始数据中创建NA并将所有内容放回到相同的数据中(因为据我所知unfilter,有些人建议不存在,... ,但).

所以只是为了说明我想要的输出,这段代码产生它(根本没用dplyr):

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

编辑:

我接受了@ krlmlr的解决方案,因为它正是我所寻找的:清晰,易读且简洁的代码,可以毫不费力地集成到dplyr链中.对于我的例子,这个解决方案看起来像这样.

mydf %>%
        rowwise %>%
        mutate(var3 = if(is.na(var2)) NA else myfn(var1, var2))

但是,正如@krlmlr在他的回答中指出的那样,逐行操作在性能方面有成本.它对于小型数据集或单次操作可能并不重要,但对于较大的数据集或重复操作数百万次,可能相当大.为了说明,这里是一个比较使用microbenchmark和三个解决方案(base,dyplr和data.table)应用于更大的数据集(不是大规模或任何东西,在我的原始示例中只有1000行而不是10行).

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)

在此输入图像描述

正如您所看到的,dplyr使用的解决方案rowwise比它basedata.table竞争对手慢得多.



1> mtoto..:

您可以考虑使用data.table,因为dplyr目前不支持就地变异,这是您似乎正在寻找的.

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


推荐阅读
author-avatar
手机用户2502936971
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有