dplyr tóm tắt: Tương đương với “.drop = FALSE” để giữ các nhóm không có độ dài trong đầu ra


97

Khi sử dụng summarisevới plyr's ddplychức năng, danh mục sản phẩm nào được giảm theo mặc định. Bạn có thể thay đổi hành vi này bằng cách thêm .drop = FALSE. Tuy nhiên, điều này không hoạt động khi sử dụng summarisevới dplyr. Có cách nào khác để giữ các danh mục trống trong kết quả không?

Đây là một ví dụ với dữ liệu giả.

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

Không chính xác những gì tôi đã hy vọng. Có dplyrphương pháp nào để đạt được kết quả tương tự như .drop=FALSEtrong plyrkhông?


Câu trả lời:


26

dplyr 0.8 group_by đã đạt được .dropđối số thực hiện đúng những gì bạn yêu cầu:

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

Một lưu ý bổ sung đi kèm với câu trả lời của @ Moody_Mudskipper: Việc sử dụng .drop=FALSEcó thể cho kết quả không mong muốn khi một hoặc nhiều biến nhóm không được mã hóa làm nhân tố. Xem các ví dụ bên dưới:

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)

Tôi đã thêm một ghi chú bổ sung vào câu trả lời của bạn. Vui lòng xóa nếu bạn không thích bản chỉnh sửa.
eipi10

Tôi đã gửi vấn đề về vấn đề này trên github để tìm hiểu xem liệu đây có phải là lỗi hay hành vi dự kiến.
eipi10

@ eipi10 ngắn hơn một chút là sử dụng count:iris %>% count(Species, group2, .drop=FALSE)
Tjebo

59

Vấn đề vẫn còn mở, nhưng trong thời gian chờ đợi, đặc biệt là vì dữ liệu của bạn đã được tính vào yếu tố, bạn có thể sử dụng completetừ "slimr" để nhận những gì bạn có thể đang tìm kiếm:

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

Nếu bạn muốn giá trị thay thế bằng 0, bạn cần chỉ định giá trị đó bằng 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
Tôi đã phải đập đầu vào tường rất nhiều để tìm ra điều này vì vậy tôi sẽ đề cập đến nó ở đây ... Nếu bạn nhóm theo 2 biến, và chúng là các ký tự chứ không phải thừa số, bạn sẽ cần phải sử dụng ungroup()trước khi hoàn thành. Nếu bạn nhận thấy completekhông thực sự hoàn thành, ungroupcó lẽ là cần thiết.
williamsurles

Điều gì xảy ra nếu bạn có nhiều biến nhóm hơn? Tôi nhận được một số lượng lớn các hàng (nhiều hơn nhiều so với khung dữ liệu ban đầu của tôi) nếu tôi sử dụng tất cả các vars nhóm từ group_by của mình
TobiO

1
Tôi đã tìm ra: Bạn phải sử dụng lồng ghép :-) Vì vậy, hãy đặt tất cả các Biến không nên được kết hợp với nhau vào complete(variablewithdroppedlevels, nesting(var1,var2,var3))(thực sự nó giúp completetôi vẫn mất một thời gian để tìm ra
TobiO

20

giải pháp dplyr:

Đầu tiên hãy tạo df được nhóm lại

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

sau đó, chúng tôi tóm tắt những cấp độ xảy ra bằng cách đếm với n()

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

sau đó, chúng tôi hợp nhất các kết quả của mình thành một khung dữ liệu có chứa tất cả các cấp yếu tố:

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

cuối cùng, trong trường hợp này vì chúng ta đang xem xét số lượng các NAgiá trị được thay đổi thành 0.

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

Điều này cũng có thể được thực hiện theo chức năng, xem câu trả lời: Thêm hàng vào dữ liệu được nhóm với dplyr?

Một vụ hack:

Tôi đã nghĩ rằng tôi sẽ đăng một bản hack khủng khiếp hoạt động trong trường hợp này vì lợi ích. Tôi thực sự nghi ngờ bạn có nên thực sự làm điều này nhưng nó cho thấy cách group_by()tạo ra các thuộc tính như thể df$blà một vector ký tự không phải là một yếu tố với các cấp. Ngoài ra, tôi không giả vờ hiểu điều này một cách chính xác - nhưng tôi hy vọng điều này sẽ giúp tôi học hỏi - đây là lý do duy nhất tôi đăng nó!

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

xác định giá trị "nằm ngoài giới hạn" không thể tồn tại trong tập dữ liệu.

oob_val <- nrow(by_b)+1

sửa đổi các thuộc tính thành "trick" summarise():

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

làm tóm tắt:

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

lập chỉ mục và thay thế tất cả các lần xuất hiện của oob_val

res[res == oob_val] <- 0

mang lại mục đích:

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

b count_a
1 1       6
2 2       6
3 3       0

11

đây không phải là chính xác những gì được hỏi trong câu hỏi, nhưng ít nhất đối với ví dụ đơn giản này, bạn có thể nhận được kết quả tương tự bằng cách sử dụng xtabs, ví dụ:

sử dụng dplyr:

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

hoặc ngắn hơn:

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

kết quả (bằng nhau trong cả hai trường hợp):

  b Freq
1 1    6
2 2    6
3 3    0
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.