r - 如何使用 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.2
和b.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
解决方案
这是一个更多的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
推荐阅读
- c# - 在 C# 中更改字典值数组中的元素
- java - Java 中的 AES 解密耗时太长
- apache-spark - Spark 基于字母分区写入镶木地板
- python - 如何为python创建一个原子模板
- javascript - AngularJS 无法加载资源
- c# - MigraDoc - 在 TextFrame 和其他元素之间设置空间
- xpath - XPath 1.0 最低值与排序无关
- python - python/pandas properly implementing df.loc with a variable
- javascript - 渲染 Vue.js 的 main.js 时出现问题
- javascript - 使用 ajax 理解前端删除