более быстрый способ создания переменной, которая агрегирует столбец по идентификатору

performance r aggregate plyr

2943 просмотра

6 ответа

414 Репутация автора

Есть ли более быстрый способ сделать это? Я думаю, что это не нужно медленно и что такую ​​задачу можно выполнить с помощью базовых функций.

df <- ddply(df, "id", function(x) cbind(x, perc.total = sum(x$cand.perc)))

Я совсем новичок в R. Я смотрел by(), aggregate()и tapply(), но не заставил их работать вообще или так, как я хотел. Вместо того, чтобы возвращать более короткий вектор, я хочу прикрепить сумму к исходному кадру данных. Каков наилучший способ сделать это?

Изменить: Вот сравнение скорости ответов, примененных к моим данным.

> # My original solution
> system.time( ddply(df, "id", function(x) cbind(x, perc.total = sum(x$cand.perc))) )
   user  system elapsed 
 14.405   0.000  14.479 

> # Paul Hiemstra
> system.time( ddply(df, "id", transform, perc.total = sum(cand.perc)) )
   user  system elapsed 
 15.973   0.000  15.992 

> # Richie Cotton
> system.time( with(df, tapply(df$cand.perc, df$id, sum))[df$id] )
   user  system elapsed 
  0.048   0.000   0.048 

> # John
> system.time( with(df, ave(cand.perc, id, FUN = sum)) )
       user  system elapsed 
      0.032   0.000   0.030 

> # Christoph_J
> system.time( df[ , list(perc.total = sum(cand.perc)), by="id"][df])
   user  system elapsed 
  0.028   0.000   0.028 
Автор: ilprincipe Источник Размещён: 22.11.2011 10:54

Ответы (6)


3 плюса

85398 Репутация автора

Используйте tapplyдля получения групповой статистики, затем добавьте ее обратно в набор данных.

Воспроизводимый пример:

means_by_wool <- with(warpbreaks, tapply(breaks, wool, mean))
warpbreaks$means.by.wool <- means_by_wool[warpbreaks$wool]

Непроверенное решение для вашего сценария:

sum_by_id <- with(df, tapply(cand.perc, id, sum))
df$perc.total <- sum_by_id[df$id]
Автор: Richie Cotton Размещён: 22.11.2011 11:28

0 плюса

50688 Репутация автора

Почему вы используете cbind (x, ...), вывод ddply будет добавлен автоматически. Это должно работать:

ddply(df, "id", transform, perc.total = sum(cand.perc))

избавление от лишнего cbind должно ускорить процесс.

Автор: Paul Hiemstra Размещён: 22.11.2011 11:32

6 плюса

20119 Репутация автора

Решение

Для любого вида агрегации, где вы хотите, чтобы результирующий вектор имел ту же длину, что и входной вектор с репликами, сгруппированными по вектору группировки, ave- это то, что вам нужно.

df$perc.total <- ave(df$cand.perc, df$id, FUN = sum)
Автор: John Размещён: 22.11.2011 12:18

0 плюса

100 Репутация автора

ilprincipe если ничего из вышеперечисленного не соответствует вашим потребностям, вы можете попробовать перенести ваши данные

dft=t(df)

затем используйте aggregate

dfta=aggregate(dft,by=list(rownames(dft)),FUN=sum)

затем вернулись ваши имена строк

rownames(dfta)=dfta[,1]
dfta=dfta[,2:ncol(dfta)]

Транспонировать обратно в исходное положение

df2=t(dfta)

и привязать к исходным данным

newdf=cbind(df,df2)
Автор: boczniak767 Размещён: 22.11.2011 12:50

12 плюса

4191 Репутация автора

Поскольку вы довольно плохо знакомы с R и скорость, по-видимому, является проблемой для вас, я рекомендую data.tableпакет, который действительно быстр. Один из способов решить вашу проблему в одной строке:

library(data.table)
DT <- data.table(ID = rep(c(1:3), each=3),
                 cand.perc = 1:9,
                 key="ID")
DT <- DT[ , perc.total := sum(cand.perc), by = ID]
DT
      ID Perc.total cand.perc
 [1,]  1          6         1
 [2,]  1          6         2
 [3,]  1          6         3
 [4,]  2         15         4
 [5,]  2         15         5
 [6,]  2         15         6
 [7,]  3         24         7
 [8,]  3         24         8
 [9,]  3         24         9

Отказ от ответственности: я не эксперт по data.table (пока ;-), так что могут быть более быстрые способы сделать это. Посетите сайт пакета, чтобы начать, если вы заинтересованы в использовании пакета: http://datatable.r-forge.r-project.org/

Автор: Christoph_J Размещён: 22.11.2011 01:03

0 плюса

14981 Репутация автора

Вы также можете загрузить свой любимый сервер foreach и попробовать аргумент .parallel = TRUE для ddply.

Автор: Zach Размещён: 23.11.2011 02:54
Вопросы из категории :
32x32