【问题标题】:dplyr mutate calling another dataframedplyr mutate 调用另一个数据帧
【发布时间】:2016-09-25 16:31:44
【问题描述】:

我想通过应用调用另一个数据帧的函数来改变数据帧。我可以通过几种不同的方式实现这一点,但想知道如何“正确”地做到这一点。

这是我正在尝试做的一个示例。我有一个带有一些开始时间的数据框,还有一个带有一些定时观察的数据框。我想返回一个包含开始时间的数据框,以及开始时间之后某个窗口内发生的观察次数。例如

set.seed(1337)
df1 <- data.frame(id=LETTERS[1:3], start_time=1:3*10)
df2 <- data.frame(time=runif(100)*100)
lapply(df1$start_time, function(s) sum(df2$time>s & df2$time<(s+15)))

到目前为止,我使用 dplyr 得到的最好的结果如下(但这会丢失身份变量):

df1 %>% 
    rowwise() %>%
    do(count = filter(df2, time>.$start_time, time < (.$start_time + 15))) %>%
    mutate(n=nrow(count))

输出:

Source: local data frame [3 x 2]
Groups: <by row>

# A tibble: 3 × 2
                  count     n
                 <list> <int>
1 <data.frame [17 × 1]>    17
2 <data.frame [18 × 1]>    18
3 <data.frame [10 × 1]>    10

我期待能够做到这一点:

df1 <- data.frame(id=LETTERS[1:3], start_time=1:3*10)
df2 <- data.frame(time=runif(100)*100)
df1 %>% 
    group_by(id) %>% 
    mutate(count = nrow(filter(df2, time>start_time, time<(start_time+15))))

但这会返回错误:

Error: comparison (6) is possible only for atomic and list types

dplyr 的做法是什么?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    这是data.table 的一个选项,我们可以使用non-equi 连接

    library(data.table)#1.9.7+
    setDT(df1)[, start_timeNew := start_time + 15]
    setDT(df2)[df1, .(id, .N), on = .(time > start_time, time < start_timeNew), 
               by = .EACHI][, c('id', 'N'), with = FALSE]
    #   id  N
    #1:  A 17
    #2:  B 18
    #3:  C 10
    

    它给出的计数与 OP 的 base R 方法中的计数相同

    sapply(df1$start_time, function(s) sum(df2$time>s & df2$time<(s+15)))
    #[1] 17 18 10
    

    如果我们还需要 'id' 变量作为 dplyr 中的输出,我们可以修改 OP 的代码

    df1 %>%
        rowwise() %>% 
        do(data.frame(., count = filter(df2, time>.$start_time,
                                     time < (.$start_time + 15)))) %>% 
        group_by(id) %>% 
        summarise(n = n())
    #      id     n
    #  <fctr> <int>
    #1      A    17
    #2      B    18
    #3      C    10
    

    或者另一个选项是map 来自purrrdplyr

    library(purrr)
    df1 %>% 
        split(.$id) %>% 
        map_df(~mutate(., N = sum(df2$time >start_time & df2$time < start_time + 15))) %>% 
        select(-start_time)
    #   id  N
    #1  A 17
    #2  B 18
    #3  C 10
    

    【讨论】:

    • @kungfujam 谢谢,我根据那个种子更新了输出
    • 第二个解决方案(和第三个)正是我想要的。
    【解决方案2】:

    使用dplyr的另一种略有不同的方法:

    result <- df1 %>% group_by(id) %>% 
                      summarise(count = length(which(df2$time > start_time &
                                                     df2$time < (start_time+15))))
    
    print(result)
    ### A tibble: 3 x 2
    ##      id count
    ##  <fctr> <int>
    ##1      A    17
    ##2      B    18
    ##3      C    10
    

    我相信您可以使用lengthwhich 来计算df1 中每个id 的条件为真的出现次数。然后,按id 分组并将其用于summarise


    如果每个id 可能有多个start_time,那么您可以使用相同的功能,但rowwisemutate

    result <- df1 %>% rowwise() %>% 
                      mutate(count = length(which(df2$time > start_time & 
                                                  df2$time < (start_time+15))))
    print(result)
    ##Source: local data frame [3 x 3]
    ##Groups: <by row>
    ##
    ### A tibble: 3 x 3
    ##      id start_time count
    ##  <fctr>      <dbl> <int>
    ##1      A         10    17
    ##2      B         20    18
    ##3      C         30    10
    

    【讨论】:

    • 如果我们使用mutate 代替summarise,这也有效,这样做的好处是,如果按 vars 分组有其他变量,它们不会被删除
    • @kungfujam: 是的,但是如果每个id 有多个start_time,那么我们需要使用函数rowwise 而不是group_by(id)。请查看我的编辑。
    猜你喜欢
    • 2023-02-15
    • 1970-01-01
    • 2019-08-26
    • 2021-03-13
    • 2021-08-08
    • 1970-01-01
    • 2020-06-20
    • 1970-01-01
    • 2015-05-12
    相关资源
    最近更新 更多