2011-12-13 7 views
7

Vorrei aggregare uno data.frame con una variabile identificativa chiamata ensg. Il frame di dati è simile al seguente:Modo elegante per risolvere il task ddply con aggregato (sperando in prestazioni migliori)

chromosome probeset    ensg symbol XXA_00 XXA_36 XXB_00 
1   X 4938842 ENSMUSG00000000003 Pbsn 4.796123 4.737717 5.326664 

voglio calcolare la media per ogni colonna numerica su righe con lo stesso valore ensg. Il problema qui è che mi piacerebbe lasciare intatte le altre variabili di identità cromosomiche e simboli poiché sono uguali per lo stesso ensg.

Alla fine vorrebbe avere un data.frame con colonne di identità chromosome, ensg, symbol e media di colonne numeriche su righe con stesso identificatore. Ho implementato questo in ddply, ma è molto lento rispetto al aggregate:

spec.mean <- function(eset.piece) 
    { 
    cbind(eset.piece[1,-numeric.columns],t(colMeans(eset.piece[,numeric.columns]))) 
    } 
t 
mean.eset <- ddply(eset.consensus.grand,.(ensg),spec.mean,.progress="tk") 

La mia prima implementazione aggregato si presenta così,

mean.eset=aggregate(eset[,numeric.columns], by=list(eset$ensg), FUN=mean, na.rm=TRUE); 

ed è molto più veloce. Ma il problema con aggregate è che devo ricollegare le variabili descrittive. Non ho capito come utilizzare la mia funzione personalizzata con aggregate poiché aggregate non passa i frame di dati ma solo i vettori.

C'è un modo elegante per farlo con aggregate? O c'è un modo più veloce per farlo con ddply?

risposta

7

Prima definiamo un esempio giocattolo:

df <- data.frame(chromosome = gl(3, 10, labels = c('A', 'B', 'C')), 
      probeset = gl(3, 10, labels = c('X', 'Y', 'Z')), 
      ensg = gl(3, 10, labels = c('E1', 'E2', 'E3')), 
      symbol = gl(3, 10, labels = c('S1', 'S2', 'S3')), 
      XXA_00 = rnorm(30), 
      XXA_36 = rnorm(30), 
      XXB_00 = rnorm(30)) 

E poi si usa aggregate con l'interfaccia formula:

df1 <- aggregate(cbind(XXA_00, XXA_36, XXB_00) ~ ensg + chromosome + symbol, 
    data = df, FUN = mean) 

> df1 
    ensg chromosome symbol  XXA_00  XXA_36  XXB_00 
1 E1   A  S1 -0.02533499 -0.06150447 -0.
2 E2   B  S2 -0.25165987 0.02494902 -0.01116426 
3 E3   C  S3 0.09454154 -0.48468517 -0.25644569 
+1

Grazie per questa bellissima soluzione! È anche fulmineo rispetto a ddply. Non sapevo della potenza dell'interfaccia formula! Ho usato R per un paio di mesi, ma continua a sorprendermi (in senso buono). In modo imbarazzante, mi manca ancora la reputazione di votare la soluzione ... – Johannes

+3

@Johannes: FWIW, l'interfaccia della formula è più lenta delle altre. –

+1

Ora ho adattato la soluzione postata [qui] (http://stackoverflow.com/a/3685919/1093951) di Hadley.Si basa sulla conversione dei dati in una matrice prima di applicare la media che porta ad un enorme aumento di velocità. Con il mio set di dati di dimensioni 10^5 x 43 la velocità aumenta di un fattore di 25! – Johannes

8

Se la velocità è una preoccupazione primaria, si dovrebbe dare uno sguardo al data.table pacchetto. Quando il numero di righe o colonne di raggruppamento è grande, data.table sembra davvero brillare. Il wiki per il pacchetto è here e ha diversi collegamenti ad altri buoni documenti introduttivi.

Ecco come si farebbe questa aggregazione con data.table()

library(data.table) 
#Turn the data.frame above into a data.table 
dt <- data.table(df) 
#Aggregation 

    dt[, list(XXA_00 = .Internal(mean(XXA_00)), 
      XXA_36 = .Internal(mean(XXA_36)), 
      XXB_00 = .Internal(mean(XXB_00))), 
    by = c("ensg", "chromosome", "symbol") 
    ] 

ci dà

 ensg chromosome symbol  XXA_00  XXA_36 XXB_00 
[1,] E1   A  S1 0.18026869 0.13118997 0.6558433 
[2,] E2   B  S2 -0.48830539 0.24235537 0.5971377 
[3,] E3   C  S3 -0.04786984 -0.03139901 0.5618208 

La soluzione aggregati forniti di cui sopra sembra a tariffa abbastanza bene quando si lavora con il data.frame 30 fila confrontare l'output dal pacchetto rbenchmark. Tuttavia, quando data.frame contiene 3e5 righe, data.table() viene eliminato come vincente. Ecco l'output:

benchmark(fag(), fdt(), replications = 10) 
    test replications elapsed relative user.self sys.self 
1 fag()   10 12.71 23.98113  12.40  0.31  
2 fdt()   10 0.53 1.00000  0.48  0.05   
+0

Non è un fan di incoraggiare gli utenti a chiamare le funzioni tramite '.Internal' in modo interattivo e dubito che ciò faccia molta differenza in questo caso. –

+1

@JoshuaUlrich - Non sono mai stato morso da '.Internal' quindi non posso dire in un modo o nell'altro. La pagina wiki per 'data.table' mostra un miglioramento della velocità di quasi 10 volte per' .Internal (mean()) 'su' mean() 'nel loro caso di test. Ecco dove ho preso quell'abitudine (forse brutta). Aggiornerò il test usando lo standard 'mean()' e vedremo cosa succede realmente. – Chase

+2

Potresti usare "mean.default" invece di "mean" per evitare l'invio del metodo e questo ti farebbe risparmiare un po 'di tempo. Non mi dispiace mostrare '.Internal' come un'opzione _ finché viene seguito con un avvertimento" here be dragons ". –

Problemi correlati