Как перебрать все комбинации столбцов и применить функцию по группам в R?

road_to_quantdom спросил: 07 октября 2018 в 10:56 в: r

У меня есть следующий data.table с именем dt

  set.seed(1)
  dt <- data.table(expand.grid(c("a","b"),1:2,1:2,c("M","N","O","P","Q")))
  dt$perf <- rnorm(nrow(dt),0,.01)
  colnames(dt) <- c("ticker","par1","par2","row_names","perf")

Моя цель - перебрать все комбинации par1 и par2 с помощью row_names и выберите тот, который максимизирует cumprod(mean(perf)+1)-1. Давайте посмотрим на данные, так что это будет более понятным визуально.

dt[order(row_names,ticker,par1,par2)]
    ticker par1 par2 row_names         perf
 1:      a    1    1         M  0.011462284
 2:      a    1    2         M -0.004252677
 3:      a    2    1         M  0.005727396
 4:      a    2    2         M -0.003892372
 5:      b    1    1         M -0.024030962
 6:      b    1    2         M  0.009510128
 7:      b    2    1         M  0.003747244
 8:      b    2    2         M -0.002843307

Для каждого ticker и row_names у нас есть 2 x 2 = 4 комбинации par1 и par2, а именно (1,1) (1,2) (2,1) (2,2).

я бы хотел вычислить mean для perf, связанного с ticker = a, par1 = 1, par2 = 1, с помощью все perf, связанные со всеми другими комбинациями для ticker = b. Используя числа из изображения выше,

res
       a_perf       b_perf
1: 0.01146228 -0.024030962
2: 0.01146228  0.009510128
3: 0.01146228  0.003747244
4: 0.01146228 -0.002843307apply(res,1,mean)
[1] -0.006284339  0.010486206  0.007604764  0.004309488

Затем мы повторим этот процесс для ticker = a, par1 = 1, par2 = 2 со всеми другими комбинациями для ticker = b.

Мы повторим этот процесс для всех комбинаций par1 и par2 с каждым row_names.

РЕДАКТИРОВАТЬ ::: Используя предложение @ earch, мы получаем следующее:

tmp <- lapply(split(dt, dt$row_names), calcCombMeans)
$M
   a.row b.row          mean
1      1     2 -0.0022140524
2      3     2 -0.0032599264
3      5     2  0.0025657555
4      7     2  0.0033553619
5      1     4  0.0048441350
6      3     4  0.0037982609
7      5     4  0.0096239429
8      7     4  0.0104135493
9      1     6 -0.0072346110
10     3     6 -0.0082804850
11     5     6 -0.0024548031
12     7     6 -0.0016651967
13     1     8  0.0005593545
14     3     8 -0.0004865195
15     5     8  0.0053391624
16     7     8  0.0061287688

Здесь я хотел бы выбрать max(mean) для row_names M,N,O,P,Q. Один из способов сделать это было бы, если бы я не заботился о ссылках на индексы позже:

res <- sapply(1:length(tmp),function(i) which.max(tmp[[i]]$perf))
[1]  8  6  3 12 16

Это было бы так, как я рассчитал бы мой желаемый конечный результат с завершением:

res <- rbindlist(tmp,id="row_names")
  res <- res[,list(best=max(perf),best_idx = which.max(perf)),by=row_names]
   row_names        best best_idx
1:         M 0.010413549        8
2:         N 0.009508122        6
3:         O 0.009314068        3
4:         P 0.008883106       12
5:         Q 0.009316006       16

Я не решил, нужна ли мне информация best_idx (вероятно, мне понадобится, чтобы повторить точный расчет определенного row_names ), но используя этот res, я могу вычислить свой cumRet, выполнив:

res[,cumRet:= cumprod(best+1)-1]
> res
   row_names        best best_idx      cumRet
1:         M 0.010413549        8 0.01041355
2:         N 0.009508122        6 0.02002068
3:         O 0.009314068        3 0.02952123
4:         P 0.008883106       12 0.03866657
5:         Q 0.009316006       16 0.04834280

@ earch's действительно помогает видеть Процесс расчета всех этих комбинаций. Мне было интересно, есть ли более эффективное решение с использованием функциональности data.table. Мой реальный набор данных намного больше этого (миллионы строк), и комбинации начнут приносить убытки.

РЕДАКТИРОВАТЬ # 2 ::: После того, как вы сможете пройти через процесс, Я нашел очень быстрое решение!

tmp <- dt[,list(par1=par1[which.max(perf)],par2=par2[which.max(perf)],perf=max(perf)),by=list(ticker,row_names)]
    res <- tmp[,list(perf=mean(perf),par1= paste(par1,collapse=","),par2=paste(par2,collapse=",")),by=row_names]

Использование data.table позволяет мне рассчитать максимальный перф по группам и комбинациям тикеров. Затем, после этого, я могу группировать по row_names. И он получает те же результаты!

> res
   row_names        perf par1 par2
1:         M 0.010413549  2,2  2,1
2:         N 0.009508122  2,2  1,1
3:         O 0.009314068  1,1  2,1
4:         P 0.008883106  2,1  2,2
5:         Q 0.009316006  2,2  2,2

0 ответов