首页 文章

dplyr总结:相当于“ . drop = FALSE”以保持输出中长度为零的组

提问于
浏览
81

使用 summariseplyrddply 函数时,默认情况下会删除空类别 . 您可以通过添加 .drop = FALSE 来更改此行为 . 但是,当 summarisedplyr 一起使用时,这不起作用 . 还有另一种方法可以在结果中保留空类别吗?

这是假数据的一个例子 .

library(dplyr)

df = data.frame(a=rep(1:3,4), b=rep(1:2,6))

# Now add an extra level to df$b that has no corresponding value in df$a
df$b = factor(df$b, levels=1:3)

# Summarise with plyr, keeping categories with a count of zero
plyr::ddply(df, "b", summarise, count_a=length(a), .drop=FALSE)

  b    count_a
1 1    6
2 2    6
3 3    0

# Now try it with dplyr
df %.%
  group_by(b) %.%
  summarise(count_a=length(a), .drop=FALSE)

  b     count_a .drop
1 1     6       FALSE
2 2     6       FALSE

不完全是我所希望的 . 是否有 dplyr 方法可以在 plyr 中实现与 .drop=FALSE 相同的结果?

3 回答

  • 20

    问题仍然存在,但与此同时,特别是因为您的数据已经考虑因素,您可以使用 complete 来自"tidyr"获取您可能正在寻找的内容:

    library(tidyr)
    df %>%
      group_by(b) %>%
      summarise(count_a=length(a)) %>%
      complete(b)
    # Source: local data frame [3 x 2]
    # 
    #        b count_a
    #   (fctr)   (int)
    # 1      1       6
    # 2      2       6
    # 3      3      NA
    

    如果您希望替换值为零,则需要使用 fill 指定:

    df %>%
      group_by(b) %>%
      summarise(count_a=length(a)) %>%
      complete(b, fill = list(count_a = 0))
    # Source: local data frame [3 x 2]
    # 
    #        b count_a
    #   (fctr)   (dbl)
    # 1      1       6
    # 2      2       6
    # 3      3       0
    
  • 54

    dplyr解决方案:

    首先制作分组df

    by_b <- tbl_df(df) %>% group_by(b)
    

    然后我们通过 n() 计算总结那些发生的水平

    res <- by_b %>% summarise( count_a = n() )
    

    然后我们将结果合并到一个包含所有因子水平的数据框中:

    expanded_res <- left_join(expand.grid(b = levels(df$b)),res)
    

    最后,在这种情况下,因为我们正在查看计数, NA 值将更改为0 .

    final_counts <- expanded_res[is.na(expanded_res)] <- 0
    

    这也可以在功能上实现,请参阅答案:Add rows to grouped data with dplyr?

    一个黑客:

    为了感兴趣,我想我会在这种情况下发布 terrible hack . 我严重怀疑你应该真的这样做,但它显示 group_by() 如何生成atrributes,好像 df$b 是一个字符向量而不是一个水平因素 . 另外,我没有't pretend to understand this properly -- but I am hoping this helps me learn -- this is the only reason I'米贴出来!

    by_b <- tbl_df(df) %>% group_by(b)
    

    定义数据集中不存在的“越界”值 .

    oob_val <- nrow(by_b)+1
    

    将属性修改为"trick" summarise()

    attr(by_b, "indices")[[3]] <- rep(NA,oob_val)
    attr(by_b, "group_sizes")[3] <- 0
    attr(by_b, "labels")[3,] <- 3
    

    做总结:

    res <- by_b %>% summarise(count_a = n())
    

    索引并替换所有出现的oob_val

    res[res == oob_val] <- 0
    

    给出了预期的:

    > res
    Source: local data frame [3 x 2]
    
    b count_a
    1 1       6
    2 2       6
    3 3       0
    
  • 10

    这不完全是问题中的问题,但至少对于这个简单的例子,你可以使用xtabs获得相同的结果,例如:

    使用dplyr:

    df %.%
      xtabs(formula = ~ b) %.%
      as.data.frame()
    

    或更短:

    as.data.frame(xtabs( ~ b, df))
    

    结果(两种情况都相同):

    b Freq
    1 1    6
    2 2    6
    3 3    0
    

相关问题