首页 > 解决方案 > 如何使用 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是错误的,因为平均然后取累积乘积与取累积乘积和平均是不同的。

在此处输入图像描述我需要前者的解决方案,而这可以找到后者的解决方案(它们并非总是如此接近或具有相同的组合)。

最后,这是另一种方法,但不完全是我需要的。下面我尝试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

标签: rdata.table

解决方案


这是一个更多的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方法的方法。但我相信这会让它尽可能快!f1data.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

推荐阅读