Kiểm tra sự bình đẳng giữa tất cả các phần tử của một vectơ duy nhất


101

Tôi đang cố gắng kiểm tra xem tất cả các phần tử của vectơ có bằng nhau hay không. Các giải pháp tôi đưa ra có vẻ hơi vòng vo, cả hai đều liên quan đến việc kiểm tra length().

x <- c(1, 2, 3, 4, 5, 6, 1)  # FALSE
y <- rep(2, times = 7)       # TRUE

Với unique():

length(unique(x)) == 1
length(unique(y)) == 1

Với rle():

length(rle(x)$values) == 1
length(rle(y)$values) == 1

Một giải pháp cho phép tôi bao gồm một giá trị dung sai để đánh giá 'sự bình đẳng' giữa các phần tử sẽ là lý tưởng để tránh các vấn đề Câu hỏi thường gặp 7.31 .

Có một chức năng tích hợp cho loại bài kiểm tra mà tôi đã hoàn toàn bỏ qua không? identical()all.equal()so sánh hai đối tượng R, vì vậy chúng sẽ không hoạt động ở đây.

Chỉnh sửa 1

Dưới đây là một số kết quả điểm chuẩn. Sử dụng mã:

library(rbenchmark)

John <- function() all( abs(x - mean(x)) < .Machine$double.eps ^ 0.5 )
DWin <- function() {diff(range(x)) < .Machine$double.eps ^ 0.5}
zero_range <- function() {
  if (length(x) == 1) return(TRUE)
  x <- range(x) / mean(x)
  isTRUE(all.equal(x[1], x[2], tolerance = .Machine$double.eps ^ 0.5))
}

x <- runif(500000);

benchmark(John(), DWin(), zero_range(),
  columns=c("test", "replications", "elapsed", "relative"),
  order="relative", replications = 10000)

Với kết quả:

          test replications elapsed relative
2       DWin()        10000 109.415 1.000000
3 zero_range()        10000 126.912 1.159914
1       John()        10000 208.463 1.905251

Vì vậy, nó có vẻ như diff(range(x)) < .Machine$double.eps ^ 0.5là nhanh nhất.

Câu trả lời:


37

Tôi sử dụng phương pháp này, so sánh giá trị tối thiểu và giá trị lớn nhất, sau khi chia cho giá trị trung bình:

# Determine if range of vector is FP 0.
zero_range <- function(x, tol = .Machine$double.eps ^ 0.5) {
  if (length(x) == 1) return(TRUE)
  x <- range(x) / mean(x)
  isTRUE(all.equal(x[1], x[2], tolerance = tol))
}

Nếu bạn đang sử dụng điều này nghiêm túc hơn, bạn có thể muốn xóa các giá trị bị thiếu trước khi tính toán phạm vi và giá trị trung bình.


Tôi chọn cái này vì nhanh hơn của Dirk. Tôi không có hàng triệu phần tử, nhưng điều này sẽ chạy nhanh hơn một chút đối với tôi.
kmm

@Kevin: còn giải pháp của John thì sao? Nó nhanh hơn ~ 10 lần so với Hadley và cho phép bạn thiết lập dung sai. Nó có bị thiếu hụt theo một số cách khác không?
Joshua Ulrich

Vui lòng cung cấp một số điểm chuẩn - Tôi vừa kiểm tra của tôi là giống nhau đối với một vector của một triệu bộ đồng phục.
hadley

@hadley: Tôi đang chạy system.time(for(i in 1:1e4) zero_range(x)), ở đâu xtừ OP. Giải pháp của John là ~ 10 lần cho x, nhanh hơn ~ 3 lần cho yvà chậm hơn một chút cho runif(1e6).
Joshua Ulrich

10x chênh lệch không quan trọng nhiều khi bạn đang nhìn vào sự khác biệt giữa 0,00023 và 0,000023 giây - và DWin có lẽ sẽ tuyên bố họ đều giống nhau đến mức độ nhất định của sự khoan dung;)
hadley

46

Tại sao không chỉ đơn giản sử dụng phương sai:

var(x) == 0

Nếu tất cả các phần tử của xbằng nhau, bạn sẽ nhận được phương sai của 0.


17
length(unique(x))=1kết thúc nhanh hơn khoảng gấp đôi, nhưng varngắn gọn là tốt.
AdamO

YohanBadia, tôi có một mảng c (-5.532456e-09, 1.695298e-09) và có John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSEnghĩa là tất cả các thử nghiệm khác đều nhận ra rằng các giá trị giống nhau trong R. Làm cách nào để kiểm tra phương sai có thể được sử dụng trong ngữ cảnh đó?
mjs

2 giá trị trong mảng của bạn không giống nhau. Tại sao bạn muốn kiểm tra trở lại TRUE? Trong trường hợp câu trả lời của John, bạn kiểm tra xem sự khác biệt có trên một ngưỡng nhất định hay không. Trong trường hợp của bạn, sự khác biệt giữa 2 giá trị là rất thấp, điều này có thể dẫn đến việc nó nằm dưới ngưỡng bạn đã xác định.
Yohan Obadia

41

Nếu chúng đều là giá trị số thì nếu tol là dung sai của bạn thì ...

all( abs(y - mean(y)) < tol ) 

là giải pháp cho vấn đề của bạn.

BIÊN TẬP:

Sau khi xem xét câu trả lời này, và các câu trả lời khác, và đo điểm chuẩn một số điều, phần sau sẽ nhanh hơn câu trả lời DWin gấp đôi.

abs(max(x) - min(x)) < tol

Điều này nhanh hơn một chút đáng ngạc nhiên diff(range(x))diffkhông nên khác nhiều so với -absvới hai con số. Yêu cầu phạm vi phải tối ưu hóa để nhận được tối thiểu và tối đa. Cả hai diffrangeđều là các hàm nguyên thủy. Nhưng thời gian không nói dối.


Bạn có thể nhận xét về giá trị tương đối của phép trừ trung bình so với phép chia cho nó?
hadley

Nó đơn giản hơn về mặt tính toán. Tùy thuộc vào hệ thống, và cách R được biên dịch và vector hóa, nó sẽ được hoàn thành nhanh hơn với mức tiêu thụ điện năng ít hơn. Ngoài ra, khi bạn chia cho giá trị trung bình, kết quả được thử nghiệm của bạn tương đối với 1 trong khi với phép trừ thì nó là 0, điều này có vẻ đẹp hơn đối với tôi. Ngoài ra, sự khoan dung có một cách giải thích đơn giản hơn.
John

1
Nhưng nó thậm chí không phức tạp đến mức phép chia phức tạp vì việc tìm kiếm và sắp xếp cần thiết để trích xuất phạm vi tốn kém về mặt tính toán hơn nhiều so với một phép trừ đơn giản. Tôi đã thử nghiệm nó và đoạn mã trên nhanh hơn khoảng 10 lần so với hàm zero_range Hadley (và câu trả lời đúng nhanh nhất của bạn ở đây). Chức năng so sánh của Dirk rất chậm. Đây là câu trả lời nhanh nhất ở đây.
John

Tôi chỉ thấy bình luận về thời gian của Josh trong câu trả lời của bạn Hadley ... Tôi không gặp bất kỳ tình huống nào mà zero_range nhanh hơn. Sự khác biệt giữa nhanh hơn một chút (có thể 20%) đến 10 lần luôn có lợi nếu câu trả lời này. Nó đã thử một số phương pháp.
John

24
> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE

Khác cùng dòng:

> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE

Tôi không nghĩ rằng tác phẩm này rất tốt cho số lượng rất nhỏ:x <- seq(1, 10) / 1e10
hadley

2
@Hadley: OP đã yêu cầu một giải pháp cho phép đặc tả một dung sai, có lẽ vì ông ấy không quan tâm đến những khác biệt rất nhỏ. all.equal có thể được sử dụng với các dung sai khác và OP dường như hiểu điều này.
IRTFM

2
Tôi đã không thể hiện bản thân một cách rõ ràng - trong ví dụ của tôi, có sự khác biệt tương đối gấp mười lần giữa số lớn nhất và số nhỏ nhất. Đó có lẽ là điều bạn muốn lưu ý! Tôi nghĩ rằng dung sai số cần được tính toán liên quan đến phạm vi dữ liệu - tôi đã không làm điều này trong quá khứ và nó đã gây ra vấn đề.
hadley

2
Tôi không nghĩ rằng tôi đã hiểu lầm bạn một cách sai lầm. Tôi chỉ nghĩ rằng người hỏi đang yêu cầu một giải pháp có thể bỏ qua sự khác biệt tương đối gấp mười lần đối với các số có hiệu quả bằng 0. Tôi nghe thấy anh ấy hỏi một giải pháp có thể bỏ qua sự khác biệt giữa 1e-11 và 1e-13.
IRTFM

5
Tôi cố gắng và cung cấp cho mọi người những gì họ cần, không phải những gì họ muốn;) Nhưng hãy thành công.
hadley

16

Bạn có thể sử dụng identical()all.equal()bằng cách so sánh yếu tố đầu tiên với tất cả các yếu tố khác, quét qua so sánh một cách hiệu quả:

R> compare <- function(v) all(sapply( as.list(v[-1]), 
+                         FUN=function(z) {identical(z, v[1])}))
R> compare(x)
[1] FALSE
R> compare(y)
[1] TRUE
R> 

Bằng cách đó, bạn có thể thêm bất kỳ epsilon nào vào identical()nếu cần.


2
Hideously không hiệu quả mặc dù ... (trên máy tính của tôi nó mất khoảng 10 giây cho một triệu số)
hadley

2
Không nghi ngờ gì nữa. Tuy nhiên OP đã đặt câu hỏi liệu điều này có thể được thực hiện ở tất cả . Làm tốt nó là bước thứ hai. Và bạn biết nơi tôi đứng với vòng ... ;-)
Dirk Eddelbuettel

10
Vòng lặp đó thật tuyệt vời? ;)
hadley

4
Điều tôi thích ở appoach này là nó có thể được sử dụng với các đối tượng không phải số.
Luciano Selzer

so sánh <- function (v) all (sapply (as.list (v [-1]), FUN = function (z) {isTRUE (all.equal (z, v [1]))}))
N. McA .

16

Bạn chỉ cần kiểm tra all(v==v[1])


Cái này là bc tuyệt vời, nó cũng hoạt động với chuỗi! Cảm ơn
arvi1000

Điều này hoạt động trừ khi bạn có NAtrong vectơ của mình: x <- c(1,1,NA); all(x == x[1])return NA, not FALSE. Trong những trường hợp như vậy length(unique(x)) == 1hoạt động.
HBat

11

Vì tôi tiếp tục quay lại câu hỏi này lặp đi lặp lại, đây là một Rcppgiải pháp nói chung sẽ nhanh hơn nhiều so với bất kỳ Rgiải pháp nào nếu câu trả lời thực sự là FALSE(vì nó sẽ dừng lại ngay khi nó gặp sự không khớp) và sẽ có cùng tốc độ là giải pháp R nhanh nhất nếu câu trả lời là TRUE. Ví dụ đối với điểm chuẩn OP, system.timeđồng hồ ở chính xác 0 bằng cách sử dụng chức năng này.

library(inline)
library(Rcpp)

fast_equal = cxxfunction(signature(x = 'numeric', y = 'numeric'), '
  NumericVector var(x);
  double precision = as<double>(y);

  for (int i = 0, size = var.size(); i < size; ++i) {
    if (var[i] - var[0] > precision || var[0] - var[i] > precision)
      return Rcpp::wrap(false);
  }

  return Rcpp::wrap(true);
', plugin = 'Rcpp')

fast_equal(c(1,2,3), 0.1)
#[1] FALSE
fast_equal(c(1,2,3), 2)
#[2] TRUE

1
Điều này là tốt & +1 cho tốc độ, nhưng tôi không tin rằng so sánh tất cả các yếu tố với yếu tố thứ nhất là hoàn toàn đúng. Một vectơ có thể vượt qua bài kiểm tra này, nhưng sự khác biệt giữa max (x) và min (x) lớn hơn độ chính xác. Ví dụfast_equal(c(2,1,3), 1.5)
dww

@dww Những gì bạn đang chỉ ra là so sánh không có tính bắc cầu khi bạn gặp vấn đề về độ chính xác - tức là a == b, b == ckhông nhất thiết ngụ ý a == cnếu bạn đang thực hiện so sánh dấu phẩy động. Bạn có thể có thể chia chính xác của bạn bằng của số yếu tố để tránh vấn đề này, hoặc sửa đổi các thuật toán để tính toán minmaxvà sử dụng đó như một điều kiện dừng.
eddi

10

Tôi đã viết một hàm dành riêng cho việc này, hàm này có thể kiểm tra không chỉ các phần tử trong một vectơ mà còn có thể kiểm tra xem tất cả các phần tử trong danh sách có giống hệt nhau hay không . Tất nhiên nó cũng xử lý tốt các vectơ ký tự và tất cả các loại vectơ khác. Nó cũng có xử lý lỗi thích hợp.

all_identical <- function(x) {
  if (length(x) == 1L) {
    warning("'x' has a length of only 1")
    return(TRUE)
  } else if (length(x) == 0L) {
    warning("'x' has a length of 0")
    return(logical(0))
  } else {
    TF <- vapply(1:(length(x)-1),
                 function(n) identical(x[[n]], x[[n+1]]),
                 logical(1))
    if (all(TF)) TRUE else FALSE
  }
}

Bây giờ hãy thử một số ví dụ.

x <- c(1, 1, 1, NA, 1, 1, 1)
all_identical(x)       ## Return FALSE
all_identical(x[-4])   ## Return TRUE
y <- list(fac1 = factor(c("A", "B")),
          fac2 = factor(c("A", "B"), levels = c("B", "A"))
          )
all_identical(y)     ## Return FALSE as fac1 and fac2 have different level order

4

Bạn thực sự không cần sử dụng giá trị tối thiểu, trung bình hoặc tối đa. Dựa trên câu trả lời của John:

all(abs(x - x[[1]]) < tolerance)

3

Đây là một giải pháp thay thế sử dụng thủ thuật tối thiểu, tối đa nhưng dành cho khung dữ liệu. Trong ví dụ, tôi đang so sánh các cột nhưng tham số lề từ applycó thể được thay đổi thành 1 cho các hàng.

valid = sum(!apply(your_dataframe, 2, function(x) diff(c(min(x), max(x)))) == 0)

Nếu valid == 0sau đó tất cả các phần tử đều giống nhau

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.