【问题标题】:Add rows to grouped data with dplyr?使用 dplyr 将行添加到分组数据?
【发布时间】:2014-06-20 12:13:25
【问题描述】:

我的数据采用 data.frame 格式,例如以下示例数据:

data <- 
structure(list(Article = structure(c(1L, 1L, 3L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 2L, 1L, 2L, 1L
), .Label = c("10004", "10006", "10007"), class = "factor"), 
Demand = c(26L, 780L, 2L, 181L, 228L, 214L, 219L, 291L, 104L, 
72L, 155L, 237L, 182L, 148L, 52L, 227L, 2L, 355L, 2L, 432L, 
1L, 156L), Week = c("2013-W01", "2013-W01", "2013-W01", "2013-W01", 
"2013-W01", "2013-W02", "2013-W02", "2013-W02", "2013-W02", 
"2013-W02", "2013-W03", "2013-W03", "2013-W03", "2013-W03", 
"2013-W03", "2013-W04", "2013-W04", "2013-W04", "2013-W04", 
"2013-W04", "2013-W04", "2013-W04")), .Names = c("Article", 
"Demand", "Week"), class = "data.frame", row.names = c(NA, -22L))

我想按周和文章来总结需求栏。为此,我使用:

library(dplyr)
WeekSums <- 
  data %>%
   group_by(Article, Week) %>%
   summarize(
    WeekDemand = sum(Demand)
   )

但由于某些文章在某些周内没有售出,因此每篇文章的行数不同(WeekSums 数据框中仅显示有销售的周数)。如何调整我的数据,使每篇文章的行数相同(每周一行),包括需求为 0 的周数?

输出应如下所示:

  Article     Week WeekDemand
1   10004 2013-W01       1215
2   10004 2013-W02        900
3   10004 2013-W03        774
4   10004 2013-W04       1170
5   10006 2013-W01        0
6   10006 2013-W02        0
7   10006 2013-W03        0
8   10006 2013-W04         5
9   10007 2013-W01         2
10   10007 2013-W02        0
11   10007 2013-W03        0
12   10007 2013-W04        0

我试过了

WeekSums %>%
  group_by(Article) %>%
  if(n()< 4) rep(rbind(c(Article,NA,NA)), 4 - n() )

但这不起作用。在我最初的方法中,我通过将第 1-4 周的数据框与每篇文章的原始数据文件合并来解决这个问题。这样,我每篇文章有 4 周(行),但是使用 for 循环的实现效率非常低,所以我尝试对 dplyr(或任何其他更有效的包/函数)做同样的事情。任何建议将不胜感激!

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    没有 dplyr 可以这样完成:

    as.data.frame(xtabs(Demand ~ Week + Article, data))
    

    给予:

           Week Article Freq
    1  2013-W01   10004 1215
    2  2013-W02   10004  900
    3  2013-W03   10004  774
    4  2013-W04   10004 1170
    5  2013-W01   10006    0
    6  2013-W02   10006    0
    7  2013-W03   10006    0
    8  2013-W04   10006    5
    9  2013-W01   10007    2
    10 2013-W02   10007    0
    11 2013-W03   10007    0
    12 2013-W04   10007    0
    

    这可以重写为 magrittr 或 dplyr 管道,如下所示:

    data %>% xtabs(formula = Demand ~ Week + Article) %>% as.data.frame()
    

    如果需要宽格式解决方案,可以省略末尾的 as.data.frame()

    【讨论】:

    • xtabs 使用指定的公式创建一个 "table" 类的对象,其维度是右侧变量,其单元格是左侧变量的总和,如果该单元格为空,则为零. as.data.frame 应用于表格时会将其重塑为长表格。
    【解决方案2】:

    由于dplyr 正在积极开发中,我想我会发布一个包含tidyr 的更新:

    library(dplyr)
    library(tidyr)
    
    data %>%
      expand(Article, Week) %>%
      left_join(data) %>%
      group_by(Article, Week) %>%
      summarise(WeekDemand = sum(Demand, na.rm=TRUE))
    

    产生:

       Article     Week WeekDemand
    1    10004 2013-W01       1215
    2    10004 2013-W02        900
    3    10004 2013-W03        774
    4    10004 2013-W04       1170
    5    10006 2013-W01          0
    6    10006 2013-W02          0
    7    10006 2013-W03          0
    8    10006 2013-W04          5
    9    10007 2013-W01          2
    10   10007 2013-W02          0
    11   10007 2013-W03          0
    12   10007 2013-W04          0
    

    使用 tidyr >= 0.3.1 现在可以写成:

    data %>% 
      complete(Article, Week) %>%  
      group_by(Article, Week) %>% 
      summarise(Demand = sum(Demand, na.rm = TRUE))
    

    【讨论】:

    • 感谢您展示解决问题的另一种方法!我不得不承认我喜欢 xtabs 解决方案的简单性,但这也产生了预期的结果 (+1)
    【解决方案3】:

    我想我会提供一个dplyr-esque 解决方案。

    • 使用expand.grid() 生成您正在寻找的成对组合。
    • 使用left_join() 加入需求数据(用 NA 填充其余部分)。

    解决办法:

    full_data <- expand.grid(Article=data$Article,Week=data$Week)
    out <- left_join(tbl_dt(full_data),data)
    out[is.na(out)] <- 0    # fill with zeroes for summarise below.
    

    然后和以前一样:

    WeekSums <- out %>%
                group_by(Article, Week) %>%
                summarise(
                         WeekDemand = sum(Demand)
                         )
    

    Fxnal 编程?

    如果您经常使用这种转换,那么也许是一个便利功能:

    xpand <- function(df, col1, col2,na_to_zero=TRUE){
    
        require(dplyr)
    
        # to substitute in the names "as is" need substitute then eval.
        xpand_call <- substitute(     
            expanded <- df %>%
                        select(col1,col2) %>%
                        expand.grid()
        )
    
        eval(xpand_call)                       
    
        out <- left_join(tbl_dt(expanded), df)         # join in any other variables from df.
    
        if(na_to_zero) out[is.na(out)] <- 0    # convert NAs to zeroes?
    
        return(out)
    }
    

    这样你就可以做到:

    expanded_df <- xpand(df,Article,Week)
    

    【讨论】:

      【解决方案4】:

      对于这种情况,您还可以使用dcastmelt

         library(dplyr)
         library(reshape2)
         data %>%
            dcast(Article ~ Week, value.var = "Demand", fun.aggregate = sum) %>%
            melt(id = "Article") %>%
            arrange(Article, variable)
      

      【讨论】:

        猜你喜欢
        • 2019-04-29
        • 2016-02-07
        • 1970-01-01
        • 1970-01-01
        • 2020-09-14
        • 1970-01-01
        • 2021-07-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多