dplyr riepilogare: Equivalente di “.drop = FALSE” per mantenere i gruppi con lunghezza zero in output

Quando si utilizza il summarise con la funzione ddply , le categorie vuote vengono eliminate per impostazione predefinita. Puoi cambiare questo comportamento aggiungendo .drop = FALSE . Tuttavia, questo non funziona quando si usa il summarise con dplyr . C’è un altro modo per mantenere le categorie vuote nel risultato?

Ecco un esempio con dati falsi.

 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 

Non esattamente quello che speravo. Esiste un metodo dplyr per ottenere lo stesso risultato di .drop=FALSE in plyr ?

Il problema è ancora aperto, ma nel frattempo, soprattutto dal momento che i tuoi dati sono già presi in considerazione, puoi utilizzare complete da “tidyr” per ottenere ciò che stai cercando:

 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 

Se si desidera che il valore di sostituzione sia zero, è necessario specificare che con 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 

soluzione dplyr:

Per prima cosa raggruppa df

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

quindi riassumiamo quei livelli che si verificano contando con n()

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

quindi uniamo i nostri risultati in un frame di dati che contiene tutti i livelli di fattore:

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

infine, in questo caso, visto che stiamo guardando i conteggi, i valori di NA sono cambiati a 0.

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

Questo può anche essere implementato funzionalmente, vedere le risposte: aggiungere le righe ai dati raggruppati con dplyr?

Un hack:

Ho pensato di pubblicare un terribile hack che funziona in questo caso per interesse. Dubito seriamente che dovresti farlo davvero, ma mostra come group_by() genera gli atrributes come se df$b fosse un vettore di caratteri non un fattore con livelli. Inoltre, non pretendo di capirlo correttamente - ma spero che questo mi aiuti a imparare - questo è l'unico motivo per cui lo sto postando!

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

definire un valore "fuori dai limiti" che non può esistere nel set di dati.

 oob_val < - nrow(by_b)+1 

modifica attributi a "trucco" summarise() :

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

fai il riassunto:

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

indicizza e sostituisci tutte le occorrenze di oob_val

 res[res == oob_val] < - 0 

che dà l'intenzione:

 > res Source: local data frame [3 x 2] b count_a 1 1 6 2 2 6 3 3 0 

questo non è esattamente ciò che è stato chiesto nella domanda, ma almeno per questo semplice esempio, è ansible ottenere lo stesso risultato usando xtabs, ad esempio:

usando dplyr:

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

o più breve:

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

risultato (uguale in entrambi i casi):

  b Freq 1 1 6 2 2 6 3 3 0