我的问题是在保留代码功能的同时摆脱for循环。
我有一个元素A_1, A_2, ... A_N
的成对排序矩阵。每个排序都表示为矩阵的一行。下面的代码显示了一个示例。
# Matrix representing the relations
# A1 < A2, A1 < A5, A2 < A4
(mat <- matrix(c(1, 2, 1, 5, 2, 4), ncol = 2, byrow = TRUE))
#> [,1] [,2]
#> [1,] 1 2
#> [2,] 1 5
#> [3,] 2 4
我希望将整个矩阵作为一组有序对。原因是我以后需要生成这些关系的传递性关闭。我一直在使用sets
包并在下面创建函数。
create_sets <- function(mat){
# Empty set
my_set <- sets::set()
# For loop for adding pair elements to the set, one at a time
for(i in seq(from = 1, to = nrow(mat), by = 1)){
my_set <- sets::set_union(my_set,
sets::pair(mat[[i, 1]], mat[[i, 2]]))
}
return(my_set)
}
create_sets(mat)
#> {(1, 2), (1, 5), (2, 4)}
此功能运行良好,但是我认为for循环是不必要的,并且不能替换它。对于上面只有三行的特定示例矩阵,我可以改用以下代码:
my_set2 <- sets::set(
sets::pair(mat[[1, 1]], mat[[1, 2]]),
sets::pair(mat[[2, 1]], mat[[2, 2]]),
sets::pair(mat[[3, 1]], mat[[3, 2]])
)
my_set2
#> {(1, 2), (1, 5), (2, 4)}
之所以可行,是因为sets::set
可以有任意数量的对。
args(sets::set)
#> function (...)
#> NULL
但是,矩阵mat
将具有任意数量的行,我希望该函数能够处理所有可能的情况。这就是为什么我无法摆脱for循环的原因。
因此,我的问题是:给定一个矩阵mat
,其中每行代表一个有序对,是否有一些通用方法将每行中的对作为单独的参数传递给sets::set
,而不会循环?
答案 0 :(得分:1)
for循环并不总是世界末日,如果矩阵不是很大,这看起来也不是很糟糕。
编写一个将行内容组合在一起的函数(有一种较短的方法,但这使您的任务明确)
f <- function(x) {
sets::pair(x[1], x[2])
}
Reduce(sets::set_union, lapply(split(mat, 1:nrow(mat)), f))
## {(1, 2), (1, 5), (2, 4)}
Reduce
的作用与for循环相同(重复应用set_union
),lapply
将矩阵变成成对的列表(也像for循环一样)
答案 1 :(得分:1)
OP询问了
[...]是否有一些通用的方法将每一行中的对作为单独的参数传递给
sets::set
,而不会循环?
是的,do.call()
函数可能正是您想要的。来自help(do.call)
:
do.call
从名称或函数以及要传递给它的参数列表中构造并执行函数调用。
因此,OP的create_sets()
函数可以替换为
do.call(sets::set, apply(mat, 1, function(x) sets::pair(x[1], x[2])))
{(1, 2), (1, 5), (2, 4)}
do.call()
的第二个参数需要一个列表。这是由
apply(mat, 1, function(x) sets::pair(x[1], x[2]))
返回列表
[[1]] (1, 2) [[2]] (1, 5) [[3]] (2, 4)
apply(mat, 1, FUN)
是一种隐式for
循环,它循环调用矩阵mat
的行,并在调用函数FUN
时将行值的向量作为参数。
as.tuple()
代替pair()
pair()
函数需要两个参数。这就是为什么我们被迫定义一个匿名函数function(x) sets::pair(x[1], x[2])
。
as.tuple()
函数将对象的元素强制为集合的元素。因此,代码可以更加简化:
do.call(sets::set, apply(mat, 1, sets::as.tuple))
{(1, 2), (1, 5), (2, 4)}
在这里,as.tuple()
提取行值的整个向量并将其强制为一组。