dplyr: odpowiednik ".drop = FALSE " aby zachować grupy o zerowej długości na wyjściu

Podczas używania summarise z plyr'S ddply funkcja, puste kategorie są domyślnie usuwane. Możesz zmienić to zachowanie dodając .drop = FALSE. Nie działa to jednak podczas używania summarise z dplyr. Czy istnieje inny sposób na zachowanie pustych kategorii w wyniku?

Oto przykład z fałszywymi danymi.

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
Nie do końca na to liczyłem. Czy istnieje dplyr metoda osiągnięcia tego samego wyniku co .drop=FALSE w plyr?
Author: eipi10, 2014-03-20

3 answers

Problem jest nadal otwarty, ale w międzyczasie, zwłaszcza, że Twoje dane są już uwzględnione, możesz użyć complete z "tidyr", aby uzyskać to, czego możesz szukać:

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

Jeśli chcesz, aby wartość zastępcza wynosiła zero, musisz to określić za pomocą 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
 46
Author: A5C1D2H2I1M1N2O1R2T1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-18 19:07:51

Rozwiązanie Dplyr:

Najpierw Utwórz grupę df

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

Następnie podsumujemy te poziomy, które występują, licząc za pomocą n()

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

Następnie łączymy nasze wyniki w ramkę danych, która zawiera wszystkie poziomy czynników:

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

Wreszcie, w tym przypadku, ponieważ patrzymy na liczy NA wartości są zmieniane na 0.

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

Można to również zaimplementować funkcjonalnie, Zobacz odpowiedzi: dodać wiersze do zgrupowanych danych za pomocą dplyr?

A hack:

Pomyślałem, że opublikuję straszny hack, który działa w tym przypadku ze względu na zainteresowanie. Szczerze wątpię, czy kiedykolwiek powinieneś to zrobić, ale pokazuje to, jak group_by() generuje atrybuty, tak jakby df$b był wektorem znaków, a nie czynnikiem z poziomami. Poza tym nie udaję, że dobrze to rozumiem , ale mam nadzieję, że to pomoże mi się nauczyć - to jedyny powód, dla którego to zamieszczam!

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

Zdefiniuj wartość "poza granicami", która nie może istnieć w zestaw danych.

oob_val <- nrow(by_b)+1

Zmień atrybuty na "trick" summarise():

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

Zrób podsumowanie:

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

Indeks i Zastąp wszystkie wystąpienia oob_val

res[res == oob_val] <- 0

Co daje zamierzony:

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

b count_a
1 1       6
2 2       6
3 3       0
 20
Author: npjc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:11

Nie jest to dokładnie to, co zostało zadane w pytaniu, ale przynajmniej w tym prostym przykładzie można uzyskać ten sam wynik za pomocą xtabs, na przykład:

Używając dplyr:

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

Lub krótszy:

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

Wynik (równy w obu przypadkach):

  b Freq
1 1    6
2 2    6
3 3    0
 9
Author: docendo discimus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-05 19:12:45