【问题标题】:dplyr summarise: Equivalent of ".drop=FALSE" to keep groups with zero length in outputdplyr 摘要:等效于“.drop=FALSE”以保持输出中长度为零的组
【发布时间】:2014-04-26 17:12:56
【问题描述】:

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 相同的结果?

【问题讨论】:

标签: r dplyr plyr tidyr


【解决方案1】:

问题仍然存在,但与此同时,特别是因为您的数据已经被考虑在内,您可以使用“tidyr”中的complete 来获取您可能要查找的内容:

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

【讨论】:

  • 我费了好大劲才弄清楚这一点,所以我会在这里提一下...如果您按 2 个变量分组,并且它们是字符而不是因子,您将需要在完成之前使用ungroup()。如果您注意到complete 没有真正完成,可能需要ungroup
  • 如果您有更多分组变量怎么办?如果我使用 group_by 中的所有分组变量,我会得到大量的行(比我的原始数据框多得多)
  • 我想通了:你必须使用嵌套 :-) 所以把所有不应该相互组合的变量放在complete(variablewithdroppedlevels, nesting(var1,var2,var3)) 中(实际上在complete 的帮助中仍然需要我需要一段时间才能弄清楚
【解决方案2】:

由于 dplyr 0.8 group_by 获得了满足您要求的 .drop 参数:

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

df %>%
  group_by(b, .drop=FALSE) %>%
  summarise(count_a=length(a))

#> # A tibble: 3 x 2
#>   b     count_a
#>   <fct>   <int>
#> 1 1           6
#> 2 2           6
#> 3 3           0

与@Moody_Mudskipper 的回答有关的一个附加说明:当一个或多个分组变量未编码为因子时,使用.drop=FALSE 可能会产生意想不到的结果。请参阅以下示例:

library(dplyr)
data(iris)

# Add an additional level to Species
iris$Species = factor(iris$Species, levels=c(levels(iris$Species), "empty_level"))

# Species is a factor and empty groups are included in the output
iris %>% group_by(Species, .drop=FALSE) %>% tally

#>   Species         n
#> 1 setosa         50
#> 2 versicolor     50
#> 3 virginica      50
#> 4 empty_level     0

# Add character column
iris$group2 = c(rep(c("A","B"), 50), rep(c("B","C"), each=25))

# Empty groups involving combinations of Species and group2 are not included in output
iris %>% group_by(Species, group2, .drop=FALSE) %>% tally

#>   Species     group2     n
#> 1 setosa      A         25
#> 2 setosa      B         25
#> 3 versicolor  A         25
#> 4 versicolor  B         25
#> 5 virginica   B         25
#> 6 virginica   C         25
#> 7 empty_level <NA>       0

# Turn group2 into a factor
iris$group2 = factor(iris$group2)

# Now all possible combinations of Species and group2 are included in the output, 
#  whether present in the data or not
iris %>% group_by(Species, group2, .drop=FALSE) %>% tally

#>    Species     group2     n
#>  1 setosa      A         25
#>  2 setosa      B         25
#>  3 setosa      C          0
#>  4 versicolor  A         25
#>  5 versicolor  B         25
#>  6 versicolor  C          0
#>  7 virginica   A          0
#>  8 virginica   B         25
#>  9 virginica   C         25
#> 10 empty_level A          0
#> 11 empty_level B          0
#> 12 empty_level C          0

Created on 2019-03-13 by the reprex package (v0.2.1)

【讨论】:

  • 我在您的回答中添加了附加说明。如果您不喜欢编辑,请随时删除。
  • 我已经在 github 上 filed an issue about this 查明这是错误还是预期行为。
  • @eipi10 稍短一点是使用count:iris %&gt;% count(Species, group2, .drop=FALSE)
  • @tjebo,我很确定这不起作用(从 dplyr 1.0.7 开始)。我没有得到零计数组。
  • @tjebo 和 Moody_Mudskipper,我的错。我忘了将列转换为因子。我会因为我的阅读理解力差而惩罚自己。请忽略我之前的评论。谢谢! :)
【解决方案3】:

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?

破解:

为了感兴趣,我想我会发布一个 可怕的 hack,在这种情况下有效。我严重怀疑你是否真的应该这样做,但它显示了group_by() 如何生成属性,就好像df$b 是一个字符向量而不是级别的一个因素。另外,我不会假装正确理解这一点 - 但我希望这有助于我学习 - 这是我发布它的唯一原因!

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

【讨论】:

    【解决方案4】:

    这并不是问题中所问的,但至少对于这个简单的示例,您可以使用 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 2015-02-23
      • 1970-01-01
      • 2019-03-15
      相关资源
      最近更新 更多