2015-07-17 10 views
7

Sto provando a raggruppare un gruppo di righe per un giorno in una singola riga. Mi piacerebbe in dplyr se possibile. So che il mio codice è tutt'altro che corretto, ma questo era quanto lontano ho ottenuto:R arrotolare le righe su una riga singola (variabili continue e fattore)

data %>% 
    group_by(DAY) %>% 
    summarise_each(funs(Sum = n()), SEX, GROUP, TOTAL) 

originale:

DAY SEX GROUP TOTAL  
7/1/14 FEMALE A 1  
7/1/14 FEMALE B 1  
7/1/14 FEMALE B 1  
7/1/14 FEMALE A 1  
7/1/14 MALE A 1  
7/1/14 MALE B 2  

Nuovo:

DAY  FEMALE MALE GROUP_A GROUP_B TOTAL 
7/1/14 4  2  3  3  7 
+0

Beh, a prima vista, il più facile sarebbe scriverlo 'data%>% group_by (DAY)%>% riepilogare (FEMALE = sum (SEX ==" FEMALE "), MALE = sum (SEX ==" MALE "), GROUP_A = sum (GROUP = = "A"), GROUP_B = sum (GROUP == "B"), TOTAL = sum (TOTAL)) '. Ma immagino che ti stia sforzando per l'eleganza del codice. :-) – lukeA

risposta

8

Un altro modo con data.table, testato su un data.frame con più di un giorno.

require(data.table) 
setDT(data)[, as.list(c(table(SEX), table(GROUP), TOTAL=sum(TOTAL))), by=DAY] 

#  DAY FEMALE MALE A B TOTAL 
#1: 7/1/14  3 0 1 2  3 
#2: 8/1/14  1 2 2 1  4 

EDIT: un altro, meno manuale, l'opzione (non è necessario sapere quali variabili sono fattori e che sono numerico), grazie a qualche aiuto da @jangorecki e @DavidArenburg

wh_num <- sapply(data, is.numeric)[-1] 
wh_fact <-sapply(data, is.factor)[-1] 
setDT(data)[, as.list(c(lapply(.SD[, wh_fact, with = FALSE], table), 
         lapply(.SD[, wh_num, with = FALSE], sum), 
         recursive = TRUE)), by = DAY] 

#  DAY SEX.FEMALE SEX.MALE GROUP.A GROUP.B TOTAL 
#1: 7/1/14   3  0  1  2  3 
#2: 8/1/14   1  2  2  1  4 

dati

data <- structure(list(DAY = c("7/1/14", "7/1/14", "7/1/14", "8/1/14", 
"8/1/14", "8/1/14"), SEX = structure(c(1L, 1L, 1L, 1L, 2L, 2L 
), .Label = c("FEMALE", "MALE"), class = "factor"), GROUP = structure(c(1L, 
2L, 2L, 1L, 1L, 2L), .Label = c("A", "B"), class = "factor"), 
    TOTAL = c(1L, 1L, 1L, 1L, 1L, 2L)), .Names = c("DAY", "SEX", 
"GROUP", "TOTAL"), row.names = c(NA, -6L), class = "data.frame") 
3

Il modo in cui si calcola il totale (sum) e le altre colonne (tabella) differiscono in modo sostanziale, quindi probabilmente devi fare questi passaggi separatamente. Calcolare il totale è facile. Per la tabulazione, io suggerisco di usare tidyr come segue:

# required packages 
require(dplyr) 
require(tidyr) 

# calculations 
data %>% 
    group_by(DAY) %>%      # group by day 
    mutate(TOTAL = sum(TOTAL)) %>%  # first calculate total 
    gather(key, value, -DAY, -TOTAL) %>% # collapse 
    unite(group, key, value) %>%   # get sensible column names 
    group_by(DAY, TOTAL) %>%    # group by day and total 
    do(as.data.frame(table(.$group))) %>% # table 
    spread(Var1, Freq)     # spread out 

##  DAY TOTAL GROUP_A GROUP_B SEX_FEMALE SEX_MALE 
## 1 7/1/14  7  3  3   4  2 
+1

Sono davvero sorpreso che tu abbia preferito questa complicata soluzione 'dplyr/tidyr' su una semplice soluzione' data.table' che hai postato molte volte in precedenza. –

+0

@DavidArenburg: di solito provo a rispondere con i pacchetti che l'OP sembra preferire. Ma sono d'accordo che in questo caso è preferibile la soluzione data.table (e l'ho svalutato). – shadow

5

Può sembrare un po 'arcano, ma qui è un breve incantesimo

dat %>% group_by(DAY) %>% 
    summarise_each(funs(ifelse(is.numeric(.), sum(.), list(table(.))))) -> res 

data.frame(DAY=res$DAY, t(unlist(res[, 2:ncol(res)]))) 
#  DAY SEX.FEMALE SEX.MALE GROUP.A GROUP.B TOTAL 
# 1 7/1/14   4  2  3  3  7 

Qui, è sufficiente riassumere ogni colonna come un tavolo se si tratta di non numerico, o sommarlo se lo è (per la colonna totale). Questo deve essere restituito come lista dal summarise_each si aspetta un singolo valore. Quindi, il risultato viene esteso a un normale data.frame.

+1

Non sono sicuro che funzioni correttamente per diverse date. Ad esempio se cambierai le ultime 3 date a '8/1/14'. –

+0

@DavidArenburg sì, è per il caso speciale di una singola riga. Potresti farlo funzionare per qualcosa di più, qualcosa sulla falsariga di unlist e racchiudere le righe insieme credo. – jenesaisquoi

+0

Dubito che @ user2434624 abbia solo un giorno nei dati originali ... – Cath

3

Un possibile approccio:

library(reshape2) 
library(data.table) 

cbind(dcast(df, DAY~SEX), 
     dcast(df, DAY~GROUP)[-1], 
     setDT(df)[,.(total=sum(TOTAL)),DAY][,-1,with=F]) 

#  DAY FEMALE MALE A B total 
#1 7/1/14  4 2 3 3  7 
Problemi correlati