枚举矩阵列的组合

3

(编辑备注:我将标题从“R grep:将字符串矩阵与列表匹配”更改为“R:枚举矩阵的列组合”,以更好地反映解决方案)

我正在尝试将一个字符串矩阵与一个列表匹配:这样我最终可以在data.frame上的后续操作中使用该矩阵作为映射。

第一部分按预期工作,返回所有可能的对、三元组和四元组组合的列表(尽管也许这种方法创建了我的联接?):

priceList <- data.frame(aaa = rnorm(100, 100, 10), bbb = rnorm(100, 100, 10), 
            ccc = rnorm(100, 100, 10), ddd = rnorm(100, 100, 10), 
            eee = rnorm(100, 100, 10), fff = rnorm(100, 100, 10), 
            ggg = rnorm(100, 100, 10))

getTrades <- function(dd, Maxleg=3)
{
    nodes <- colnames(dd)
    tradeList <- list()
    for (i in 2:Maxleg){
        tradeLeg <- paste0('legs',i)
        tradeList[[tradeLeg]] <- combn(nodes, i)
    }
    return(tradeList)
}

tradeCombos <- getTrades(priceList, 4)

现在我想把这个可能组合的清单转化为交易。例如:

> tradeCombos[[1]][,1]
[1] "aaa" "bbb"

需要最终变为priceList[,2] - priceList[,1]等等。

我尝试了一些带有grep和类似命令的方法,并且感觉以下方法已经接近成功:

LocList <- sapply(tradeCombos[[1]], regexpr, colnames(priceList))

然而,这种格式并不太适合下一步操作。
理想情况下,LocList[1] 应该返回类似于:1 2
假设 tradeCombos[[1]][,1] == "aaa" "bbb"
可以有人帮忙吗?
__
在下面所有答案的帮助下,我现在有了:
colDiff <- function(x) 
{
    Reduce('-', rev(x))
}

getTrades <- function(dd, Maxleg=3)
{
    tradeList <- list()
    for (i in 2:Maxleg){
        tradeLeg <- paste0('legs',i)
        tradeLegsList <- combn(names(dd), i, 
            function(x) dd[x], simplify = FALSE)
        nameMtx <- combn(names(dd), i)
        names(tradeLegsList) <- apply(nameMtx, MARGIN=2, 
            FUN=function(x) paste(rev(x), collapse='*'))
        tradeList[[tradeLeg]] <- lapply(tradeLegsList, colDiff) 
    }
    return(tradeList)
}

tradeCombos <- getTrades(priceList, 4)

这将保留组成部分的名称,并且这正是我试图实现的所有内容。

感谢大家的帮助。


3
顺便提一下,请熟悉combn函数!尝试使用combn(names(priceList, 2))或者combn(names(priceList, 3)),看看能得到什么结果。 - A5C1D2H2I1M1N2O1R2T1
@mrdwab,我在getTrades函数中基本上使用了combn(names(priceList), 3),并且使用tradeList[[tradeLeg]] <- combn(nodes, i)。然而,从下面看来,我显然还有很多关于combn的知识需要学习。非常感谢您的所有帮助。 - ricardo
3个回答

3

哇...忽略下面的一切,直接跳到更新

如我在评论中所提到的,您可以使用combn。 这个解决方案不会带您走到最后一步,而是创建一个data.frames列表。 从那里,很容易使用lapply来达到您的最终目标。

这是简化后的函数:

TradeCombos <- function(dd, MaxLeg) {
  combos = combn(names(dd), MaxLeg)
  apply(combos, 2, function(x) dd[x])
}

要使用它,只需指定您的数据集和您要查找的组合数。

TradeCombos(priceList, 3)
TradeCombos(priceList, 4)

继续往下看:@mplourde 已经向您展示了如何使用“Reduce”进行连续减法。这里可以采取类似的方法:
cumDiff <- function(x) Reduce("-", rev(x))
lapply(TradeCombos(priceList, 3), cumDiff)

通过将TradeCombos函数的输出保留为data.frame列表,您将有更多的灵活性。例如,如果您想要行和,您可以简单地使用lapply(TradeCombos(priceList, 3), rowSums); 对于任何您想应用的函数,都可以采取类似的方法。

更新

我不确定为什么@GSee没有将此作为答案添加,但我认为它非常棒:
按以下方式获取您的data.frame列表:
combn(names(priceList), 3, function(x) priceList[x], simplify = FALSE)

根据需要进行进一步操作。例如,使用我们创建的cumDiff函数: combn(names(priceList), 2, function(x) cumDiff(priceList[x]), simplify = FALSE)


2
你知道 combn 函数可以接受一个 FUN 参数吗?也就是说,你不一定需要使用 apply 函数。 - GSee
@GSee,我没有注意到,但那太厉害了!谢谢你指出来! - A5C1D2H2I1M1N2O1R2T1
@GSee,我可以以某种方式将其转移给您吗 :-) - A5C1D2H2I1M1N2O1R2T1
你是什么意思,列名都在那里了! - mnel
@mnel 我想问题的根源在于我之前使用的洞见方式。我更新函数时(请参见上面的问题),它没有返回名称。我需要mplourde的更新来完成我的任务,这就是为什么我接受了他的解决方案。他的解决方案最好地解决了我遇到的确切问题。 - ricardo
显示剩余2条评论

2

使用lapplyapplyReduce是实现您最终目标的方法。

lapply(tradeCombos, 
 function(combos) 
 apply(combos, MARGIN=2, FUN=function(combo) Reduce('-', priceList[rev(combo)])))
combo是来自于tradeCombos中的一个组合矩阵的列。 rev(combo)将该列反转,使最后一个值成为第一个值。在data.frame中选择子集列的R语法是DF[col.names],因此priceList[rev(combo)]priceList的一个子集,仅包含combo中的列,并以相反的顺序排列。实际上,data.frame只是由列组成的list,因此任何设计用于迭代list的函数都可以用于迭代data.frame中的列。其中之一是Reduce函数。 Reduce接受一个函数(在本例中是减法函数-)和一个参数列表,然后连续调用该函数,使用先前调用的结果作为下一个参数,例如 (((arg1 - arg2) - arg3) - arg4)。
您可以使用以下方式重命名tradeCombos中的列,以便最终列名反映其来源:
tradeCombos <- lapply(tradeCombos, 
    function(combos) {
        dimnames(combos)[[2]] <- apply(combos, 
            MARGIN=2, 
            FUN=function(combo) paste(rev(combo), collapse='-')
        )
        return(combos)
    }
)

+1,谢谢 - 非常有用。我不知道其中许多可能性。是否可以使每个列表位置附加的矩阵的列名反映其构造中使用的列名? - ricardo
apply会沿用其参数的列名。请参考我的编辑,了解如何重命名tradeCombos中的列。 - Matthew Plourde
不,你做得很好。你解决了我所有的问题。不能再要求更多了 - 所以我改变了已接受的答案。每个回答的人都非常出色。 - ricardo

1

tradeCombos 是一个包含 matrix 元素的 list。因此,tradeCombos[[1]] 是一个 matrix,适合使用 apply

apply(tradeCombos[[1]],1,function(x) match(x,names(priceList)))
      [,1] [,2]
 [1,]    1    2
 [2,]    1    3
 [3,]    1    4
 [4,]    1    5
 [5,]    1    6
 [6,]    1    7
 [7,]    2    3
 [8,]    2    4
 [9,]    2    5
[10,]    2    6
[11,]    2    7
[12,]    3    4
[13,]    3    5
[14,]    3    6
[15,]    3    7
[16,]    4    5
[17,]    4    6
[18,]    4    7
[19,]    5    6
[20,]    5    7
[21,]    6    7

顺便提一下,您可以使用字符串形式进行子集操作,例如 priceList[,"aaa"]


+1,谢谢。这正是我最初提出并认为需要解决的问题的答案。将您的答案与您的附加见解相结合,可以得到“apply(tradeCombos[[1]], 2, function(x) priceList[x])”,这非常有用。 - ricardo
实际上,在稍微尝试后,我将getTrades函数的一行更改为以下内容:tradeList[[tradeLeg]] <- apply(combn(colnames(dd), i), 2, function(x) dd[x]) - ricardo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接