ทดสอบความเท่าเทียมกันระหว่างองค์ประกอบทั้งหมดของเวกเตอร์เดียว


101

ฉันกำลังพยายามทดสอบว่าองค์ประกอบทั้งหมดของเวกเตอร์มีค่าเท่ากันหรือไม่ 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

วิธีการแก้ปัญหาที่จะให้ฉันมีค่าความอดทนในการประเมิน 'ความเท่าเทียมกันในหมู่องค์ประกอบจะเหมาะที่จะหลีกเลี่ยงคำถามที่พบบ่อย 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จะเร็วที่สุด

คำตอบ:


37

ฉันใช้วิธีนี้ซึ่งเปรียบเทียบค่าต่ำสุดและสูงสุดหลังจากหารด้วยค่าเฉลี่ย:

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

หากคุณใช้สิ่งนี้อย่างจริงจังมากขึ้นคุณอาจต้องการลบค่าที่ขาดหายไปก่อนที่จะคำนวณช่วงและค่าเฉลี่ย


ฉันเลือกอันนี้เพราะเร็วกว่า Dirk ฉันไม่มีองค์ประกอบนับล้าน แต่สิ่งนี้น่าจะเร็วกว่าสำหรับฉันเล็กน้อย
kmm

@ เควิน: แล้ววิธีแก้ปัญหาของจอห์นล่ะ? เร็วกว่า Hadley ประมาณ 10 เท่าและช่วยให้คุณตั้งค่าความอดทนได้ มันบกพร่องในทางอื่นหรือไม่?
Joshua Ulrich

โปรดระบุการเปรียบเทียบ - ฉันเพิ่งตรวจสอบของฉันว่าเหมือนกันสำหรับเวกเตอร์ของเครื่องแบบล้านชุด
hadley

@hadley: ฉันกำลังวิ่งอยู่system.time(for(i in 1:1e4) zero_range(x))ที่ไหนxมาจาก OP วิธีการแก้ปัญหาของจอห์นเป็น ~ 10 เท่าสำหรับx~ 3x เร็วขึ้นสำหรับและช้าลงเล็กน้อยy runif(1e6)
Joshua Ulrich

ความแตกต่าง 10x ไม่สำคัญมากนักเมื่อคุณดูความแตกต่างระหว่าง 0.00023 ถึง 0.000023 วินาทีและ DWin อาจอ้างว่ามันเท่ากันกับระดับความอดทนที่ระบุ)
hadley

46

ทำไมไม่ใช้ความแปรปรวน:

var(x) == 0

ถ้าองค์ประกอบทั้งหมดของมีค่าเท่ากันคุณจะได้รับความแปรปรวนของx0


17
length(unique(x))=1ลงเอยด้วยการเร็วเป็นสองเท่า แต่varก็สั้นลงซึ่งเป็นสิ่งที่ดี
AdamO

YohanBadia ฉันมีอาร์เรย์ c (-5.532456e-09, 1.695298e-09) และได้รับJohn test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSEความหมายการทดสอบอื่น ๆ ทั้งหมดรับรู้ว่าค่านั้นเหมือนกันใน R จะใช้การทดสอบความแปรปรวนในบริบทนั้นได้อย่างไร
mjs

2 ค่าในอาร์เรย์ของคุณไม่เหมือนกัน ทำไมคุณต้องการทดสอบเพื่อกลับTRUE? ในกรณีของคำตอบของ John คุณต้องตรวจสอบว่าความแตกต่างนั้นสูงกว่าเกณฑ์ที่กำหนดหรือไม่ ในกรณีของคุณความแตกต่างระหว่างค่า 2 ค่านั้นต่ำมากซึ่งอาจทำให้ค่าดังกล่าวต่ำกว่าเกณฑ์ที่คุณกำหนดไว้
Yohan Obadia

41

ถ้าเป็นค่าตัวเลขทั้งหมดถ้า tol ​​คือความอดทนของคุณแล้วล่ะก็ ...

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

เป็นทางออกสำหรับปัญหาของคุณ

แก้ไข:

หลังจากดูคำตอบนี้และคำตอบอื่น ๆ และการเปรียบเทียบบางสิ่งต่อไปนี้จะพบว่าเร็วกว่าคำตอบ DWin ถึงสองเท่า

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

นี้เป็นบิตที่น่าแปลกใจได้เร็วกว่าdiff(range(x))เนื่องจากdiffไม่ควรจะแตกต่างกันมากกว่า-และabsด้วยตัวเลขสอง การขอช่วงควรเพิ่มประสิทธิภาพการรับค่าต่ำสุดและสูงสุด ทั้งสองdiffและrangeเป็นฟังก์ชันดั้งเดิม แต่เวลาไม่โกหก


คุณสามารถแสดงความคิดเห็นเกี่ยวกับข้อดีของการลบค่าเฉลี่ยเทียบกับการหารด้วยมันได้หรือไม่?
hadley

มันง่ายกว่าในการคำนวณ ขึ้นอยู่กับระบบและวิธีการคอมไพล์และเวกเตอร์ R จะทำได้เร็วขึ้นโดยใช้พลังงานน้อยลง นอกจากนี้เมื่อคุณหารด้วยค่าเฉลี่ยผลการทดสอบของคุณจะสัมพันธ์กับ 1 ในขณะที่การลบเป็น 0 ซึ่งดูเหมือนจะดีกว่าสำหรับฉัน นอกจากนี้ความอดทนยังมีการตีความที่ตรงไปตรงมามากขึ้น
John

1
แต่ก็ไม่มากนักที่การหารจะซับซ้อนเนื่องจากการค้นหาและการเรียงลำดับที่จำเป็นในการแยกช่วงนั้นมีค่าใช้จ่ายในการคำนวณมากกว่าการลบแบบธรรมดา ฉันทดสอบแล้วและโค้ดด้านบนเร็วกว่าฟังก์ชัน zero_range Hadley ประมาณ 10 เท่า (และของคุณเป็นคำตอบที่ถูกต้องเร็วที่สุดที่นี่) ฟังก์ชันเปรียบเทียบของ Dirk นั้นช้าอย่างไร้ความปราณี นี่คือคำตอบที่เร็วที่สุดที่นี่
John

เพิ่งเห็นความคิดเห็นเกี่ยวกับเวลาของ Josh ในคำตอบของคุณ Hadley ... ฉันไม่เข้าใจสถานการณ์ใด ๆ ที่ zero_range เร็วกว่า ความคลาดเคลื่อนอยู่ระหว่างเร็วกว่าเล็กน้อย (อาจ 20%) ถึง 10 เท่าเสมอหากคำตอบนี้ มันพยายามหลายวิธี
John

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

ฉันไม่คิดว่ามันจะได้ผลดีสำหรับคนจำนวนน้อยมาก:x <- seq(1, 10) / 1e10
hadley

2
@Hadley: OP ขอวิธีแก้ปัญหาที่อนุญาตให้มีข้อกำหนดของความอดทนซึ่งน่าจะเป็นเพราะเขาไม่สนใจเกี่ยวกับความแตกต่างเล็กน้อย all.equal สามารถใช้กับความคลาดเคลื่อนอื่น ๆ ได้และดูเหมือนว่า OP จะเข้าใจสิ่งนี้
IRTFM

2
ฉันไม่ได้แสดงออกอย่างชัดเจนนัก - ในตัวอย่างของฉันมีความแตกต่างสัมพัทธ์ 10 เท่าระหว่างจำนวนที่มากที่สุดและน้อยที่สุด นั่นอาจเป็นสิ่งที่คุณต้องการสังเกต! ฉันคิดว่าต้องคำนวณค่าเผื่อตัวเลขเทียบกับช่วงของข้อมูล - ฉันไม่เคยทำแบบนี้มาก่อนและทำให้เกิดปัญหา
hadley

2
ฉันไม่คิดว่าฉันเข้าใจคุณผิดอย่างที่สุด ฉันแค่คิดว่าผู้ถามกำลังขอวิธีแก้ปัญหาที่จะเพิกเฉยต่อความแตกต่างสัมพัทธ์สิบเท่าสำหรับตัวเลขที่เป็นศูนย์อย่างมีประสิทธิภาพ ฉันได้ยินว่าเขาขอวิธีแก้ปัญหาที่จะไม่สนใจความแตกต่างระหว่าง 1e-11 และ 1e-13
IRTFM

5
ฉันพยายามให้สิ่งที่พวกเขาต้องการไม่ใช่สิ่งที่พวกเขาต้องการ แต่เป็นประเด็น
hadley

17

คุณสามารถตรวจสอบ all(v==v[1])


อันนี้ยอดเยี่ยมมากมันใช้งานได้กับสตริงด้วย! ขอบคุณ
arvi1000

1
งานนี้ถ้าคุณมีNAในเวกเตอร์ของคุณ: x <- c(1,1,NA); all(x == x[1])ผลตอบแทนไม่ได้NA FALSEในกรณีเช่นนี้ได้length(unique(x)) == 1ผล
HBat

16

คุณสามารถใช้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> 

ด้วยวิธีนี้คุณสามารถเพิ่ม epsilon ได้identical()ตามต้องการ


2
ไม่มีประสิทธิภาพอย่างน่ากลัวแม้ว่า ... (ในคอมพิวเตอร์ของฉันใช้เวลาประมาณ 10 วินาทีสำหรับตัวเลขหนึ่งล้าน)
hadley

2
ไม่ต้องสงสัยเลย. แต่ OP ถูกถามว่านี้สามารถทำได้ในทุก การทำมันให้ดีเป็นขั้นตอนที่สอง และคุณรู้ว่าฉันยืนอยู่ที่ไหนกับลูป ... ;-)
Dirk Eddelbuettel

10
ลูปนั้นยอดเยี่ยมมาก? ;)
hadley

4
สิ่งที่ฉันชอบเกี่ยวกับ appoach นี้คือสามารถใช้กับวัตถุที่ไม่ใช่ตัวเลขได้
Luciano Selzer

เปรียบเทียบ <- function (v) all (sapply (as.list (v [-1]), FUN = function (z) {isTRUE (all.equal (z, v [1]))))
N. McA .

11

เนื่องจากฉันกลับมาที่คำถามนี้ซ้ำแล้วซ้ำอีกต่อไปนี้เป็น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

1
นี่เป็นสิ่งที่ดี & +1 สำหรับความเร็ว แต่ฉันไม่มั่นใจว่าการเปรียบเทียบองค์ประกอบทั้งหมดกับองค์ประกอบที่ 1 นั้นค่อนข้างถูกต้อง เวกเตอร์สามารถผ่านการทดสอบนี้ได้ แต่ความแตกต่างระหว่าง max (x) และ min (x) จะมากกว่าความแม่นยำ ยกตัวอย่างเช่นfast_equal(c(2,1,3), 1.5)
dww

@dww สิ่งที่คุณกำลังชี้ให้เห็นคือการเปรียบเทียบที่ไม่ transitive เมื่อคุณมีปัญหาความแม่นยำ - คือa == b, b == cไม่จำเป็นต้องหมายความa == cว่าคุณกำลังทำรถลอยจุด คุณสามารถแบ่งความแม่นยำของคุณโดยจำนวนขององค์ประกอบที่จะหลีกเลี่ยงปัญหานี้หรือปรับเปลี่ยนอัลกอริทึมในการคำนวณminและmaxและการใช้ที่เป็นเงื่อนไขหยุด
eddi

10

ฉันเขียนฟังก์ชันเฉพาะสำหรับสิ่งนี้ซึ่งสามารถตรวจสอบไม่เพียง แต่องค์ประกอบในเวกเตอร์ แต่ยังสามารถตรวจสอบได้ว่าองค์ประกอบทั้งหมดในรายการเหมือนกันหรือไม่ แน่นอนว่ามันจัดการเวกเตอร์อักขระและเวกเตอร์ประเภทอื่น ๆ ได้ดีเช่นกัน นอกจากนี้ยังมีการจัดการข้อผิดพลาดที่เหมาะสม

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

4

คุณไม่จำเป็นต้องใช้ค่าต่ำสุดค่าเฉลี่ยหรือสูงสุด จากคำตอบของ John:

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

3

นี่เป็นอีกทางเลือกหนึ่งโดยใช้เคล็ดลับขั้นต่ำสูงสุด แต่สำหรับกรอบข้อมูล ในตัวอย่างฉันกำลังเปรียบเทียบคอลัมน์ แต่พารามิเตอร์ระยะขอบจากapplyสามารถเปลี่ยนเป็น 1 สำหรับแถวได้

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

ถ้าvalid == 0องค์ประกอบทั้งหมดเหมือนกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.