[英]dplyr summarise: Equivalent of ".drop=FALSE" to keep groups with zero length in output
將summarise
與plyr
的ddply
函數一起使用時,默認情況下會刪除空類別。 您可以通過添加.drop = FALSE
來更改此行為。 但是,這在使用帶有dplyr
summarise
時dplyr
。 有沒有另一種方法可以在結果中保留空類別?
這是一個帶有虛假數據的示例。
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
達到同樣的結果的方法.drop=FALSE
在plyr
?
這個問題仍然是開放的,但在此期間,特別是因為您的數據已經被分解,你可以使用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
由於dplyr 0.8 group_by
獲得了.drop
參數,它.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)
首先進行分組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
這也可以在功能上實現,請參閱答案: 使用 dplyr 將行添加到分組數據?
為了利益,我想我會發布一個在這種情況下有效的可怕黑客。 我嚴重懷疑你真的應該這樣做,但它顯示了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
這與問題中所問的不完全相同,但至少對於這個簡單的示例,您可以使用 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.