dplyr özetle: Çıktıda sıfır uzunlukta grupları tutmak için ".drop = FALSE" eşdeğeri


98

Kullanırken summariseile plyrbireyin ddplyfonksiyonu, boş kategorileri varsayılan olarak bırakılır. Bu davranışı ekleyerek değiştirebilirsiniz .drop = FALSE. Ancak bu, summariseile kullanıldığında çalışmaz dplyr. Sonuçta boş kategorileri tutmanın başka bir yolu var mı?

İşte sahte verilerle bir örnek.

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

Tam olarak umduğum gibi değil. Bir var mı dplyraynı sonucu elde etmek için bir yöntem .drop=FALSEde plyr?


Yanıtlar:


27

Dplyr 0.8 , tam istediğiniz şeyi group_byyapan .dropargümanı kazandığından beri :

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'ın cevabına ek bir not: Kullanmak .drop=FALSE, bir veya daha fazla gruplama değişkeni faktör olarak kodlanmadığında potansiyel olarak beklenmedik sonuçlar verebilir. Aşağıdaki örneklere bakın:

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)

Cevabınıza ek bir not ekledim. Düzenlemeyi beğenmezseniz lütfen silmekten çekinmeyin.
eipi10

Bunun bir hata mı yoksa amaçlanan davranış mı olduğunu öğrenmek için github'da bununla ilgili bir sorun bildirdim.
eipi10

@ eipi10 biraz daha kısa, aşağıdakilerin kullanımıdır count:iris %>% count(Species, group2, .drop=FALSE)
Tjebo

59

Sorun hala açık, ancak bu arada, özellikle verileriniz zaten hesaba completekatıldığı için, aradığınız şeyi elde etmek için "tidyr" den kullanabilirsiniz :

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

Değişim değerinin sıfır olmasını istiyorsanız, bunu şununla belirtmeniz gerekir 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
Bunu anlamak için duvara vurmam çok fazla kafamı aldı, bu yüzden burada bahsedeceğim ... 2 değişkene göre gruplandırırsanız ve bunlar faktörlerden ziyade karakter ise, ungroup()tamamlamadan önce kullanmanız gerekecek . completeGerçekte tamamlanmadığını fark ederseniz ungroup, muhtemelen gereklidir.
williamsurles

Ya daha fazla gruplama değişkeniniz varsa? Group_by
TobiO'daki

1
Bunu anladım: complete(variablewithdroppedlevels, nesting(var1,var2,var3))complete
İç içe geçmeyi

20

dplyr çözümü:

Önce gruplandırılmış df yapın

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

sonra bu seviyeleri sayarak özetliyoruz n()

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

daha sonra sonuçlarımızı tüm faktör seviyelerini içeren bir veri çerçevesinde birleştiriyoruz:

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

son olarak, bu durumda sayılara baktığımız için NAdeğerler 0 olarak değiştirilir.

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

Bu aynı zamanda işlevsel olarak da uygulanabilir, cevaplara bakın: dplyr ile gruplanmış verilere satırlar eklensin mi?

Bir hack:

Faiz uğruna bu durumda işe yarayan korkunç bir hack göndereceğimi düşündüm . Bunu gerçekten yapmanız gerektiğinden ciddi olarak şüpheliyim, ancak bu, seviyelerle bir faktör değil, bir karakter vektörüymüş group_by()gibi değerleri nasıl ürettiğini gösteriyor df$b. Ayrıca, bunu doğru bir şekilde anlıyormuş gibi yapmıyorum - ama bunun öğrenmeme yardımcı olacağını umuyorum - bunu yayınlamamın tek nedeni bu!

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

Veri kümesinde bulunamayacak bir "sınır dışı" değeri tanımlayın.

oob_val <- nrow(by_b)+1

nitelikleri "hile" olarak değiştirin summarise():

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

özeti yap:

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

oob_val'in tüm oluşumlarını dizine ekleyin ve değiştirin

res[res == oob_val] <- 0

amaçlanan:

> res
Source: local data frame [3 x 2]

b count_a
1 1       6
2 2       6
3 3       0

11

bu tam olarak soruda sorulan şey değil, ama en azından bu basit örnek için, xtabs kullanarak aynı sonucu elde edebilirsiniz, örneğin:

dplyr kullanarak:

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

veya daha kısa:

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

sonuç (her iki durumda da eşittir):

  b Freq
1 1    6
2 2    6
3 3    0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.