Frage dplyr fazit: Äquivalent von ".drop = FALSE", um Gruppen mit der Länge Null in der Ausgabe zu behalten


Beim Benutzen summarise mit plyrist es ddply Funktion, leere Kategorien werden standardmäßig gelöscht. Sie können dieses Verhalten ändern, indem Sie hinzufügen .drop = FALSE. Dies funktioniert jedoch nicht bei der Verwendung summarise mit dplyr. Gibt es eine andere Möglichkeit, leere Kategorien im Ergebnis zu behalten?

Hier ist ein Beispiel mit gefälschten Daten.

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

Nicht genau das, was ich mir erhofft hatte. Gibt es ein dplyr Methode zum Erreichen des gleichen Ergebnisses wie .drop=FALSE im plyr?


75
2018-03-20 03:52


Ursprung


Antworten:


Das Problem ist noch offen, aber in der Zwischenzeit, zumal Ihre Daten bereits berücksichtigt sind, können Sie verwenden complete von "tidyr", um zu bekommen, wonach Sie suchen:

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

Wenn Sie möchten, dass der Ersatzwert null ist, müssen Sie dies mit angeben 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



dplyr Lösung:

Zuerst machen gruppierte df

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

dann fassen wir die Ebenen zusammen, die beim Zählen mit auftreten n()

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

dann fügen wir unsere Ergebnisse in einen Datenrahmen ein, der alle Faktorstufen enthält:

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

schließlich, in diesem Fall, da wir uns ansehen, zählt der NA Werte werden auf 0 geändert.

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

Dies kann auch funktional implementiert werden, siehe Antworten: Hinzufügen von Zeilen zu gruppierten Daten mit dplyr?

Ein Hack:

Ich dachte ich würde einen schicken furchtbar Hack, der in diesem Fall um des Interesses willen arbeitet. Ich bezweifle ernsthaft, dass du das jemals tun solltest, aber es zeigt, wie group_by() erzeugt die attributes als ob df$b war ein Zeichenvektor kein Faktor mit Ebenen. Ich gebe auch nicht vor, das richtig zu verstehen - aber ich hoffe, das hilft mir beim Lernen - das ist der einzige Grund, warum ich es poste!

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

Definieren Sie einen "Out-of-Bounds" -Wert, der im Dataset nicht vorhanden sein kann.

oob_val <- nrow(by_b)+1

Attribute zu "Trick" modifizieren summarise():

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

Mach die Zusammenfassung:

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

Index und ersetzen alle Vorkommen von oob_val

res[res == oob_val] <- 0

was gibt das beabsichtigte:

> 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



Dies ist nicht genau das, was in der Frage gestellt wurde, aber zumindest für dieses einfache Beispiel könnten Sie das gleiche Ergebnis mit Hilfe von xtabs erhalten, zum Beispiel:

mit dplyr:

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

oder kürzer:

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

Ergebnis (in beiden Fällen gleich):

  b Freq
1 1    6
2 2    6
3 3    0

9
2018-05-05 18:46