Рассчитать для каждой строки процент переменной другой в data.table - PullRequest
0 голосов
/ 06 сентября 2018

Я ищу способ оптимизации агрегата в data.table, у меня есть несколько миллионов данных, и моя текущая реализация идет медленно.

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

library(data.table)
df <- data.table(Factor = as.factor(rep(LETTERS[1:3], 3)),
                 Variable = 1:9)

Текущая реализация:

aux <- df[, .(sumVar = sum(Variable)/sum(df$Variable)), by = .(Factor)]
df[aux, sumVar := sumVar, on = .(Factor = Factor)]

Желаемый выход:

> df
   Factor Variable    sumVar
1:      A        1 0.2666667
2:      B        2 0.3333333
3:      C        3 0.4000000
4:      A        4 0.2666667
5:      B        5 0.3333333
6:      C        6 0.4000000
7:      A        7 0.2666667
8:      B        8 0.3333333
9:      C        9 0.4000000

Я думаю, что моя проблема в merge, но я не знаю, как ее улучшить, я не знаком с dplyr, и я не нашел способа выполнить операцию за один шаг с data.table ,

Любая помощь приветствуется!

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Какие данные у вас есть и какие сроки вы ожидаете? В следующем примере с 100M строк я получаю следующие значения времени

library(data.table)

df <- data.table(
  Factor = as.factor(sample(LETTERS, size = 10^8, replace = T)),
  Variable = sample(10^3, size = 10^8, replace = T)
)

# data.table solution 1
system.time({
  aux <- df[, .(sumVar = sum(Variable)/sum(df$Variable)), by = .(Factor)]
  df[aux, sumVar := sumVar, on = .(Factor = Factor)]
})  # ~10.5 seconds

# data.table solution 2
system.time({
  df[, sumVar := sum(Variable)/sum(df$Variable), by = Factor]
})  # ~8.3 seconds

# dplyr solution 1
system.time({
  df %>% dplyr::group_by(Factor) %>% dplyr::mutate(A=sum(Variable)/sum(df$Variable))
})  # ~10.0 seconds

Обратите внимание, что ускорение data.table становится более впечатляющим по мере увеличения мощности фактора.

df <- data.table(
  Factor = as.factor(sample(as.character(10^6), size = 10^8, replace = T)),
  Variable = as.numeric(sample(10^3, size = 10^8, replace = T))
)

# data.table solution 1
system.time({
  aux <- df[, .(sumVar = sum(Variable)/sum(df$Variable)), by = .(Factor)]
  df[aux, sumVar := sumVar, on = .(Factor = Factor)]
})  # ~5.0 seconds

# data.table solution 2
system.time({
  df[, sumVar := sum(Variable)/sum(df$Variable), by = Factor]
})  # ~3.1 seconds

# dplyr solution 1
system.time({
  df %>% dplyr::group_by(Factor) %>% dplyr::mutate(A=sum(Variable)/sum(df$Variable))
})  # ~6.9 seconds
0 голосов
/ 06 сентября 2018

В вашем примере много повторений, поэтому не уверен, что я его интерпретировал. Тем не менее, кажется, лучше всего рассчитать знаменатель один раз и использовать gsum:

BigTotal <- df[, sum(Variable)]
df[, sumVar1 := sum(Variable), by = .(Factor)][, propVar := sumVar1 / BigTotal]

Примерно в два раза быстрее самого быстрого решения Бена.

df <- data.table(
  Factor = as.factor(sample(LETTERS, size = 10^8, replace = T)),
  Variable = sample(10^3, size = 10^8, replace = T)
)

microbenchmark::microbenchmark(dt1 = {
  aux <- df[, .(sumVar = sum(Variable)/sum(df$Variable)), keyby = .(Factor)]
  df[aux, sumVar := sumVar, on = .(Factor = Factor)]
},
dt2 = {
BigTotal <- df[, sum(Variable)]
df[, sumVar1 := sum(Variable), by = .(Factor)][, propVar := sumVar1 / BigTotal]
}, 
times = 2)


Unit: seconds
 expr      min       lq     mean   median       uq      max neval cld
  dt1 9.523696 9.523696 9.567555 9.567555 9.611414 9.611414     2   b
  dt2 3.996581 3.996581 4.521274 4.521274 5.045967 5.045967     2  a 
0 голосов
/ 06 сентября 2018

что-то вроде

df[ , ':='(sumVar = sum(Variable)/sum(df$Variable)), by = .(Factor)] 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...