จะลบหลายค่าจากเวกเตอร์ได้อย่างไร?


125

ฉันมีเวกเตอร์เช่น: a = c(1:10)และฉันต้องการลบหลายค่าเช่น:2, 3, 5

จะลบตัวเลขเหล่านั้น ( ไม่ใช่ตำแหน่งในเวกเตอร์) ในเวกเตอร์ได้อย่างไร?

ในขณะที่ฉันวนเวกเตอร์และทำสิ่งที่ชอบ:

a[!a=NUMBER_TO_REMOVE]

แต่ฉันคิดว่ามีฟังก์ชั่นที่ทำโดยอัตโนมัติ

คำตอบ:


192

ตัว%in%ดำเนินการจะบอกให้คุณทราบว่าองค์ประกอบใดบ้างที่อยู่ในตัวเลขที่จะลบ:

> a <- sample (1 : 10)
> remove <- c (2, 3, 5)
> a
 [1] 10  5  2  7  1  6  3  4  8  9
> a %in% remove
 [1] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
> a [! a %in% remove]
 [1] 10  7  1  6  4  8  9

โปรดทราบว่าการดำเนินการนี้จะลบสิ่งที่เข้ากันไม่ได้ออกไปอย่างเงียบ ๆ (สิ่งต่างๆเช่นNAหรือInf)เช่นกัน (แม้ว่าจะเก็บค่าที่ซ้ำกันไว้aตราบเท่าที่ไม่มีอยู่ในรายการremove)

  • หากaสามารถมีสิ่งที่ไม่เหมือนกันได้ แต่removeจะไม่มีเราสามารถใช้matchโดยบอกให้ส่งคืน0สำหรับสิ่งที่ไม่ตรงกันและสิ่งที่เทียบไม่ได้ ( %in%เป็นช็อตคัทสำหรับคอนแวนต์สำหรับmatch):

    > a <- c (a, NA, Inf)
    > a
     [1]  10   5   2   7   1   6   3   4   8   9  NA Inf
    > match (a, remove, nomatch = 0L, incomparables = 0L)
     [1] 0 3 1 0 0 0 2 0 0 0 0 0
    > a [match (a, remove, nomatch = 0L, incomparables = 0L) == 0L]
    [1]  10   7   1   6   4   8   9  NA Inf

    incomparables = 0ไม่จำเป็นเนื่องจากสิ่งที่ไม่เหมือนกันจะไม่ตรงกันเสมอไป แต่ฉันจะรวมไว้เพื่อประโยชน์ในการอ่าน
    นี่คือ btw สิ่งที่setdiffทำภายใน (แต่ไม่มีการuniqueทิ้งรายการที่ซ้ำกันaซึ่งไม่ได้อยู่ในremove)

  • หากremoveมีสิ่งที่เข้ากันไม่ได้คุณจะต้องตรวจสอบทีละรายการเช่น

    if (any (is.na (remove))) 
      a <- a [! is.na (a)]

    (สิ่งนี้ไม่ได้แตกต่างNAจากNaNคู่มือ R แต่อย่างใดเตือนว่าเราไม่ควรพึ่งพาการมีความแตกต่างระหว่างกัน)

    สำหรับInf/ -Infคุณจะต้องตรวจสอบทั้งสองsignและis.finite


1
setdiffจะดีกว่าเนื่องจากทำทุกอย่างในการดำเนินการเดียวและอ้างอิงเวกเตอร์ที่แก้ไขเพียงครั้งเดียว
Olexa

1
@Olexa: ความแตกต่างของชุดไม่เหมือนกับการลบการเกิดขึ้นทั้งหมดของชุดตัวเลขที่กำหนดออกจากเวกเตอร์: มันจะลบรายการที่ซ้ำกันในaที่ไม่ได้อยู่ในremoveนั้นด้วย หากไม่ใช่ปัญหาคุณสามารถใช้setdiffไฟล์. setdiff, btw, ใช้matchซึ่ง%in%เป็นทางลัด
cbeleites ไม่พอใจกับ SX

97

คุณสามารถใช้setdiff.

ป.ร. ให้ไว้

a <- sample(1:10)
remove <- c(2, 3, 5)

แล้วก็

> a
 [1] 10  8  9  1  3  4  6  7  2  5
> setdiff(a, remove)
[1] 10  8  9  1  4  6  7

1
มีประโยชน์มากเมื่อaเป็นผลลัพธ์ของฟังก์ชันอื่นเพื่อให้คุณสามารถทำสิ่งต่างๆในหนึ่งบรรทัดแทนที่จะเป็น 3 และตัวแปร temp
jf328

14
สิ่งนี้จะให้ผลลัพธ์ที่แตกต่างจาก%in%โซลูชันหากเวกเตอร์อินพุตมีรายการที่ซ้ำกัน (ในกรณีนี้setdiffจะส่งคืนเฉพาะชุดที่ไม่ซ้ำกันกล่าวคือไม่มีรายการที่ซ้ำกัน)
talat

2
@docendodiscimus: fsetdiffของdata.tableแพ็คเกจมีallแฟล็ก(F เริ่มต้น) ที่อนุญาตให้เก็บข้อมูลที่ซ้ำกันในเวกเตอร์อินพุต
Juergen

9

คุณสามารถทำได้ดังนี้:

> x<-c(2, 4, 6, 9, 10) # the list
> y<-c(4, 9, 10) # values to be removed

> idx = which(x %in% y ) # Positions of the values of y in x
> idx
[1] 2 4 5
> x = x[-idx] # Remove those values using their position and "-" operator
> x
[1] 2 6

ในไม่ช้า

> x = x[ - which(x %in% y)]

1
สิ่งที่คุณเรียกว่ารายการในตัวอย่างของคุณคือเวกเตอร์ใช่ไหม
patrick

ใช่ฉันหมายถึงเวกเตอร์ ขอบคุณสำหรับความคิดเห็น
ykpemre

ไม่มีความจำเป็นในwhichที่นี่ โดยพื้นฐานแล้วจะเหมือนกับคำตอบของ @cbeleites
David Arenburg

ใช่มันคล้ายกัน แต่แตกต่างกันในบางมุมมอง whichส่งกลับดัชนีของค่า TRUE ดังนั้นจึงสามารถใช้เครื่องหมายลบเพื่อบอกว่า "ดัชนีอื่นที่ไม่ใช่ดัชนีเหล่านี้" นอกจากนี้ยังwhichอ่านได้ง่ายกว่าเนื่องจากใกล้เคียงกับภาษาธรรมชาติมากขึ้น
ykpemre

4

แทน

x <- x[! x %in% c(2,3,5)]

โดยใช้แพ็คเกจpurrrและmagrittrคุณสามารถทำได้:

your_vector %<>% discard(~ .x %in% c(2,3,5))

สิ่งนี้ช่วยให้สามารถsubsetใช้ชื่อเวกเตอร์ได้เพียงครั้งเดียว และคุณสามารถใช้ในท่อ :)


คุณช่วยอธิบายข้อความสุดท้ายของคุณเกี่ยวกับความยาวชื่อตัวแปรได้ไหม ทำไมคุณไม่ชอบ? ทำไมถึงดีกว่าทางอื่น? หรือลบย่อหน้านั้นออกเนื่องจากไม่เกี่ยวข้องกับปัญหา / คำถามหลัก
rodrigoap

2

ก่อนอื่นเราสามารถกำหนดตัวดำเนินการใหม่

"%ni%" = Negate( "%in%" )

จากนั้นมันก็เหมือนกับ x ไม่ได้ลบออก

x <- 1:10
remove <- c(2,3,5)
x <- x[ x %ni% remove ]

หรือทำไมต้องไปลบให้ไปโดยตรง

x <- x[ x %ni% c(2,3,5)]

3
คำถามกล่าวโดยเฉพาะว่า 2, 3 และ 5 ไม่ใช่ตำแหน่งในเวกเตอร์
blakeoft

1

UPDATE:

คำตอบทั้งหมดข้างต้นใช้ไม่ได้กับค่าซ้ำคำตอบของ @ BenBolker โดยใช้เพรดิเคตduplicated()ช่วยแก้ปัญหานี้ได้:

full_vector[!full_vector %in% searched_vector | duplicated(full_vector)]

คำตอบเดิม: ที่นี่ฉันเขียนฟังก์ชันเล็กน้อยสำหรับสิ่งนี้:

exclude_val<-function(full_vector,searched_vector){

      found=c()

      for(i in full_vector){  

        if(any(is.element(searched_vector,i))){
          searched_vector[(which(searched_vector==i))[1]]=NA
        }
        else{
          found=c(found,i)
        }
    }

    return(found)
}

ดังนั้นขอบอกและfull_vector=c(1,2,3,4,1)searched_vector=c(1,2,3)

exclude_val(full_vector,searched_vector)จะกลับมา (4,1) (4)แต่ข้างต้นคำตอบที่จะกลับมาเพียงแค่


1
แล้วfull_vector[!full_vector %in% searched_vector | duplicated(full_vector)]ไง?
Ben Bolker

@BenBolker อาฉันไม่รู้ว่าเพรดิเคต "ซ้ำกัน": ((ตอนนี้ฉันจะลบคำตอบของฉันหรือเปลี่ยนเป็นแสดงเฉพาะของคุณแทน?
Özgür

@BenBolker วิธีแก้ปัญหาของคุณไม่ถูกต้อง เพียงแค่พยายามที่: full_vector = c(1,1,1,2,3); searched_vector = c(1,1,3);- ที่ผลิตแทนการตอบที่ถูกต้อง1, 1, 2 1, 2
fnl

เพียงเพื่อเพิ่มวิธีแก้ปัญหาที่เป็นไปได้และถูกต้องสำหรับค่าซ้ำ:removeif <- function(from, where) { for (i in where) if (i %in% from) {from = from[-match(i, from)]}; from}
fnl

1
q <- c(1,1,2,2,3,3,3,4,4,5,5,7,7)
rm <- q[11]
remove(rm)
q
q[13] = NaN
q
q %in% 7

สิ่งนี้จะตั้งค่า 13 ในเวกเตอร์ไม่ใช่ตัวเลข (NAN) ซึ่งแสดงการลบเท็จ (q [c (11,12,13)]) หากคุณลองทำเช่นนี้คุณจะเห็นว่าฟังก์ชันลบไม่ทำงานกับจำนวนเวกเตอร์ คุณลบเวกเตอร์ทั้งหมด แต่อาจไม่ใช่องค์ประกอบเดียว


1

นอกจากนี้ยังsubsetมีบางครั้งที่อาจมีประโยชน์:

a <- sample(1:10)
bad <- c(2, 3, 5)

> subset(a, !(a %in% bad))
[1]  9  7 10  6  8  1  4
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.