Raggruppa per colonne multiple in dplyr, usando l’input vettore di stringhe

Sto cercando di trasferire la mia comprensione di plyr in dplyr, ma non riesco a capire come raggruppare per colonne multiple.

# make data with weird column names that can't be hard coded data = data.frame( asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # get the columns we want to average within columns = names(data)[-3] # plyr - works ddply(data, columns, summarize, value=mean(value)) # dplyr - raises error data %.% group_by(columns) %.% summarise(Value = mean(value)) #> Error in eval(expr, envir, enclos) : index out of bounds 

Cosa mi manca per tradurre l’esempio plyr in una syntax dplyr-esque?

Modifica 2017 : Dplyr è stato aggiornato, quindi è disponibile una soluzione più semplice. Vedi la risposta attualmente selezionata.

Poiché questa domanda è stata pubblicata, dplyr ha aggiunto le versioni con scope di group_by ( documentazione qui ). Questo ti permette di usare le stesse funzioni che useresti con select , in questo modo:

 data = data.frame( asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # get the columns we want to average within columns = names(data)[-3] library(dplyr) df1 <- data %>% group_by_at(vars(one_of(columns))) %>% summarize(Value = mean(value)) #compare plyr for reference df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value)) table(df1 == df2, useNA = 'ifany') ## TRUE ## 27 

L'output della tua domanda di esempio è come previsto (vedi confronto con plyr sopra e output sotto):

 # A tibble: 9 x 3 # Groups: asihckhdoydkhxiydfgfTgdsx [?] asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value    1 AA 0.04095002 2 AB 0.24943935 3 AC -0.25783892 4 BA 0.15161805 5 BB 0.27189974 6 BC 0.20858897 7 CA 0.19502221 8 CB 0.56837548 9 CC -0.22682998 

Nota che dal momento che dplyr::summarize solo uno strato di raggruppamento alla volta, hai ancora alcuni raggruppamenti in corso nel tibble risultante (che a volte può catturare le persone di sorpresa più tardi lungo la linea). Se si desidera essere assolutamente al sicuro da comportamenti di raggruppamento imprevisti, è sempre ansible aggiungere %>% ungroup alla pipeline dopo averlo riepilogato.

Giusto per scrivere il codice per intero, ecco un aggiornamento sulla risposta di Hadley con la nuova syntax:

 library(dplyr) df <- data.frame( asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # Columns you want to group by grp_cols <- names(df)[-3] # Convert character vector to list of symbols dots <- lapply(grp_cols, as.symbol) # Perform frequency counts df %>% group_by_(.dots=dots) %>% summarise(n = n()) 

produzione:

 Source: local data frame [9 x 3] Groups: asihckhdoydk asihckhdoydk a30mvxigxkgh n 1 AA 10 2 AB 10 3 AC 13 4 BA 14 5 BB 10 6 BC 12 7 CA 9 8 CB 12 9 CC 10 

Il supporto per questo in dplyr è attualmente piuttosto debole, alla fine penso che la syntax sarà qualcosa del tipo:

 df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja")) 

Ma probabilmente non ci sarà per un po ‘(perché ho bisogno di pensare a tutte le conseguenze).

Nel frattempo, puoi usare regroup() , che prende una lista di simboli:

 library(dplyr) df <- data.frame( asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) df %.% regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.% summarise(n = n()) 

Se si dispone di un vettore di caratteri di nomi di colonne, è ansible convertirli nella struttura corretta con lapply() e as.symbol() :

 vars <- setdiff(names(df), "value") vars2 <- lapply(vars, as.symbol) df %.% regroup(vars2) %.% summarise(n = n()) 

Le specifiche delle stringhe delle colonne in dplyr ora sono supportate attraverso le varianti delle funzioni dplyr con nomi che terminano in un trattino basso. Ad esempio, corrispondente alla funzione group_by esiste una funzione group_by_ che può assumere argomenti stringa. Questa vignetta descrive la syntax di queste funzioni in dettaglio.

Lo snippet seguente risolve in modo pulito il problema che @sharoz aveva originariamente posto (notare la necessità di scrivere l’argomento .dots ):

 # Given data and columns from the OP data %>% group_by_(.dots = columns) %>% summarise(Value = mean(value)) 

(Nota che dplyr ora usa l’operatore %>% e %.% È deprecato).

Fino a quando dplyr ha il pieno supporto per gli argomenti stringa, forse questo gist è utile:

https://gist.github.com/skranz/9681509

Contiene un sacco di funzioni wrapper come s_group_by, s_mutate, s_filter, ecc. Che usano argomenti di stringa. È ansible combinarli con le normali funzioni dplyr. Per esempio

 cols = c("cyl","gear") mtcars %.% s_group_by(cols) %.% s_summarise("avdisp=mean(disp), max(disp)") %.% arrange(avdisp) 

Funziona se gli passi gli oggetti (beh, non lo sei, ma …) piuttosto che come vettore di caratteri:

 df %.% group_by(asdfgfTgdsx, asdfk30v0ja) %.% summarise(Value = mean(value)) > df %.% + group_by(asdfgfTgdsx, asdfk30v0ja) %.% + summarise(Value = mean(value)) Source: local data frame [9 x 3] Groups: asdfgfTgdsx asdfgfTgdsx asdfk30v0ja Value 1 AC 0.046538002 2 CB -0.286359899 3 BA -0.305159419 4 CA -0.004741504 5 BB 0.520126476 6 CC 0.086805492 7 BC -0.052613078 8 AA 0.368410146 9 AB 0.088462212 

dove df erano i tuoi data .

?group_by dice:

  ...: variables to group by. All tbls accept variable names, some will also accept functons of variables. Duplicated groups will be silently dropped. 

che interpreto per significare non le versioni dei caratteri dei nomi, ma come faresti riferimento a loro in foo$bar ; bar non è citata qui. O come faresti riferimento a variabili in una formula: foo ~ bar .

@Arun dice anche che puoi fare:

 df %.% group_by("asdfgfTgdsx", "asdfk30v0ja") %.% summarise(Value = mean(value)) 

Ma non è ansible passare a qualcosa che non valutato non è un nome di una variabile nell’object dati.

Presumo che ciò sia dovuto ai metodi interni che Hadley sta usando per cercare le cose che passi attraverso l’argomento ...

 data = data.frame( my.a = sample(LETTERS[1:3], 100, replace=TRUE), my.b = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value)) 

Un caso (minuscolo) che manca nelle risposte qui, che volevo rendere esplicito, è quando le variabili da raggruppare vengono generate dynamicmente a metà strada in una pipeline:

 library(wakefield) df_foo = r_series(rnorm, 10, 1000) df_foo %>% # 1. create quantized versions of base variables mutate_each( funs(Quantized = . > 0) ) %>% # 2. group_by the indicator variables group_by_( .dots = grep("Quantized", names(.), value = TRUE) ) %>% # 3. summarize the base variables summarize_each( funs(sum(., na.rm = TRUE)), contains("X_") ) 

Questo mostra in pratica come usare grep in combinazione con group_by_(.dots = ...) per ottenere questo risultato.