2014-12-05 17 views
6

Cách tốt nhất để so sánh nhiều hơn hai đối tượng với all.equal() là gì?R: all.equal() cho nhiều đối tượng?

Dưới đây là một cách:

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 chỉ ra rằng một lót ở trên chỉ cho bạn biết liệu các đối tượng đều bình đẳng hay không, trong khi all.equal() cho bạn biết những gì không phải là bình đẳng về chúng nếu chúng không bằng nhau.

Trả lời

1

Tôi nghĩ điều này đến gần với hành vi của all.equal() nhất có thể.

Nó trả về TRUE nếu các đối tượng đều bình đẳng và một danh sách so sánh cặp đôi nếu không. Nó trả về một danh sách một mục nếu chỉ có hai đối tượng được so sánh với tính nhất quán của đầu ra.

# 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)} 
    } 

Kiểm tra 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 
6

Dưới đây là một lựa chọn:

objs <- mget(c("foo", "bar", "faz")) 
outer(objs, objs, Vectorize(all.equal)) 

Đó là tốt hơn so với bạn bởi vì nó sẽ phát hiện khi barfaz đều giống nhau, ngay cả khi không phải là foo. Điều đó nói rằng, nó có rất nhiều so sánh không cần thiết và sẽ được làm chậm. Ví dụ, nếu chúng ta thay đổi fooletters[1:10] chúng tôi nhận được:

foo   bar   faz   
foo TRUE  Character,2 Character,2 
bar Character,2 TRUE  TRUE  
faz Character,2 TRUE  TRUE 

Để biết chi tiết về những gì đã xảy ra, chỉ cần tập hợp con:

outer(objs, objs, Vectorize(all.equal))[1, 2] 

Tạo:

[[1]] 
[1] "Modes: character, numeric"    
[2] "target is character, current is numeric"  

Nếu tất cả các bạn quan tâm về là tất cả các đối tượng phải là all.equal, thì giải pháp của bạn khá tốt.

Ngoài ra, theo ý kiến ​​để hạn chế một số tính toán trùng lặp:

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 

Tạo

foo bar   faz   
foo NULL Character,2 Character,2 
bar NULL NULL  TRUE  
faz NULL NULL  NULL   

này vẫn thể hiện đầy đủ các ma trận, nhưng làm cho nó rõ ràng những gì so sánh được sản xuất gì.

+0

Ý tưởng hay - có khả năng phát hiện sự khác biệt giữa các hạng mục phi đầu tiên sẽ được thuận tiện. Điều gì về việc tạo ra một ma trận của các cặp tên để tránh việc tự so sánh trong 'outer()'? 'tên <- c (" foo "," bar "," faz "); cặp <- t (combn (tên, 2)); áp dụng (cặp, 1, hàm (x) all.equal (get (x [1]), nhận (x [2]))) ' – treysp

+0

@treysp, đồng ý, xem sửa đổi của tôi. – BrodieG

Các vấn đề liên quan