如何基于双向量生成元素依赖向量组合的矩阵及优化
问题解答
问题背景
我有两个向量:
seqX <- c("seq1","seq2","seq3") seqY <- c("seqA","seqB")
希望创建一个seqX × seqY的矩阵,其中每个单元格的值由依赖对应seqX和seqY元素组合的自定义函数计算。例如这个自定义函数:
my.function.example <- function(seqX, seqY) { return(paste(seqX, "-", seqY)) }
预期生成的矩阵结果如下:
[,1] [,2] [,3] [1,] seq1-seqA seq2-seqA seq3-seqA [2,] seq1-seqB seq2-seqB seq3-seqB
我对tapply、sapply等函数的使用感到困惑,因此提出以下两个问题:
- 如何创建该矩阵并将这类依赖双向量的函数应用于其中?
- 当
seqX与seqY相等时(所有组合会被重复计算),如何实现最高效的计算?
问题1:创建矩阵并应用双向量函数
这里有两种简单直接的实现方式:
方法1:使用outer()函数(最推荐)
outer()是R中专门生成向量外积的函数,天生支持自定义运算规则,能自动遍历两个向量的所有元素组合并应用函数:
# 定义向量与自定义函数 seqX <- c("seq1","seq2","seq3") seqY <- c("seqA","seqB") my.function.example <- function(x, y) paste(x, "-", y) # 生成初始外积矩阵 temp_matrix <- outer(seqX, seqY, FUN = my.function.example) # 转置得到你需要的行列格式(outer默认按seqX为行、seqY为列生成,你的预期是seqY为行、seqX为列) result_matrix <- t(temp_matrix)
运行后输出完全符合预期:
[,1] [,2] [,3] [1,] seq1-seqA seq2-seqA seq3-seqA [2,] seq1-seqB seq2-seqB seq3-seqB
方法2:使用sapply()循环
如果你更熟悉sapply的用法,可以对seqY的每个元素,批量处理整个seqX,再将结果组合成矩阵:
result_matrix <- sapply(seqY, function(y) my.function.example(seqX, y))
这种方式得到的矩阵直接就是你想要的行列格式,无需额外转置。
问题2:seqX与seqY相等时的高效计算
当seqX和seqY完全相同时,矩阵会出现大量重复计算(比如(seq1,seq2)和(seq2,seq1)),可以根据函数是否对称来优化:
情况1:函数是对称的(f(x,y) = f(y,x))
比如计算两个元素的相似度、长度和这类对称场景,只需要计算上三角(或下三角)区域,再对称填充即可,计算量从n²降到n(n+1)/2:
# 示例对称函数:计算两个字符串的长度之和 my_sym_func <- function(x, y) nchar(x) + nchar(y) seqX <- seqY <- c("seq1","seq2","seq3") n <- length(seqX) # 初始化空矩阵 result_matrix <- matrix(NA, nrow = n, ncol = n) # 计算上三角并对称填充 for (i in 1:n) { for (j in i:n) { val <- my_sym_func(seqX[i], seqY[j]) result_matrix[i,j] <- val result_matrix[j,i] <- val } }
情况2:函数不对称,但仍想避免重复计算
如果函数的f(x,y)和f(y,x)结果不同,但你不想重复执行两次函数逻辑,可以先生成所有唯一组合计算一次,再反向填充:
# 示例非对称函数:x在前y在后的拼接 my_asym_func <- function(x, y) paste(x, "-", y) seqX <- seqY <- c("seq1","seq2","seq3") n <- length(seqX) # 生成所有i<=j的唯一组合 combinations <- expand.grid(i = 1:n, j = 1:n) combinations <- combinations[combinations$i <= combinations$j, ] # 计算每个唯一组合的结果 combinations$result <- mapply(my_asym_func, seqX[combinations$i], seqY[combinations$j]) # 初始化矩阵并填充 result_matrix <- matrix(NA, nrow = n, ncol = n) for (k in 1:nrow(combinations)) { i <- combinations$i[k] j <- combinations$j[k] result_matrix[i,j] <- combinations$result[k] # 反向填充时直接生成对应结果,无需重复调用原函数 result_matrix[j,i] <- paste(seqY[j], "-", seqX[i]) }
如果你的函数本身支持向量化运算(比如paste),即使不用上述优化,outer()的效率也足够高;但如果是计算密集型函数,上述优化能大幅减少运算时间。
内容的提问来源于stack exchange,提问作者Martin Busch
相关产品推荐
相关产品推荐

