dplyr要約:出力で長さがゼロのグループを保持するための「.drop = FALSE」と同等


97

の関数で使用summariseする場合、空のカテゴリはデフォルトで削除されます。この動作を変更するには、を追加します。ただし、これをで使用する場合は機能しません。結果に空のカテゴリを保持する別の方法はありますか?plyrddply.drop = FALSEsummarisedplyr

以下は、偽のデータの例です。

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


回答:


26

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の答えに加えて、もう1つ注意点.drop=FALSEがあります。1つ以上のグループ化変数が因子としてコーディングされていない場合、使用すると予期しない結果が生じる可能性があります。以下の例を参照してください。

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)

回答にメモを追加しました。編集が気に入らない場合は、削除してください。
eipi10

私がしましたこのことについて問題を提起し、これはバグか、意図した動作であるかどうかを調べるためにgithubの上で。
eipi10

eipi10 @少し短いの使用であるcountiris %>% count(Species, group2, .drop=FALSE)
Tjebo

59

問題は未解決のままですが、それまでの間、特にデータがすでに因数分解されているため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

11
これを理解するために壁に頭を叩くのに多くの時間を要したので、ここで触れます... 2つの変数でグループ化し、それらが因子ではなく文字である場合、ungroup()完了する前に使用する必要があります。complete実際に完了していないことに気付いた場合ungroupは、おそらく必要です。
williamsurles 2017年

さらに多くのグループ化変数がある場合はどうなりますか?group_byのすべてのグループ化変数を使用すると、膨大な数の行(元のデータフレームよりもはるかに多く)が表示されます
TobiO

1
私はそれを理解しました:ネストを使用する必要があります:-)したがって、それらを一緒に組み合わせてはならないすべての変数を入れてくださいcomplete(variablewithdroppedlevels, nesting(var1,var2,var3))(実際には、completeまだ理解するのに少し時間がかかりました
TobiO

20

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

これは機能的に実装することもできます。回答を参照してください: dplyrでグループ化されたデータに行を追加しますか?

ハック:

私はこの目的のために機能するひどいハックを投稿するつもりだと思った。これを実際に行うべきかどうかは真剣に疑っていますが、レベルの要素ではなく文字ベクトルであるgroup_by()かのようdf$bに属性がどのように生成されるかを示しています。また、私はこれを正しく理解するふりをしていません-これが私が学習に役立つことを願っています-これが私が投稿する唯一の理由です!

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

データセットに存在できない「範囲外」の値を定義します。

oob_val <- nrow(by_b)+1

属性を「トリック」に変更します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

11

これは質問で正確に尋ねられたものとは異なりますが、少なくともこの単純な例では、xtabを使用して同じ結果を得ることができます。次に例を示します。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.