【问题标题】:dplyr - right join after group_by not producing desired/expected resultdplyr - 在 group_by 之后右加入未产生所需/预期的结果
【发布时间】:2015-12-20 15:05:32
【问题描述】:

我正在尝试让我的每个 id/year/month 行都有对应于所有 7 个工作日的所有行,其中 NA 表示“缺少工作日”。

这是数据框和我完成此任务的尝试:

> df
  id year month weekday  amount
1  1 2015     1  Friday 3650.43
2  2 2015     1  Monday 1271.12
3  1 2015     2  Friday 1315.79
4  2 2015     2  Monday 2195.37
> wday
    weekday
1    Friday
2  Saturday
3 Wednesday
4    Sunday
5   Tuesday
6    Monday
7  Thursday

尝试使用 group_by() 和右连接。但是,它并没有像我想象的那样产生。有没有一种简单的方法可以达到我所追求的结果?

> df <- df %>% group_by(id, year, month) %>% right_join(wday)
Joining by: "weekday"
> df
Source: local data frame [9 x 5]
Groups: id, year, month [?]

     id  year month   weekday  amount
  (dbl) (int) (int)     (chr)   (dbl)
1     1  2015     1    Friday 3650.43
2     1  2015     2    Friday 1315.79
3    NA    NA    NA  Saturday      NA
4    NA    NA    NA Wednesday      NA
5    NA    NA    NA    Sunday      NA
6    NA    NA    NA   Tuesday      NA
7     2  2015     1    Monday 1271.12
8     2  2015     2    Monday 2195.37
9    NA    NA    NA  Thursday      NA

我希望每个 id/年/月组合有 7 行,其中缺少工作日的数量将为 NA(或理想情况下为零,但我知道如何通过 mutate() 获得)。

生成的数据框应如下所示:

> df
   id year month   weekday  amount
1   1 2015     1    Friday 3650.43
2   1 2015     1    Monday    0.00
3   1 2015     1  Saturday    0.00
4   1 2015     1    Sunday    0.00
5   1 2015     1  Thursday    0.00
6   1 2015     1   Tuesday    0.00
7   1 2015     1 Wednesday    0.00
8   1 2015     2    Friday 1315.79
9   1 2015     2    Monday    0.00
10  1 2015     2  Saturday    0.00
11  1 2015     2    Sunday    0.00
12  1 2015     2  Thursday    0.00
13  1 2015     2   Tuesday    0.00
14  1 2015     2 Wednesday    0.00
15  2 2015     1    Friday    0.00
16  2 2015     1    Monday 1271.12
17  2 2015     1  Saturday    0.00
18  2 2015     1    Sunday    0.00
19  2 2015     1  Thursday    0.00
20  2 2015     1   Tuesday    0.00
21  2 2015     1 Wednesday    0.00
22  2 2015     2    Friday    0.00
23  2 2015     2    Monday 2195.37
24  2 2015     2  Saturday    0.00
25  2 2015     2    Sunday    0.00
26  2 2015     2  Thursday    0.00
27  2 2015     2   Tuesday    0.00
28  2 2015     2 Wednesday    0.00

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    我们可以使用expand.grid

    expand.grid(c(lapply(df[1:3], unique), wday['weekday'])) %>% 
           left_join(., df) %>%
           mutate(amount=replace(amount, is.na(amount), 0)) %>% 
           arrange(id, year, month, weekday)
    #    id year month   weekday  amount
    #1   1 2015     1    Friday 3650.43
    #2   1 2015     1    Monday    0.00
    #3   1 2015     1  Saturday    0.00
    #4   1 2015     1    Sunday    0.00
    #5   1 2015     1  Thursday    0.00
    #6   1 2015     1   Tuesday    0.00
    #7   1 2015     1 Wednesday    0.00
    #8   1 2015     2    Friday 1315.79
    #9   1 2015     2    Monday    0.00
    #10  1 2015     2  Saturday    0.00
    #11  1 2015     2    Sunday    0.00
    #12  1 2015     2  Thursday    0.00
    #13  1 2015     2   Tuesday    0.00
    #14  1 2015     2 Wednesday    0.00
    #15  2 2015     1    Friday    0.00
    #16  2 2015     1    Monday 1271.12
    #17  2 2015     1  Saturday    0.00
    #18  2 2015     1    Sunday    0.00
    #19  2 2015     1  Thursday    0.00
    #20  2 2015     1   Tuesday    0.00
    #21  2 2015     1 Wednesday    0.00
    #22  2 2015     2    Friday    0.00
    #23  2 2015     2    Monday 2195.37
    #24  2 2015     2  Saturday    0.00
    #25  2 2015     2    Sunday    0.00
    #26  2 2015     2  Thursday    0.00
    #27  2 2015     2   Tuesday    0.00
    #28  2 2015     2 Wednesday    0.00
    

    【讨论】:

    • 甚至不知道它存在于基础包中。像魅力一样工作。
    【解决方案2】:

    sqldf 对于复杂的连接,通常使用 SQL 更容易:

    library(sqldf)
    sqldf("select 
             id, 
             year, 
             month, 
             wday.weekday, 
             sum((df.weekday = wday.weekday) * amount) amount 
           from df 
           join wday
           group by 1, 2, 3, 4")
    

    给予:

       id year month   weekday  amount
    1   1 2015     1    Friday 3650.43
    2   1 2015     1  Saturday    0.00
    3   1 2015     1 Wednesday    0.00
    4   1 2015     1    Sunday    0.00
    5   1 2015     1   Tuesday    0.00
    6   1 2015     1    Monday    0.00
    7   1 2015     1  Thursday    0.00
    8   2 2015     1    Friday    0.00
    9   2 2015     1  Saturday    0.00
    10  2 2015     1 Wednesday    0.00
    11  2 2015     1    Sunday    0.00
    12  2 2015     1   Tuesday    0.00
    13  2 2015     1    Monday 1271.12
    14  2 2015     1  Thursday    0.00
    15  1 2015     2    Friday 1315.79
    16  1 2015     2  Saturday    0.00
    17  1 2015     2 Wednesday    0.00
    18  1 2015     2    Sunday    0.00
    19  1 2015     2   Tuesday    0.00
    20  1 2015     2    Monday    0.00
    21  1 2015     2  Thursday    0.00
    22  2 2015     2    Friday    0.00
    23  2 2015     2  Saturday    0.00
    24  2 2015     2 Wednesday    0.00
    25  2 2015     2    Sunday    0.00
    26  2 2015     2   Tuesday    0.00
    27  2 2015     2    Monday 2195.37
    28  2 2015     2  Thursday    0.00
    

    base R我们可以使用mergetransform在base R中复制这个:

    xt <- transform(
      merge(df, wday, by = c()),
      amount = (as.character(weekday.x) == as.character(weekday.y)) * amount, 
      weekday = weekday.y, 
      weekday.x = NULL, 
      weekday.y = NULL
    ))
    aggregate(amount ~., xt, sum)
    

    dplyr 如果我们真的想使用 dplyr,我们可以将 transform 替换为 mutaterenameselect

    library(dplyr)
    merge(df, wday, by = c()) %>% 
     mutate(amount = (as.character(weekday.x) == as.character(weekday.y)) * amount) %>%
     rename(weekday = weekday.y) %>%
     select(-weekday.x) %>%
     group_by(id, year, month, weekday) %>%
     summarise(amount = sum(amount))
    

    注意:如果每个组只有一个工作日(如问题中所示),我们可以选择在三个解决方案中分别省略 group by/sum、aggregate 和 group_by/summarise。

    【讨论】:

    • 添加了其他解决方案
    【解决方案3】:

    使用tidyrdplyrcomplete 在这里完成繁重的工作 - 如果您已经在 df 的某个地方拥有每个工作日,则不需要 bind_rowsna.omit(或 dplyr)。

    library(dplyr)
    library(tidyr)
    df %>% #initial data
        bind_rows(wday) %>% #adding on so we have all the weekdays
        complete(id, year, month, weekday,  #completing all levels of id:year:month:weekday
                    fill = list(amount = 0)) %>% #filling amount column with 0
        na.omit() #remove the NAs we got from the bind_rows
    

    【讨论】:

      猜你喜欢
      • 2014-08-30
      • 2018-02-21
      • 1970-01-01
      • 2015-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      相关资源
      最近更新 更多