2014-12-05 12 views
6

Qual è il modo migliore per confrontare più di due oggetti con all.equal()?R: all.equal() per più oggetti?

Ecco un modo:

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 ha sottolineato che l'one-liner sopra di voi dice solo se gli oggetti sono tutti uguali o no, mentre all.equal() ti dice che cosa non è uguale su di loro se non sono uguali.

risposta

1

Penso che questo si avvicini il più possibile al comportamento di all.equal().

Restituisce TRUE se gli oggetti sono tutti uguali e un elenco di confronti a coppie altrimenti. Restituisce un elenco di singoli elementi se vengono confrontati solo due oggetti per coerenza di output.

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

Test 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

Qui è un'opzione:

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

E 'migliore della tua, perché in grado di rilevare quando bar e faz sono gli stessi, anche quando non è foo. Detto questo, ha un sacco di confronti inutili e sarà lento. Ad esempio, se cambiamo foo essere letters[1:10] otteniamo:

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

Per i dettagli su cosa è andato storto, basta sottoinsieme:

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

produce:

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

Se tutto si cura circa è che tutti gli oggetti devono essere all.equal, quindi la soluzione è abbastanza buona.

Inoltre, come da commenti di limitare alcuni dei calcoli duplicati:

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 

Produce

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

Questo dimostra ancora la matrice piena, ma rende evidente ciò che i confronti prodotte cosa.

+0

Buona idea - essere in grado di rilevare le differenze tra i non primi elementi sarebbe utile. Che ne pensi di generare una matrice di coppie di nomi per evitare i confronti di sé in 'outer()'? 'nomi <- c (" foo "," bar "," faz "); coppie <- t (combn (nomi, 2)); apply (pairs, 1, function (x) all.equal (get (x [1]), get (x [2]))) ' – treysp

+0

@treysp, d'accordo, vedere la mia modifica. – BrodieG