Элегантный способ решения задачи ddply с помощью агрегата (в надежде на лучшую производительность) - PullRequest
7 голосов
/ 13 декабря 2011

Я бы хотел агрегировать data.frame по переменной-идентификатору с именем ensg. Фрейм данных выглядит следующим образом:

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

Я хочу вычислить среднее значение для каждого числового столбца по строкам с одинаковым значением ensg. Проблема здесь в том, что я хотел бы оставить другие переменные идентичности хромосомой и символом нетронутыми, поскольку они также одинаковы для одинаковых ensg.

В конце я хотел бы получить data.frame со столбцами идентификаторов chromosome, ensg, symbol и средним числом числовых столбцов над строками с одинаковым идентификатором. Я реализовал это в ddply, но это очень медленно по сравнению с 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")

Моя первая агрегатная реализация выглядит так,

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

и намного быстрее. Но проблема с aggregate заключается в том, что мне нужно заново присоединить описывающие переменные. Я не понял, как использовать мою пользовательскую функцию с aggregate, поскольку aggregate не пропускает фреймы данных, а только векторы.

Есть ли элегантный способ сделать это с aggregate? Или есть какой-нибудь более быстрый способ сделать это с ddply?

Ответы [ 2 ]

8 голосов
/ 13 декабря 2011

Если скорость имеет первостепенное значение, вам следует взглянуть на пакет data.table.Когда количество строк или группирующих столбцов велико, data.table действительно светится.Вики для пакета: здесь и содержит несколько ссылок на другие хорошие вводные документы.

Вот как вы бы сделали это объединение с 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")
   ]

Дает нам

     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

Представленное выше агрегатное решение, кажется, работает очень хорошо при работе с 30-строчным data.frame, сравнивая выходные данные из пакета rbenchmark .Однако, когда data.frame содержит 3e5 строк, data.table() удаляется как явный победитель.Вот вывод:

 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         
7 голосов
/ 13 декабря 2011

Сначала давайте определим игрушечный пример:

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))

И затем мы используем aggregate с интерфейсом формулы:

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.01234508
2   E2          B     S2 -0.25165987  0.02494902 -0.01116426
3   E3          C     S3  0.09454154 -0.48468517 -0.25644569
...