Tek bir vektörün tüm öğeleri arasında eşitliği test edin


101

Bir vektörün tüm elemanlarının birbirine eşit olup olmadığını test etmeye çalışıyorum. Bulduğum çözümler biraz dolambaçlı görünüyor, her ikisi de kontrol etmeyi içeriyor length().

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

İle unique():

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

İle rle():

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

Öğeler arasında 'eşitliği' değerlendirmek için bir tolerans değeri eklememe izin verecek bir çözüm, SSS 7.31 sorunlarından kaçınmak için ideal olacaktır .

Tamamen gözden kaçırdığım test türü için yerleşik bir işlev var mı? identical()ve all.equal()iki R nesnesini karşılaştırın, böylece burada çalışmazlar.

Düzenle 1

İşte bazı kıyaslama sonuçları. Kodu kullanarak:

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)

Sonuçlarla birlikte:

          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

Yani diff(range(x)) < .Machine$double.eps ^ 0.5en hızlısı gibi görünüyor .

Yanıtlar:


37

Ortalamaya böldükten sonra min ve maks. Karşılaştıran bu yöntemi kullanıyorum:

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

Bunu daha ciddi bir şekilde kullanıyor olsaydınız, aralığı ve ortalamayı hesaplamadan önce muhtemelen eksik değerleri kaldırmak istersiniz.


Bunu Dirk'ten daha hızlı olduğu için seçtim. Milyonlarca elementim yok ama bu benim için biraz daha hızlı çalışmalı.
kmm

@Kevin: John'un çözümü ne olacak? Hadley'inkinden ~ 10 kat daha hızlıdır ve tolerans belirlemenizi sağlar. Başka bir şekilde eksik mi?
Joshua Ulrich

Lütfen biraz kıyaslama sağlayın - az önce benimkinin bir milyon üniformalı vektör için aynı olup olmadığını kontrol ettim.
hadley

@hadley: Koşuyordum , OP'nin system.time(for(i in 1:1e4) zero_range(x))neresindeydi x. John'un çözümü için ~ 10 kat, için x~ 3 kat daha hızlı yve biraz daha yavaştır runif(1e6).
Joshua Ulrich

Eğer 0,00023 ve 0,000023 saniye arasındaki farka bakıyoruz zaman 10x fark çok önemli değil - ve DWin muhtemelen tolerans belirtilen derecede aynıdır iddia ediyorum;)
Hadley

46

Neden varyansı kullanmıyorsunuz:

var(x) == 0

Tüm öğeleri xeşitse, bir varyans elde edersiniz 0.


17
length(unique(x))=1yaklaşık iki kat daha hızlı sonuçlanır, ancak varkısa ve özdür, bu da güzeldir.
AdamO

YohanBadia, bir c dizisine sahibim (-5.532456e-09, 1.695298e-09) ve John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSEdiğer tüm testlerin R'deki değerlerin aynı olduğunu anladığını anlamaya başladım . Bu bağlamda varyans testi nasıl kullanılabilir?
mjs

Dizinizdeki 2 değer aynı değil. Testin neden geri dönmesini TRUEistersiniz? John'un cevabı durumunda, farkın belirli bir eşiğin üzerinde olup olmadığını kontrol edersiniz. Sizin durumunuzda, 2 değer arasındaki fark çok düşüktür ve bu, tanımladığınız eşiğin altında olmasına neden olabilir.
Yohan Obadia

41

Hepsi sayısal değerlerse, tol sizin toleransınızsa o zaman ...

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

probleminizin çözümü.

DÜZENLE:

Buna ve diğer yanıtlara baktıktan ve birkaç şeyi karşılaştırdıktan sonra, aşağıdakiler DWin yanıtının iki katından daha hızlı çıkıyor.

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

Bu biraz edilenden çok daha kısa olduğunu diff(range(x))beri diffçok farklı olmamalı -ve absiki numaraları ile. Aralığı talep etmek, minimum ve maksimum elde etmeyi optimize etmelidir. Hem diffve rangeilkel işlevlerdir. Ama zamanlama yalan söylemez.


Ortalamayı bölmeye kıyasla çıkarmanın göreceli yararları hakkında yorum yapabilir misiniz?
hadley

Hesaplama açısından daha basittir. Sisteme ve R'nin nasıl derlendiğine ve vektörleştirildiğine bağlı olarak, daha az güç tüketimi ile daha hızlı gerçekleştirilecektir. Ayrıca, ortalamaya böldüğünüzde, test edilen sonucunuz 1'e göreli iken, çıkarma ile 0'dır, bu da bana daha hoş geliyor. Ayrıca toleransın daha basit bir yorumu vardır.
John

1
Ancak, aralığı çıkarmak için gereken arama ve sıralama, basit bir çıkarmadan çok daha hesaplamalı olarak pahalı olduğundan, bölme o kadar da karmaşık değildir. Test ettim ve yukarıdaki kod sıfır_aralık işlevinden Hadley'den yaklaşık 10 kat daha hızlı (ve sizinki buradaki en hızlı doğru cevapla ilgili). Dirk'in karşılaştırma işlevi acımasızca yavaştır. Buradaki en hızlı cevap budur.
John

Cevabınızda Josh'un zamanlama yorumlarını gördüm Hadley ... Sıfır_aralığının daha hızlı olduğu herhangi bir durum anlamadım. Tutarsızlık biraz daha hızlı (belki% 20) ile 10x arasındadır, bu cevap her zaman lehinedir. Bir dizi yöntem denedi.
John

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

Aynı çizgide bir başkası:

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

Bunun çok küçük sayılar için pek işe yaradığını sanmıyorum:x <- seq(1, 10) / 1e10
hadley

2
@Hadley: OP, muhtemelen çok küçük farklılıkları umursamadığı için bir toleransın belirlenmesine izin verecek bir çözüm istedi. all.equal diğer toleranslarla kullanılabilir ve OP bunu anlıyor gibi görünüyor.
IRTFM

2
Kendimi çok net ifade etmedim - örneğimde en büyük ve en küçük sayılar arasında on kat göreceli bir fark var. Bu muhtemelen fark etmek isteyeceğiniz bir şey! Veri aralığına göre sayısal toleransın hesaplanması gerektiğini düşünüyorum - bunu geçmişte yapmadım ve sorunlara neden oldu.
hadley

2
Seni en ufak bir yanlış anladığımı sanmıyorum. Sadece soruyu soran kişinin, fiilen sıfır olan sayılar için on kat göreli farkı yok sayacak bir çözüm istediğini düşündüm. 1e-11 ve 1e-13 arasındaki farkı görmezden gelecek bir çözüm istediğini duydum.
IRTFM

5
İnsanlara istediklerini değil, ihtiyaçları olanı vermeye çalışıyorum;) Ama alınan nokta.
hadley

17

Kontrol edebilirsin all(v==v[1])


Bu harika bc dizelerle de çalışıyor! Teşekkürler
arvi1000

1
Bu NA, vektörünüzde yoksa çalışır : x <- c(1,1,NA); all(x == x[1])döner NA, değil FALSE. Bu gibi durumlarda length(unique(x)) == 1işe yarar.
HBat

16

Kullanabilir identical()ve all.equal()ilk öğeyi diğerleriyle karşılaştırarak karşılaştırmayı etkili bir şekilde aşağıdakiler arasında gezdirebilirsiniz:

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> 

Bu şekilde identical(), gerektiği kadar epsilon ekleyebilirsiniz .


2
Yine de son derece verimsiz ... (bilgisayarımda bir milyon sayı için yaklaşık 10 saniye sürüyor)
hadley

2
Şüphesiz. OP ancak bu yapılabilir olup olmadığını sorguluyorum hiç . İyi yapmak ikinci bir adımdır. Ve döngülerle nerede durduğumu biliyorsun ... ;-)
Dirk Eddelbuettel

10
Bu döngüler harika mı? ;)
hadley

4
Bu yaklaşımın sevdiğim yanı sayısal olmayan nesnelerle de kullanılabilmesidir.
Luciano Selzer

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

11

Bu soruya defalarca geri dönüp Rcppdurduğum için R, cevabın gerçekten olması durumunda genellikle çözümlerin herhangi birinden çok daha hızlı FALSEolacak (çünkü bir uyuşmazlıkla karşılaştığı anda duracaktır) ve aynı hıza sahip olacak bir çözüm var. Cevap ise en hızlı R çözümü olarak TRUE. Örneğin, OP kıyaslaması için, system.timebu işlevi kullanarak tam olarak 0'da saatler.

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
Bu hız için güzel ve +1, ancak tüm unsurları 1. elementle karşılaştırmanın oldukça doğru olduğuna ikna olmadım. Bir vektör bu testi geçebilir, ancak max (x) ve min (x) arasındaki fark hassasiyetten daha büyük olabilir. Örneğinfast_equal(c(2,1,3), 1.5)
dww

@dww Gösterdiğiniz şey, karşılaştırmanın hassas sorunlarınız olduğunda geçişli olmadığı - yani a == b, kayan nokta karşılaştırmaları yapıyorsanız b == cille de ima a == cedilmediği. Bu sorunu önlemek için hassasiyetinizi öğe sayısına bölebilir veya bunu hesaplamak minve maxbunu bir durdurma koşulu olarak kullanmak için algoritmayı değiştirebilirsiniz .
eddi

10

Ben bir vektör içinde sadece unsurları kontrol edebilirsiniz bunun için özel olarak bir işlev, yazdım, ama bir listedeki tüm unsurları olmadığını kontrol özelliğine sahip olmasını özdeş . Elbette karakter vektörlerini ve diğer tüm vektör türlerini de iyi idare eder. Aynı zamanda uygun hata işlemeye sahiptir.

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

Şimdi bazı örnekler deneyin.

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

Gerçekte min, ortalama veya maks kullanmanıza gerek yoktur. John'un cevabına göre:

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

3

Burada min, max hilesini kullanan ancak bir veri çerçevesi için bir alternatif. Örnekte sütunları karşılaştırıyorum ancak margin parametresi applysatırlar için 1 olarak değiştirilebilir.

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

Eğer valid == 0o zaman tüm unsurları aynıdır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.