Проверка на равенство между всеми элементами одного вектора - PullRequest
83 голосов
/ 20 января 2011

Я пытаюсь проверить, все ли элементы вектора равны друг другу. Решения, которые я придумаю, кажутся несколько окольными, и оба включают проверку length().

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

С unique():

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

С rle():

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

Решение, которое позволило бы мне включить значение допуска для оценки «равенства» между элементами, было бы идеальным, чтобы избежать проблем FAQ 7.31 .

Есть ли встроенная функция для типа теста, который я полностью пропустил? identical() и all.equal() сравнивают два объекта R, поэтому они здесь не будут работать.

Редактировать 1

Вот некоторые результаты тестирования. Используя код:

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)

С результатами:

          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

Похоже, что diff(range(x)) < .Machine$double.eps ^ 0.5 самый быстрый.

Ответы [ 9 ]

35 голосов
/ 21 января 2011

Если все они являются числовыми значениями, то, если tol является вашим допуском, тогда ...

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

является решением вашей проблемы.

РЕДАКТИРОВАТЬ:

Изучив этот и другие ответы, а также сравнив несколько моментов, мы получаем вдвое быстрее, чем ответ DWin.

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

Это немного удивительно быстрее, чем diff(range(x)), поскольку diff не должно сильно отличаться от - и abs с двумя числами.Запрос диапазона должен оптимизировать получение минимума и максимума.И diff, и range являются примитивными функциями.Но время не лжет.

32 голосов
/ 21 января 2011

Я использую этот метод, который сравнивает мин и макс после деления на среднее значение:

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

Если бы вы использовали это более серьезно, вы, вероятно, захотите удалить пропущенные значения перед вычислениемдиапазон и среднее значение.

30 голосов
/ 09 марта 2016

Почему бы просто не использовать дисперсию:

var(x) == 0

Если все элементы x равны, вы получите дисперсию 0.

20 голосов
/ 21 января 2011
> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE

Еще один в том же духе:

> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE
12 голосов
/ 20 января 2011

Вы можете использовать identical() и all.equal(), сравнивая первый элемент со всеми другими, эффективно просматривая сравнение:

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> 

Таким образом, вы можете добавить любой эпсилон к identical() по мере необходимости.

10 голосов
/ 20 июля 2013

Поскольку я продолжаю возвращаться к этому вопросу снова и снова, вот решение Rcpp, которое, как правило, будет намного быстрее, чем любое из решений R, если ответ на самом деле FALSE (потому что он остановитв тот момент, когда он сталкивается с несоответствием) и будет иметь ту же скорость, что и самое быстрое решение R, если ответ TRUE.Например, для эталонного теста OP system.time синхронизируется ровно с 0 с помощью этой функции.

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
7 голосов
/ 10 июля 2015

Я специально для этого написал функцию, которая может проверять не только элементы в векторе, но и проверять, все ли элементы в списке идентичны . Конечно, он также хорошо обрабатывает векторы символов и все другие типы векторов. Он также имеет соответствующую обработку ошибок.

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

Теперь попробуйте несколько примеров.

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
3 голосов
/ 03 ноября 2014

На самом деле вам не нужно использовать минимальное, среднее или максимальное значение. На основании ответа Джона:

all(abs(x - x[[1]]) < tolerance)
2 голосов
/ 13 августа 2015

Здесь альтернатива, использующая трюк min, max, но для фрейма данных.В примере я сравниваю столбцы, но параметр поля из apply можно изменить для строк на 1.

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

Если valid == 0, то все элементы одинаковы

...