ลบแถวที่ซ้ำกันโดยใช้ dplyr


128

ฉันมี data.frame แบบนี้ -

set.seed(123)
df = data.frame(x=sample(0:1,10,replace=T),y=sample(0:1,10,replace=T),z=1:10)
> df
   x y  z
1  0 1  1
2  1 0  2
3  0 1  3
4  1 1  4
5  1 0  5
6  0 1  6
7  1 0  7
8  1 0  8
9  1 0  9
10 0 1 10

ฉันต้องการลบแถวที่ซ้ำกันตามสองคอลัมน์แรก ผลผลิตที่คาดหวัง -

df[!duplicated(df[,1:2]),]
  x y z
1 0 1 1
2 1 0 2
4 1 1 4

ฉันกำลังมองหาวิธีแก้ปัญหาโดยใช้dplyrแพ็คเกจโดยเฉพาะ

คำตอบ:


137

หมายเหตุ : dplyrขณะนี้มีdistinctฟังก์ชันสำหรับวัตถุประสงค์นี้

คำตอบเดิมด้านล่าง:


library(dplyr)
set.seed(123)
df <- data.frame(
  x = sample(0:1, 10, replace = T),
  y = sample(0:1, 10, replace = T),
  z = 1:10
)

แนวทางหนึ่งคือการจัดกลุ่มแล้วเก็บเฉพาะแถวแรก:

df %>% group_by(x, y) %>% filter(row_number(z) == 1)

## Source: local data frame [3 x 3]
## Groups: x, y
## 
##   x y z
## 1 0 1 1
## 2 1 0 2
## 3 1 1 4

(ใน dplyr 0.2 คุณไม่ต้องการzตัวแปรดัมมี่และจะสามารถเขียนได้row_number() == 1)

ฉันยังคิดที่จะเพิ่มไฟล์ slice()ฟังก์ชันที่จะใช้งานได้เช่น:

df %>% group_by(x, y) %>% slice(from = 1, to = 1)

หรืออาจเป็นรูปแบบunique()ที่ช่วยให้คุณสามารถเลือกตัวแปรที่จะใช้:

df %>% unique(x, y)

4
@dotcomken ก่อนหน้านั้นก็สามารถใช้ได้df %>% group_by(x, y) %>% do(head(.,1))
Holger Brandl

16
@MahbubulMajumder ที่จะทำงาน แต่ค่อนข้างช้า dplyr 0.3 จะมีdistinct()
hadley

3
@hadley ฉันชอบฟังก์ชันที่ไม่ซ้ำกัน () และฟังก์ชันที่แตกต่าง () อย่างไรก็ตามพวกเขาทั้งหมดลบรายการที่ซ้ำกันที่ 2 ออกจากกรอบข้อมูล จะเกิดอะไรขึ้นถ้าฉันต้องการลบการเผชิญหน้าครั้งแรกทั้งหมดของค่าที่ซ้ำกัน? จะทำได้อย่างไร? ขอบคุณสำหรับความช่วยเหลือ!
FlyingDutch

2
@MvZB - คุณจะไม่จัดเรียง (desc ()) แล้วใช้ความแตกต่างหรือไม่?
Woodstock

ฉันแน่ใจว่ามีวิธีง่ายๆ แต่ถ้าฉันต้องการกำจัดทั้งสองแถวที่ซ้ำกันล่ะ? ฉันมักจะทำงานกับข้อมูลเมตาที่เกี่ยวข้องกับตัวอย่างทางชีววิทยาและหากฉันมีรหัสตัวอย่างซ้ำกันฉันมักจะไม่แน่ใจว่าแถวใดมีข้อมูลที่ถูกต้อง การเดิมพันที่ปลอดภัยที่สุดคือการทิ้งทั้งสองอย่างเพื่อหลีกเลี่ยงการเชื่อมโยงข้อมูลเมตาที่ผิดพลาด วิธีแก้ปัญหาง่ายๆนอกเหนือจากการสร้างรายการ ID ตัวอย่างที่ซ้ำกันและการกรองแถวที่มี ID เหล่านั้นหรือไม่
glongo_fishes

191

นี่คือวิธีแก้ปัญหาโดยใช้dplyr >= 0.5ไฟล์.

library(dplyr)
set.seed(123)
df <- data.frame(
  x = sample(0:1, 10, replace = T),
  y = sample(0:1, 10, replace = T),
  z = 1:10
)

> df %>% distinct(x, y, .keep_all = TRUE)
    x y z
  1 0 1 1
  2 1 0 2
  3 1 1 4

3
โซลูชันนี้ดูเหมือนจะเร็วกว่ามาก (10 เท่าในกรณีของฉัน) มากกว่าโซลูชันที่ Hadley ให้ไว้
Calimo

101
ในทางเทคนิคแล้วนี่เป็นวิธีแก้ปัญหาโดย Hadley :-)
Tyler Rinker

27

เพื่อความสมบูรณ์สิ่งต่อไปนี้ยังใช้ได้:

df %>% group_by(x) %>% filter (! duplicated(y))

อย่างไรก็ตามฉันชอบวิธีแก้ปัญหาโดยใช้distinctและฉันสงสัยว่ามันเร็วกว่าด้วย


7

ส่วนใหญ่แล้วทางออกที่ดีที่สุดคือการใช้distinct()dplyr ตามที่ได้แนะนำไปแล้ว

อย่างไรก็ตามนี่เป็นอีกแนวทางหนึ่งที่ใช้slice()ฟังก์ชันจาก dplyr

# Generate fake data for the example
  library(dplyr)
  set.seed(123)
  df <- data.frame(
    x = sample(0:1, 10, replace = T),
    y = sample(0:1, 10, replace = T),
    z = 1:10
  )

# In each group of rows formed by combinations of x and y
# retain only the first row

    df %>%
      group_by(x, y) %>%
      slice(1)

ความแตกต่างจากการใช้ distinct()ฟังก์ชัน

ข้อดีของโซลูชันนี้คือทำให้ชัดเจนว่าแถวใดถูกเก็บรักษาไว้จากดาต้าเฟรมเดิมและสามารถจับคู่กับarrange()ฟังก์ชันได้ดี

สมมติว่าคุณมีข้อมูลการขายของลูกค้าและคุณต้องการเก็บบันทึกหนึ่งรายการต่อลูกค้าหนึ่งรายและคุณต้องการให้บันทึกนั้นเป็นข้อมูลจากการซื้อล่าสุดของพวกเขา จากนั้นคุณสามารถเขียน:

customer_purchase_data %>%
   arrange(desc(Purchase_Date)) %>%
   group_by(Customer_ID) %>%
   slice(1)

3

เมื่อเลือกคอลัมน์ใน R สำหรับชุดข้อมูลที่ลดลงคุณมักจะลงเอยด้วยรายการที่ซ้ำกัน

สองบรรทัดนี้ให้ผลลัพธ์เหมือนกัน แต่ละเอาต์พุตชุดข้อมูลที่ไม่ซ้ำกันโดยมีสองคอลัมน์ที่เลือกเท่านั้น:

distinct(mtcars, cyl, hp);

summarise(group_by(mtcars, cyl, hp));

1

หากคุณต้องการค้นหาแถวที่ซ้ำกันคุณสามารถใช้ได้find_duplicatesจากhablar:

library(dplyr)
library(hablar)

df <- tibble(a = c(1, 2, 2, 4),
             b = c(5, 2, 2, 8))

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