медленная функция по группам в data.table r - PullRequest
1 голос
/ 20 марта 2019

В моем экспериментальном проекте деревья измерены в разных лесах с повторными измерениями в разные годы

DT <- data.table(forest=rep(c("a","b"),each=6),
                    year=rep(c("2000","2010"),each=3),
                    id=c("1","2","3"),
                    size=(1:12))

DT[,id:=paste0(forest,id)]

> DT
    forest year id size
 1:     a 2000 a1     1
 2:     a 2000 a2     2
 3:     a 2000 a3     3
 4:     a 2010 a1     4
 5:     a 2010 a2     5
 6:     a 2010 a3     6
 7:     b 2000 b1     7
 8:     b 2000 b2     8
 9:     b 2000 b3     9
10:     b 2010 b1    10
11:     b 2010 b2    11
12:     b 2010 b3    12

Для каждого дерева i я хочу вычислить новую переменную, равную суммированию размера всех других людей в той же группе / году, которые больше, чем дерево i.

Я создал следующую функцию:

f.new <- function(i,n){ 
 DT[forest==DT[id==i, unique(forest)] & year==n # select the same forest & year of the tree i
 & size>DT[id==i & year==n, size], # select the trees larger than the tree i
 sum(size, na.rm=T)] # sum the sizes of all such selected trees
}

При применении в таблице данных я получил правильные результаты.

       DT[,new:=f.new(id,year), by=.(id,year)]

> DT
    forest year id size new
 1:     a 2000 a1     1   5
 2:     a 2000 a2     2   3
 3:     a 2000 a3     3   0
 4:     a 2010 a1     4  11
 5:     a 2010 a2     5   6
 6:     a 2010 a3     6   0
 7:     b 2000 b1     7  17
 8:     b 2000 b2     8   9
 9:     b 2000 b3     9   0
10:     b 2010 b1    10  23
11:     b 2010 b2    11  12
12:     b 2010 b3    12   0

Обратите внимание, что у меня есть большой набор данных с несколькими лесами (40) и повторными годами (6) и одиночными индивидуумами (20 000), в общей сложности почти 50 000 измерений. Когда я выполняю вышеуказанную функцию, это занимает 8-10 минут (Windows 7, процессор i5-6300U при 2,40 ГГц, 2,40 ГГц, ОЗУ 8 ГБ). Мне нужно повторять это часто с несколькими небольшими изменениями, и это занимает много времени.

  1. Есть ли более быстрый способ сделать это? Я проверил * apply функции, но не могу найти решение на их основе.
  2. Могу ли я создать универсальную функцию, которая не зависит от конкретной структуры набора данных (то есть я мог бы использовать в качестве «размера» различные столбцы)?

1 Ответ

2 голосов
/ 20 марта 2019

Просто отсортируйте данные, и это может быть очень быстро:

setorder(DT, forest, year, -size)
DT[, new := cumsum(size) - size, by = .(forest, year)]
setorder(DT, forest, year, id)
DT
#    forest year id size new
# 1:      a 2000 a1    1   5
# 2:      a 2000 a2    2   3
# 3:      a 2000 a3    3   0
# 4:      a 2010 a1    4  11
# 5:      a 2010 a2    5   6
# 6:      a 2010 a3    6   0
# 7:      b 2000 b1    7  17
# 8:      b 2000 b2    8   9
# 9:      b 2000 b3    9   0
#10:      b 2010 b1   10  23
#11:      b 2010 b2   11  12
#12:      b 2010 b3   12   0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...