Pertanyaan dplyr meringkas: Setara dari ".drop = FALSE" untuk menjaga kelompok dengan panjang nol dalam output


Ketika menggunakan summarise dengan plyr's ddply fungsi, kategori kosong dijatuhkan secara default. Anda dapat mengubah perilaku ini dengan menambahkan .drop = FALSE. Namun, ini tidak berfungsi saat menggunakan summarise dengan dplyr. Apakah ada cara lain untuk menjaga kategori kosong dalam hasil?

Berikut ini contoh dengan data palsu.

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

Tidak persis apa yang saya harapkan. Apakah ada dplyr metode untuk mencapai hasil yang sama seperti .drop=FALSE di plyr?


75
2018-03-20 03:52


asal


Jawaban:


Masalahnya masih terbuka, tetapi sementara itu, terutama karena data Anda sudah diperhitungkan, Anda dapat menggunakannya complete dari "tidyr" untuk mendapatkan apa yang mungkin Anda cari:

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

Jika Anda ingin nilai penggantian menjadi nol, Anda perlu menentukan itu dengan 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

44
2018-03-18 19:07



solusi dplyr:

Pertama buat grouped df

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

kemudian kami merangkum level-level yang terjadi dengan menghitung dengan n()

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

kemudian kami menggabungkan hasil kami ke dalam bingkai data yang berisi semua tingkat faktor:

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

akhirnya, dalam hal ini karena kita melihat menghitung NA nilai diubah menjadi 0.

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

Ini juga dapat diterapkan secara fungsional, lihat jawaban: Tambahkan baris ke data yang dikelompokkan dengan dplyr?

Peretasan:

Saya pikir saya akan memposting buruk hack yang bekerja dalam hal ini demi kepentingan. Saya benar-benar meragukan Anda seharusnya benar-benar melakukan ini tetapi itu menunjukkan bagaimana group_by() menghasilkan atribut seolah-olah df$b adalah vektor karakter bukan faktor dengan level. Juga, saya tidak berpura-pura memahami ini dengan benar - tetapi saya berharap ini membantu saya belajar - ini adalah satu-satunya alasan saya mempostingnya!

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

menentukan nilai "di luar batas" yang tidak bisa ada dalam kumpulan data.

oob_val <- nrow(by_b)+1

ubah atribut menjadi "trik" summarise():

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

lakukan ringkasannya:

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

indeks dan ganti semua kejadian dari oob_val

res[res == oob_val] <- 0

yang memberikan maksud:

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

b count_a
1 1       6
2 2       6
3 3       0

20
2018-05-24 17:11



ini bukan apa yang ditanyakan dalam pertanyaan, tapi setidaknya untuk contoh sederhana ini, Anda bisa mendapatkan hasil yang sama menggunakan xtabs, misalnya:

menggunakan dplyr:

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

atau lebih pendek:

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

hasil (sama dalam kedua kasus):

  b Freq
1 1    6
2 2    6
3 3    0

9
2018-05-05 18:46