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
? 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
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
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
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