R: объединить одинаковые идентификаторы в кадре данных

У меня есть фрейм данных с двумя столбцами, одним идентификатором и столбцом с именами. Каждый идентификатор несколько раз присутствует в идентификаторе столбца (см. ниже).

 ID           Names
uc001aag.1  DKFZp686C24272
uc001aag.1  DQ786314
uc001aag.1  uc001aag.1
uc001aah.2  AK056232
uc001aah.2  FLJ00038
uc001aah.2  uc001aah.1
uc001aah.2  uc001aah.2
uc001aai.1  AY217347

Теперь я хочу создать фрейм данных следующим образом:

 ID           Names
uc001aag.1  DKFZp686C24272 | DQ786314 | uc001aag.1
uc001aah.2  AK056232 | FLJ00038 | uc001aah.1 | uc001aah.2
uc001aai.1  AY217347

Может кто-нибудь помочь мне?


person Lisann    schedule 10.05.2011    source источник


Ответы (2)


Aggregate довольно быстрый, но вы можете использовать решение sapply для распараллеливания кода. Это можно легко сделать в Windows, используя snowfall :

require(snowfall)
sfInit(parallel=TRUE,cpus=2)
sfExport("Data")

ID <- unique(Data$ID)
CombNames <- sfSapply(ID,function(i){
    paste(Data$Names[Data$ID==i],collapse=" | ")
})
data.frame(ID,CombNames)
sfStop()

Параллельная версия даст вам дополнительное ускорение, но одиночное решение на самом деле медленнее, чем агрегированное. Tapply немного быстрее, но его нельзя распараллелить с помощью Snowfall. на моем компьютере :

n <- 3000
m <- 3
Data <- data.frame( ID = rep(1:n,m),
                    Names=rep(LETTERS[1:m],each=n))
 # using snowfall for parallel sapply    
 system.time({
   ID <- unique(Data$ID)
   CombNames <- sfSapply(ID,function(i){
     paste(Data$Names[Data$ID==i],collapse=" | ")
   })
   data.frame(ID,CombNames)
 }) 
   user  system elapsed 
   0.02    0.00    0.33 

 # using tapply
 system.time({
   CombNames <- tapply(Data$Names,Data$ID,paste,collapse=" | ")
   data.frame(ID=names(CombNames),CombNames)
 })
   user  system elapsed 
   0.44    0.00    0.44 

 # using aggregate
 system.time(
   aggregate(Names ~ ID, data=Data, FUN=paste, collapse=" | ")
 )
   user  system elapsed 
   0.47    0.00    0.47 

 # using the normal sapply
 system.time({
   ID <- unique(Data$ID)
   CombNames <- sapply(ID,function(i){
     paste(Data$Names[Data$ID==i],collapse=" | ")
   })
   data.frame(ID,CombNames)
 })
   user  system elapsed 
   0.75    0.00    0.75 

Примечание.

Для справки, лучшим sapply-решением будет:

CombNames <- sapply(split(Data$Names,Data$ID),paste,collapse=" | ")
data.frame(ID=names(CombNames),CombNames)

что эквивалентно постукиванию. Но распараллеливание этого на самом деле медленнее, так как вам нужно перемещать больше данных внутри sfSapply. Скорость достигается за счет копирования набора данных на каждый процессор. Это то, что вы должны иметь в виду, когда ваш набор данных огромен: вы заплатите за скорость большим использованием памяти.

person Joris Meys    schedule 10.05.2011

Вы можете использовать aggregate:

R> aggregate(Names ~ ID, data=tmp, FUN=paste, collapse=" | ")
          ID                                         Names
1 uc001aag.1        DKFZp686C24272 | DQ786314 | uc001aag.1
2 uc001aah.2 AK056232 | FLJ00038 | uc001aah.1 | uc001aah.2
3 uc001aai.1                                      AY217347
person rcs    schedule 10.05.2011
comment
@rcs, этот метод отлично работает, но у меня очень большой набор данных. есть ли способ ускорить анализ? Спасибо - person Lisann; 10.05.2011
comment
Возможно распараллеливание с ddply из пакета plyr: ddply(tmp, .(ID), function(x) paste(x$Names, collapse=" | "), .parallel=TRUE) - person rcs; 10.05.2011
comment
Этот код из пакета plyr дает мне эту ошибку: Загрузка требуемого пакета: foreach Ошибка: пакет foreach требуется для параллельной работы plyr Кроме того: Предупреждающее сообщение: В библиотеке (пакет, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE, : нет пакета с именем foreach - person Lisann; 10.05.2011
comment
Требуется пакет foreach. Кроме того, вам необходимо зарегистрировать параллельный бэкэнд (т. е. doMC, недоступно для платформ Windows). См. также здесь: /5621388#5621388" title="domc vs dosnow vs dosmp vs dompi почему нет различных параллельных серверных частей для f"> stackoverflow.com/questions/5588914/ - person rcs; 10.05.2011
comment
В каталоге был файл LOCK00, поэтому пакет foreach не мог быть загружен. Это исправлено, но анализ по-прежнему идет медленно. Файл содержит 2 столбца и 450000 строк. - person Lisann; 10.05.2011
comment
@Lisann tapply(tmp$Names,tmp$ID,paste,collapse=" | ") немного быстрее, но в любом случае в этом масштабе это работа для кода C. - person mbq; 10.05.2011
comment
tapply можно изменить для параллельной работы с помощью snowfall. - person Roman Luštrik; 10.05.2011
comment
Спасибо за предложение, я попробую! знак равно - person Lisann; 10.05.2011
comment
Я не уверен, насколько поможет какой-либо из трюков с распараллеливанием. Проблема с ddply в этом случае (450 000 строк объединяются примерно в 200 000) заключается в шаге объединения. Раздельный шаг, вероятно, не так уж дорог, и паста тоже не должна быть слишком плохой. Но ddply многократно выполняет rbind для результирующих небольших data.frames, что является катастрофой, потому что ему приходится каждый раз повторно оптимизировать хранилище строк. (Я думаю?) Может быть, лучше использовать dlply, каждый раз возвращая только вставленную строку, а затем unlist(), чтобы свернуть в вектор. Затем вставьте обратно имена идентификаторов. - person Harlan; 10.05.2011