2016-01-30 24 views
5

Byłbym zaskoczony, gdyby to nie dup, ale nie mogłem znaleźć rozwiązania.Wektoryzowane testy równości

Rozumiem ograniczenia == dla testowania równości liczb zmiennoprzecinkowych. Należy używać all.equal

0.1 + 0.2 == 0.3 
# FALSE 
all.equal(0.1 + 0.2, 0.3) 
# TRUE 

Ale == ma tę zaletę, że wektorowy:

set.seed(1) 
Df <- data.frame(x = sample(seq(-1, 1, by = 0.1), size = 100, replace = TRUE), 
       y = 0.1) 
Df[Df$x > 0 & Df$x < 0.2,] 
## x y 
## 44 0.1 0.1 
## 45 0.1 0.1 

# yet 
sum(Df$x == Df$y) 
# [1] 0 

można napisać (zły) Funkcja sobie:

All.Equal <- function(x, y){ 
    stopifnot(length(x) == length(y)) 
    out <- logical(length(x)) 
    for (i in seq_along(x)){ 
    out[i] <- isTRUE(all.equal(x[i], y[i])) 
    } 
    out 
} 

sum(All.Equal(Df$x, Df$y)) 

co daje poprawną odpowiedź, ale wciąż ma przed sobą długą drogę.

microbenchmark::microbenchmark(All.Equal(Df$x, Df$y), Df$x == Df$y) 
Unit: microseconds 
        expr  min  lq  mean  median  uq  max neval cld 
All.Equal(Df$x, Df$y) 9954.986 10298.127 20382.24436 10511.5360 10798.841 915182.911 100 b 
      Df$x == Df$y 16.857 19.265 29.06261 30.8535 38.529  45.151 100 a 

Inną opcją może być:

All.equal.abs <- function(x,y){ 
    tol <- .Machine$double.eps^0.5 
    abs(x - y) < tol 
} 

który wykonuje porównywalnie do ==.

Co to jest istniejąca funkcja, która wykonuje to zadanie?

+0

Najbliższy mogę myśleć jest 'z (DF, mapply (function (a, b) isTRUE (all.equal (a, b)), x, y))', ale to będzie prawdopodobnie lepszy niż to, co już zrobiłeś. Możesz uzyskać niewielki wzrost prędkości używając '.mapply()' (bare bones 'mapply()'). –

+4

'abs (x-y) fishtank

Odpowiedz

0

nie można zrobić porównawczy test, ale Wektoryzacja funkcja all.equal może pracować:

All.equal <- Vectorize(all.equal, c("target", "current")) 
sum(All.equal(Df$x, Df$y)==T) 
2

Vectorize() okazuje się być powolny opcja. Jak sugeruje @fishtank w komentarzu, najlepszym rozwiązaniem jest sprawdzenie, czy bezwzględna różnica jest mniejsza niż pewna wartość tolerancji, tj. is_equal_tol() od dołu.

set.seed(123) 
a <- sample(1:10, size = 50, replace = T) 
b <- sample(a) 

is_equal_tol <- function(x, y, tol = .Machine$double.eps^0.5) { 
    abs(x - y) < tol 
} 

is_equal_vec <- Vectorize(all.equal, c("target", "current")) 

is_equal_eq <- function(x, y) x == y 

microbenchmark::microbenchmark(is_equal_eq(a, b), 
           is_equal_tol(a, b), 
           isTRUE(is_equal_vec(a, b)), 
           times = 1000L) 

Unit: nanoseconds 
         expr  min  lq  mean median  uq  max neval 
      is_equal_eq(a, b)  0  856 1545.797 1284 2139 14113 1000 
     is_equal_tol(a, b) 1711 2567 4991.377 4278 6843 27370 1000 
isTRUE(is_equal_vec(a, b)) 2858445 3008552 3258916.503 3082964 3204204 46130260 1000 
+1

Nie pokazujesz swojego pokolenia 'a' i' b', ale z twoimi rezultatami w nanosekundach, myślę, że powinieneś uczynić je dłuższymi wektorami. – Gregor

+0

@Gregor Masz rację. Edytowałem przykład, aby podać pełny przykład. Dzięki! –