What is the best way to compare more than two objects with all.equal()
?
使用all.equal()比较两个以上对象的最佳方法是什么?
Here's one way:
这是一种方式:
foo <- c(1:10)
bar <- letters[1:10]
baz <- c(1:10)
# doesn't work because all.equal() returns a character vector when objects not all equal
all(sapply(list(bar, baz), all.equal, foo))
# this works
mode(sapply(list(bar, baz), all.equal, foo)) == "logical" #FALSE
bar <- c(1:10)
mode(sapply(list(bar, baz), all.equal, foo)) == "logical" #TRUE
UPDATE: @BrodieG pointed out that the one-liner above only tells you whether the objects are all equal or not, whereas all.equal()
tells you what isn't equal about them if they aren't equal.
更新:@BrodieG指出上面的单行只告诉你对象是否全部相等,而all.equal()告诉你如果它们不相等则不相等。
2 个解决方案
#1
8
Here is an option:
这是一个选项:
objs <- mget(c("foo", "bar", "faz"))
outer(objs, objs, Vectorize(all.equal))
It's better than yours because it will detect when bar
and faz
are the same, even when foo
isn't. That said, it does a lot of unnecessary comparisons and will be slow. For example, if we change foo
to be letters[1:10]
we get:
它比你的好,因为它会检测bar和faz何时相同,即使foo不是。也就是说,它做了很多不必要的比较,而且会很慢。例如,如果我们将foo改为字母[1:10],我们得到:
foo bar faz
foo TRUE Character,2 Character,2
bar Character,2 TRUE TRUE
faz Character,2 TRUE TRUE
For details on what went wrong, just subset:
有关出错的详细信息,只需子集:
outer(objs, objs, Vectorize(all.equal))[1, 2]
Produces:
[[1]]
[1] "Modes: character, numeric"
[2] "target is character, current is numeric"
If all you care about is that all the objects must be all.equal
, then your solution is pretty good.
如果您关心的是所有对象必须all.equal,那么您的解决方案非常好。
Also, as per comments to limit some of the duplicate calculations:
另外,根据评论限制一些重复计算:
res <- outer(objs, objs, function(x, y) vector("list", length(x)))
combs <- combn(seq(objs), 2)
res[t(combs)] <- Vectorize(all.equal)(objs[combs[1,]], objs[combs[2,]])
res
Produces
foo bar faz
foo NULL Character,2 Character,2
bar NULL NULL TRUE
faz NULL NULL NULL
This still shows the full matrix, but makes it obvious what comparisons produced what.
这仍然显示了完整的矩阵,但很明显比较产生了什么。
#2
2
I think this comes as close to the behavior of all.equal()
as possible.
我认为这与all.equal()的行为尽可能接近。
It returns TRUE
if the objects are all equal and a list of pairwise comparisons otherwise. It returns a single-item list if only two objects are compared for consistency of output.
如果对象全部相等则返回TRUE,否则返回成对比较列表。如果仅比较两个对象以确保输出的一致性,则返回单项列表。
# function: all.equal.mult()
# description: compares >=2 objects with all.equal()
# input: >=2 comma-separated object names
# output: TRUE or list of pairwise all.equal() object comparisons
# examples:
# foo <- c(1:10)
# bar <- c(1:10)
# foz <- c(1:10)
# baz <- letters[1:10]
#
# all.equal.mult(foo, bar) # TRUE
# all.equal.mult(foo, baz) # results of all.equal(foo, baz) as one-item list
# all.equal.mult(foo, bar, foz) # TRUE
# all.equal.mult(foo, bar, baz) # list of pairwise all.equal() comparisons among objects
all.equal.mult <- function(...) {
# more than one object required
if (length(list(...)) < 2) stop("More than one object required")
# character vector of object names
names <- as.character(substitute(list(...)))[-1L]
# matrix of object name pairs
pairs <- t(combn(names, 2))
# if only two objects, return one item list containing all.equal() for them
if (nrow(pairs) == 1) return(list(all.equal(get(pairs[1,1]), get(pairs[1,2]))))
# function: eq.fun()
# description: applies all.equal() to two quoted names of objects
# input: two quoted names of objects
# output: list containing all.equal() comparison and "[obj1] vs. [obj2]"
# examples:
# x <- 1
# y <- 1
# z <- 2
# eq.fun("x", "y") # list(TRUE, "x vs. y")
# eq.fun("x", "z") # list("Mean relative difference: 1", "x vs. z")
eq.fun <- function(x, y) {
all.eq <- all.equal(get(x, inherits=TRUE), get(y, inherits=TRUE))
name <- paste0(x, " vs. ", y)
return(list(all.eq, name))
}
# list of eq.fun object comparisons
out <- vector(mode="list", length=nrow(pairs))
for (x in 1:nrow(pairs)) {
eq.list <- eq.fun(pairs[x, 1], pairs[x, 2])
out[[x]] <- eq.list[[1]]
names(out)[x] <- eq.list[[2]]
}
# return TRUE if all objects equal, comparison list otherwise
if (mode(unlist(out)) == "logical") {return(TRUE)} else {return(out)}
}
Testing 1, 2:
测试1,2:
foo <- c(1:10)
bar <- c(1:10)
foz <- c(1:10)
baz <- letters[1:10]
all.equal.mult(foo) # Error
all.equal.mult(foo, bar) # TRUE
all.equal.mult(foo, baz) # results of all.equal(foo, baz) as one-item list
all.equal.mult(foo, bar, foz) # TRUE
all.equal.mult(foo, bar, baz) # list of pairwise all.equal() comparisons among objects
#1
8
Here is an option:
这是一个选项:
objs <- mget(c("foo", "bar", "faz"))
outer(objs, objs, Vectorize(all.equal))
It's better than yours because it will detect when bar
and faz
are the same, even when foo
isn't. That said, it does a lot of unnecessary comparisons and will be slow. For example, if we change foo
to be letters[1:10]
we get:
它比你的好,因为它会检测bar和faz何时相同,即使foo不是。也就是说,它做了很多不必要的比较,而且会很慢。例如,如果我们将foo改为字母[1:10],我们得到:
foo bar faz
foo TRUE Character,2 Character,2
bar Character,2 TRUE TRUE
faz Character,2 TRUE TRUE
For details on what went wrong, just subset:
有关出错的详细信息,只需子集:
outer(objs, objs, Vectorize(all.equal))[1, 2]
Produces:
[[1]]
[1] "Modes: character, numeric"
[2] "target is character, current is numeric"
If all you care about is that all the objects must be all.equal
, then your solution is pretty good.
如果您关心的是所有对象必须all.equal,那么您的解决方案非常好。
Also, as per comments to limit some of the duplicate calculations:
另外,根据评论限制一些重复计算:
res <- outer(objs, objs, function(x, y) vector("list", length(x)))
combs <- combn(seq(objs), 2)
res[t(combs)] <- Vectorize(all.equal)(objs[combs[1,]], objs[combs[2,]])
res
Produces
foo bar faz
foo NULL Character,2 Character,2
bar NULL NULL TRUE
faz NULL NULL NULL
This still shows the full matrix, but makes it obvious what comparisons produced what.
这仍然显示了完整的矩阵,但很明显比较产生了什么。
#2
2
I think this comes as close to the behavior of all.equal()
as possible.
我认为这与all.equal()的行为尽可能接近。
It returns TRUE
if the objects are all equal and a list of pairwise comparisons otherwise. It returns a single-item list if only two objects are compared for consistency of output.
如果对象全部相等则返回TRUE,否则返回成对比较列表。如果仅比较两个对象以确保输出的一致性,则返回单项列表。
# function: all.equal.mult()
# description: compares >=2 objects with all.equal()
# input: >=2 comma-separated object names
# output: TRUE or list of pairwise all.equal() object comparisons
# examples:
# foo <- c(1:10)
# bar <- c(1:10)
# foz <- c(1:10)
# baz <- letters[1:10]
#
# all.equal.mult(foo, bar) # TRUE
# all.equal.mult(foo, baz) # results of all.equal(foo, baz) as one-item list
# all.equal.mult(foo, bar, foz) # TRUE
# all.equal.mult(foo, bar, baz) # list of pairwise all.equal() comparisons among objects
all.equal.mult <- function(...) {
# more than one object required
if (length(list(...)) < 2) stop("More than one object required")
# character vector of object names
names <- as.character(substitute(list(...)))[-1L]
# matrix of object name pairs
pairs <- t(combn(names, 2))
# if only two objects, return one item list containing all.equal() for them
if (nrow(pairs) == 1) return(list(all.equal(get(pairs[1,1]), get(pairs[1,2]))))
# function: eq.fun()
# description: applies all.equal() to two quoted names of objects
# input: two quoted names of objects
# output: list containing all.equal() comparison and "[obj1] vs. [obj2]"
# examples:
# x <- 1
# y <- 1
# z <- 2
# eq.fun("x", "y") # list(TRUE, "x vs. y")
# eq.fun("x", "z") # list("Mean relative difference: 1", "x vs. z")
eq.fun <- function(x, y) {
all.eq <- all.equal(get(x, inherits=TRUE), get(y, inherits=TRUE))
name <- paste0(x, " vs. ", y)
return(list(all.eq, name))
}
# list of eq.fun object comparisons
out <- vector(mode="list", length=nrow(pairs))
for (x in 1:nrow(pairs)) {
eq.list <- eq.fun(pairs[x, 1], pairs[x, 2])
out[[x]] <- eq.list[[1]]
names(out)[x] <- eq.list[[2]]
}
# return TRUE if all objects equal, comparison list otherwise
if (mode(unlist(out)) == "logical") {return(TRUE)} else {return(out)}
}
Testing 1, 2:
测试1,2:
foo <- c(1:10)
bar <- c(1:10)
foz <- c(1:10)
baz <- letters[1:10]
all.equal.mult(foo) # Error
all.equal.mult(foo, bar) # TRUE
all.equal.mult(foo, baz) # results of all.equal(foo, baz) as one-item list
all.equal.mult(foo, bar, foz) # TRUE
all.equal.mult(foo, bar, baz) # list of pairwise all.equal() comparisons among objects