Menguji kesetaraan di antara semua elemen vektor tunggal

101

Saya mencoba untuk menguji apakah semua elemen vektor sama satu sama lain. Solusi yang saya temukan tampaknya agak tidak langsung, keduanya melibatkan pemeriksaan length().

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

Dengan unique():

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

Dengan rle():

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

Solusi yang memungkinkan saya menyertakan nilai toleransi untuk menilai 'kesetaraan' di antara elemen akan ideal untuk menghindari masalah FAQ 7.31 .

Apakah ada fungsi bawaan untuk jenis pengujian yang telah saya abaikan sepenuhnya? identical()dan all.equal()membandingkan dua objek R, sehingga tidak akan berfungsi di sini.

Edit 1

Berikut beberapa hasil benchmarking. Menggunakan kode:

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)

Dengan hasil:

          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

Jadi sepertinya diff(range(x)) < .Machine$double.eps ^ 0.5yang tercepat.

kmm
sumber

Jawaban:

37

Saya menggunakan metode ini, yang membandingkan min dan max, setelah membagi dengan cara:

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

Jika Anda menggunakan ini lebih serius, Anda mungkin ingin menghapus nilai yang hilang sebelum menghitung kisaran dan mean.

hadley
sumber
Saya memilih yang ini karena lebih cepat dari Dirk. Saya tidak memiliki jutaan elemen, tetapi ini seharusnya berjalan sedikit lebih cepat untuk saya.
kmm
@Kevin: bagaimana dengan solusi John? Ini ~ 10x lebih cepat dari Hadley dan memungkinkan Anda mengatur toleransi. Apakah itu kekurangan dalam beberapa hal lain?
Joshua Ulrich
Harap berikan beberapa pembandingan - Saya baru saja memeriksa milik saya hampir sama untuk vektor sejuta seragam.
hadley
@adley: Saya berlari system.time(for(i in 1:1e4) zero_range(x)), dari mana xdari OP. Solusi John ~ 10x untuk x, ~ 3x lebih cepat untuk ydan sedikit lebih lambat untuk runif(1e6).
Joshua Ulrich
Perbedaan 10x tidak terlalu menjadi masalah ketika Anda melihat perbedaan antara 0,00023 dan 0,000023 detik - dan DWin mungkin akan mengklaim bahwa keduanya sama dengan tingkat toleransi yang ditentukan;)
hadley
46

Mengapa tidak hanya menggunakan varians:

var(x) == 0

Jika semua elemen xsama, Anda akan mendapatkan varian sebesar 0.

Yohan Obadia
sumber
17
length(unique(x))=1akhirnya menjadi sekitar dua kali lebih cepat, tetapi varsingkat dan itu bagus.
AdamO
YohanBadia, saya memiliki array c (-5.532456e-09, 1.695298e-09), dan mendapatkan John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSEarti semua tes lainnya mengenali bahwa nilai-nilai identik dalam R. Bagaimana uji varians dapat digunakan dalam konteks itu?
mjs
2 nilai dalam larik Anda tidak identik. Mengapa Anda ingin tes tersebut kembali TRUE? Dalam kasus jawaban John, Anda memeriksa apakah perbedaannya di atas ambang tertentu. Dalam kasus Anda, perbedaan antara 2 nilai tersebut sangat rendah, yang dapat membuatnya berada di bawah ambang batas yang Anda tetapkan.
Yohan Obadia
41

Jika semuanya nilai numerik maka jika tol adalah toleransi Anda maka ...

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

adalah solusi untuk masalah Anda.

EDIT:

Setelah melihat ini, dan jawaban lainnya, dan membandingkan beberapa hal berikut ini keluar dua kali lebih cepat dari jawaban DWin.

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

Ini sedikit mengejutkan lebih cepat daripada diff(range(x))karena diffseharusnya tidak jauh berbeda dari -dan absdengan dua angka. Meminta kisaran harus mengoptimalkan mendapatkan minimum dan maksimum. Keduanya diffdan rangemerupakan fungsi primitif. Tapi waktunya tidak berbohong.

John
sumber
Dapatkah Anda mengomentari manfaat relatif mengurangi mean dibandingkan dengan membaginya?
hadley
Ini lebih sederhana secara komputasi. Bergantung pada sistem, dan bagaimana R dikompilasi dan di-vektorisasi, ini akan diselesaikan lebih cepat dengan konsumsi daya yang lebih sedikit. Juga, ketika Anda membagi dengan rata-rata, hasil pengujian Anda relatif terhadap 1 sedangkan dengan pengurangan itu 0, yang menurut saya lebih baik. Selain itu, toleransi memiliki interpretasi yang lebih lugas.
Yohanes
1
Namun, pembagian itu tidak terlalu rumit karena pencarian dan pengurutan yang diperlukan untuk mengekstrak rentang jauh lebih mahal secara komputasi daripada pengurangan sederhana. Saya mengujinya dan kode di atas sekitar 10x lebih cepat daripada fungsi zero_range Hadley (dan milik Anda adalah jawaban yang benar tercepat di sini). Fungsi perbandingan Dirk sangat lambat. Ini adalah jawaban tercepat disini.
Yohanes
Baru saja melihat komentar waktu Josh di jawaban Anda Hadley ... Saya tidak mendapatkan situasi di mana zero_range lebih cepat. Perbedaannya antara sedikit lebih cepat (mungkin 20%) hingga 10x selalu mendukung jika jawaban ini. Ini mencoba sejumlah metode.
Yohanes
24
> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE

Lainnya di sepanjang garis yang sama:

> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE
IRTFM
sumber
Saya tidak berpikir ini bekerja dengan baik untuk nomor yang sangat kecil:x <- seq(1, 10) / 1e10
hadley
2
@Hadley: OP meminta solusi yang akan memungkinkan spesifikasi toleransi, mungkin karena dia tidak peduli dengan perbedaan yang sangat kecil. all.equal dapat digunakan dengan toleransi lain dan OP tampaknya memahami hal ini.
IRTFM
2
Saya tidak mengekspresikan diri saya dengan sangat jelas - dalam contoh saya, ada perbedaan relatif sepuluh kali lipat antara angka terbesar dan terkecil. Itu mungkin sesuatu yang ingin Anda perhatikan! Saya pikir toleransi numerik perlu dihitung relatif terhadap kisaran data - Saya belum pernah melakukan ini di masa lalu dan ini menyebabkan masalah.
hadley
2
Saya tidak berpikir saya salah paham sama sekali. Saya hanya mengira si penanya meminta solusi yang akan mengabaikan perbedaan relatif sepuluh kali lipat untuk bilangan yang secara efektif nol. Saya mendengarnya meminta solusi yang akan mengabaikan perbedaan antara 1e-11 dan 1e-13.
IRTFM
5
Saya mencoba dan memberi orang apa yang mereka butuhkan, bukan apa yang mereka inginkan;) Tapi poin diambil.
hadley
16

Anda dapat menggunakan identical()dan all.equal()dengan membandingkan elemen pertama dengan elemen lainnya, secara efektif menyapu perbandingan di:

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> 

Dengan cara itu Anda dapat menambahkan epsilon apa pun identical()sesuai kebutuhan.

Dirk Eddelbuettel
sumber
2
Sangat tidak efisien ... (di komputer saya dibutuhkan sekitar 10 detik untuk satu juta angka)
hadley
2
Tanpa keraguan. Namun OP mempertanyakan apakah ini bisa dilakukan sama sekali . Melakukannya dengan baik adalah langkah kedua. Dan Anda tahu di mana saya berdiri dengan loop ... ;-)
Dirk Eddelbuettel
10
Loop itu luar biasa? ;)
hadley
4
Apa yang saya suka tentang appoach ini adalah dapat digunakan dengan objek non numerik.
Luciano Selzer
bandingkan <- function (v) all (sapply (as.list (v [-1]), FUN = function (z) {isTRUE (all.equal (z, v [1]))}))
N. McA .
16

Anda tinggal memeriksanya all(v==v[1])

Retribusi Maya
sumber
Yang ini bagus karena bekerja dengan string juga! Terima kasih
arvi1000
Ini berfungsi kecuali Anda memiliki NAdi vektor Anda: x <- c(1,1,NA); all(x == x[1])return NA, not FALSE. Dalam kasus seperti itu length(unique(x)) == 1berhasil.
HBat
11

Karena saya terus kembali ke pertanyaan ini berulang kali, berikut adalah Rcppsolusi yang umumnya akan jauh lebih cepat daripada Rsolusi mana pun jika jawabannya benar-benar FALSE(karena itu akan berhenti saat menemui ketidakcocokan) dan akan memiliki kecepatan yang sama sebagai solusi R tercepat jika jawabannya adalah TRUE. Misalnya untuk benchmark OP, system.timeclock tepat 0 menggunakan fungsi ini.

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
eddi
sumber
1
Ini bagus & +1 untuk kecepatan, tetapi saya tidak yakin bahwa membandingkan semua elemen dengan elemen pertama cukup tepat. Sebuah vektor dapat lulus tes ini, namun perbedaan antara max (x) dan min (x) lebih besar dari presisi. Misalnyafast_equal(c(2,1,3), 1.5)
dww
@dww Apa yang Anda tunjukkan adalah bahwa perbandingan tidak bersifat transitif ketika Anda memiliki masalah presisi - yaitu a == b, b == ctidak selalu berarti a == cjika Anda melakukan perbandingan floating point. Anda dapat membagi presisi Anda dengan jumlah elemen untuk menghindari masalah ini, atau memodifikasi algoritme untuk menghitung mindan maxdan menggunakannya sebagai kondisi penghentian.
eddi
10

Saya menulis fungsi khusus untuk ini, yang tidak hanya dapat memeriksa elemen dalam vektor, tetapi juga mampu memeriksa apakah semua elemen dalam daftar identik . Tentu saja itu juga menangani vektor karakter dan semua jenis vektor lainnya dengan baik. Ini juga memiliki penanganan kesalahan yang tepat.

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

Sekarang coba beberapa contoh.

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
Lawrence Lee
sumber
4

Anda sebenarnya tidak perlu menggunakan min, mean, atau max. Berdasarkan jawaban John:

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

sumber
3

Di sini alternatif menggunakan trik min, max tetapi untuk bingkai data. Dalam contoh saya membandingkan kolom tetapi parameter margin dari applydapat diubah menjadi 1 untuk baris.

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

Jika valid == 0kemudian semua elemennya sama

pedrosaurio
sumber