Как переписать команду sapply для повышения производительности?

У меня есть data.frame с именем «d» из ~ 1 300 000 строк и 4 столбца и еще один data.frame с именем «gc» из ~ 12 000 строк и 2 столбца (но см. меньший пример ниже).

d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
gc <- data.frame( gene=c("a","b","c"), chr=c("c1","c2","c3") )

Вот как выглядит «д»:

   gene         val ind exp
1     a  1.38711902  i1  e1
2     b -0.25578496  i1  e1
3     c  0.49331256  i1  e1
4     a -1.38015272  i1  e2
5     b  1.46779219  i1  e2
6     c -0.84946320  i1  e2
7     a  0.01188061  i2  e1
8     b -0.13225808  i2  e1
9     c  0.16508404  i2  e1
10    a  0.70949804  i2  e2
11    b -0.64950167  i2  e2
12    c  0.12472479  i2  e2

А вот "гс":

  gene chr
1    a  c1
2    b  c2
3    c  c3

Я хочу добавить 5-й столбец в «d», включив данные из «gc», которые соответствуют 1-му столбцу «d». На данный момент я использую sapply.

d$chr <- sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )

Но на реальных данных это занимает «очень много времени» (я запускаю команду с «system.time ()» уже более 30 минут, и она все еще не завершена).

У вас есть идеи, как я мог бы переписать это по-умному? Или мне следует рассмотреть возможность использования plyr, возможно, с параметром «параллельный» (у меня на компьютере четыре ядра)? В таком случае, какой синтаксис был бы лучшим?

Заранее спасибо.


person tflutre    schedule 14.03.2011    source источник


Ответы (2)


Я думаю, вы можете просто использовать фактор как индекс:

gc[ d[,1], 2]
 [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3

делает то же самое, что и:

 sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
 [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3

Но гораздо быстрее:

> system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
   user  system elapsed 
   5.03    0.00    5.02 
> 
> system.time(replicate(1000,gc[ d[,1], 2]))
   user  system elapsed 
   0.12    0.00    0.13 

Редактировать:

Чтобы немного расширить мой комментарий. Фрейму данных gc требуется одна строка для каждого уровня gene в порядке уровней, чтобы это работало:

 d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
gc <- data.frame( gene=c("c","a","b"), chr=c("c1","c2","c3") )

gc[ d[,1], 2]
 [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3

sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
 [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3

Но исправить это не сложно:

levels(gc$gene) <- levels(d$gene) # Seems redundant as this is done right quite often automatically
gc <- gc[order(gc$gene),]


gc[ d[,1], 2]
 [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3

sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
 [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3
person Sacha Epskamp    schedule 14.03.2011
comment
Спасибо, это именно то, что мне нужно. - person tflutre; 14.03.2011
comment
У меня тоже нет :) Но есть одна загвоздка. То есть gc[,1] здесь должен иметь точно такой же коэффициент, что и d[,1], иметь только одну строку для каждого уровня, и каждый уровень должен быть в том же порядке. Хитрость в том, что множитель численно соответствует 1,2... - person Sacha Epskamp; 15.03.2011

Альтернативное решение, которое не превосходит подход Саши по времени, но является более обобщаемым и читабельным, состоит в том, чтобы просто merge два фрейма данных:

d <- merge(d, gc)

У меня более медленная система, поэтому вот мои тайминги:

> system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
   user  system elapsed 
  11.22    0.12   11.86 
> system.time(replicate(1000,gc[ d[,1], 2])) 
   user  system elapsed 
   0.34    0.00    0.35 
> system.time(replicate(1000, merge(d, gc, by="gene"))) 
   user  system elapsed 
   3.35    0.02    3.40 

Преимущество в том, что у вас может быть несколько ключей, точный контроль над несовпадающими элементами и т. д.

person Aniko    schedule 15.03.2011