如何在R中使用data.table选择三列的最佳组合并按组应用功能?

时间:2018-11-06 20:09:38

标签: 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")

我想选择ticker,par1,par2的最佳组合,以使row_names上的累积乘积最大化。例如,下面的代码可以做到这一点,但是以data.table的方式效率不高,我需要这样:

x <- split(dt,list(dt$ticker,dt$par1,dt$par2))
combn <- setDT(expand.grid(seq(1,length(x),2),seq(2,length(x),2)))
res <- data.table()

for(i in 1:nrow(combn)){
  tmp <- rbindlist(x[as.numeric(combn[i])])
  tmp <- tmp[,list(perf=mean(perf),par1=paste(par1,collapse=","),
                   par2=paste(par2,collapse=",")),by=row_names]
  cumRet <- c(cumRet,tail(cumprod(tmp$perf+1)-1,1))
  res <- rbind(res,data.table(cumRet=cumRet,
                              comb1 = names(x)[as.numeric(combn[i])][1], 
                              comb2=names(x)[as.numeric(combn[i])][2]))
}

res[which.max(cumRet)]
       cumRet comb1 comb2
1: 0.02452314 a.2.2 b.1.1

我知道以下代码以data.table的方式执行类似的操作。但是,它会最大化每个周期的组合,而无需考虑ticker,par1,par2上的row_names M,N,O,P,Q相同。我正在寻找与此类似但具有上述实现逻辑的解决方案。

# best possible return
tmp1 <- dt[,list(par1=par1[which.max(perf)],
                par2=par2[which.max(perf)],perf=max(perf)),by=list(ticker,row_names)]
res1 <- tmp1[,list(perf=mean(perf),comb1= paste(c(rbind(par1,par2))[1:.N],collapse="."),
                   comb2=paste(c(rbind(par1,par2))[-1:-.N],collapse=".")),
                    by=row_names]
   row_names        perf comb1 comb2
1:         M 0.010413549   2.2   2.1
2:         N 0.009508122   2.1   2.1
3:         O 0.009314068   1.2   1.1
4:         P 0.008883106   2.2   1.2
5:         Q 0.009316006   2.2   2.2
tail(cumprod(res1$perf+1)-1,1)
[1] 0.0483428

这是执行此操作的另一种方法,但这仍然不是我真正需要的:

# individual way
 tmp2 <- dt[,list(perf=tail(cumprod(perf+1)-1,1)),by=list(ticker,par1,par2)]
 tmp2 <- tmp2[,list(perf=max(perf),par1=par1[which.max(perf)],
                                      par2=par2[which.max(perf)]),by=ticker]
> tmp2
   ticker        perf par1 par2
1:      a 0.042091594    2    2
2:      b 0.007095708    1    1
> mean(tmp2$perf)
[1] 0.02459365

结果与我的实际计算res非常相似。它给出了正确的组合a.2.2b.1.1。但是,平均值perf的计算是错误的,因为取平均值然后取累加乘积与取累加乘积取平均值不同。

enter image description here我需要针对前者的解决方案,而这找到了针对后者的解决方案(并非总是如此,它们会如此接近或具有相同的组合)。

最后,这是执行此操作的另一种方法,但并非完全符合我的需要。下面,我尝试结合par1,par2来最大化我的结果。但是,在这里,我在两个par1,par2上都使用了相同的tickers。我想在par1,par2上应用相同的row_names,但允许不同的tickers使用不同的组合。

# group way
tmp3 <- dt[,.(perf=mean(perf)),by=.(par1,par2,row_names)]
res3 <- tmp3[,.(perf=tail(cumprod(perf+1)-1,1)),by=.(par1,par2)]
res3[which.max(perf)]
> res3[which.max(perf)]
   par1 par2       perf
1:    2    2 0.01756057

1 个答案:

答案 0 :(得分:0)

这是一种更data.table的方法!

dt[,id:= paste(ticker,par1,par2,sep=".")]
setkey(dt,id)
combn <- unique(setDT(expand.grid(unique(dt$id)[1:length(unique(dt$id))/2],
                                 unique(dt$id)[(length(unique(dt$id))/2+1):length(unique(dt$id))])))

f1 <- function(x){

  return(tail(cumprod(dt[x,.(row_names,perf),by=.EACHI]
                      [,.(perf=mean(perf)),by=row_names]$perf+1)-1,1))
}

combn[,perf:=apply(combn,1,f1)]
combn[which.max(perf)]
    Var1  Var2       perf
1: a.2.2 b.1.1 0.02452314

我不确定如何遍历combn组合并应用data.table所使用的f1方法来使其成为完整的data.table方法。但是我相信这会使其尽快!

编辑:这是一种几乎完整的data.table方法!

# create new dt that has all combinations and data
res2 <- rbindlist(lapply(1:nrow(combn),function(i) 
                    dt[as.matrix(combn[i])[1,],.(row_names,perf,comb=.GRP*i)]))
res2 <- res2[,.(perf=mean(perf)),by=.(row_names,comb)]
res2 <- res2[,.(perf=tail(cumprod(perf+1)-1,1)),by=comb]
res2[which.max(perf)]
   comb       perf
1:    4 0.02452314
> combn[4]
        Var1  Var2
    1: a.2.2 b.1.1