สั่งซื้อแถวข้อมูลเฟรมตามเวกเตอร์พร้อมคำสั่งเฉพาะ


158

มีวิธีที่ง่ายกว่าหรือไม่ในการตรวจสอบให้แน่ใจว่าแถวของกรอบข้อมูลได้รับการจัดเรียงตามเวกเตอร์ "เป้าหมาย" ที่ฉันดำเนินการในตัวอย่างสั้น ๆ ด้านล่างหรือไม่

df <- data.frame(name = letters[1:4], value = c(rep(TRUE, 2), rep(FALSE, 2)))

df
#   name value
# 1    a  TRUE
# 2    b  TRUE
# 3    c FALSE
# 4    d FALSE

target <- c("b", "c", "a", "d")

สิ่งนี้ดูเหมือนจะค่อนข้าง "ซับซ้อน" เพื่อให้งานเสร็จ:

idx <- sapply(target, function(x) {
    which(df$name == x)
})
df <- df[idx,]
rownames(df) <- NULL

df 
#   name value
# 1    b  TRUE
# 2    c FALSE
# 3    a  TRUE
# 4    d FALSE

คำตอบ:


232

ลองmatch:

df <- data.frame(name=letters[1:4], value=c(rep(TRUE, 2), rep(FALSE, 2)))
target <- c("b", "c", "a", "d")
df[match(target, df$name),]

  name value
2    b  TRUE
3    c FALSE
1    a  TRUE
4    d FALSE

มันจะทำงานได้ตราบใดที่คุณtargetมีองค์ประกอบเหมือนกับdf$nameและไม่ได้มีค่าซ้ำกัน

จาก?match:

match returns a vector of the positions of (first) matches of its first argument 
in its second.

ดังนั้นmatchจะค้นหาหมายเลขแถวที่ตรงกับtargetองค์ประกอบของแล้วเราจะกลับมาdfตามลำดับ


เยี่ยมมากมันเป็นแบบนั้นและเป็นสิ่งที่ฉันกำลังมองหา! ขอบคุณมาก
Rappster

1
คำถามหนึ่งจะเกิดอะไรขึ้นถ้าคอลัมน์ที่ฉันต้องการจับคู่มีค่าซ้ำกัน b,c,a,d,b,c,a,dเช่น ฉันพยายามmatchแต่มันใช้งานไม่ได้
Yulong

@Yulong: match()ฉันคิดว่าคุณจะต้องชัดเจนให้แน่ใจว่าที่ซ้ำกันจะถูกลบออกก่อนที่จะมีการยิง อะไรมาคิดเป็นduplicated(), unique()หรือกิจวัตรประจำวันอื่น ๆ ที่กำหนดเอง "ช่วยให้" องค์ประกอบที่ต้องการในขณะที่โยนไปที่คนอื่น ๆ HTH
Rappster

@ เอ็ดเวิร์ดมันเป็นทางออกที่ดี อย่างไรก็ตามมันยังเปลี่ยนดัชนี ฉันจะเก็บไว้ในลำดับที่สูงขึ้น (1, 2, 3, 4) ได้อย่างไร
Hasan Iqbal

2
ไม่แน่ใจว่ามันเป็นวิธีที่สะอาดที่สุด แต่ด้วยฟังก์ชั่น "base" เท่านั้นมันน่าจะทำงานได้ถ้าคุณมีไฟล์ซ้ำใน df:df <- data.frame(name=letters[c(1:4, 1:4)], value=c(rep(TRUE, 2), rep(FALSE, 2),rep(TRUE, 2), rep(FALSE, 2) )) target <- c("b", "c", "a", "d") df[order(unlist(sapply(df$name, function(x) which(target == x)))),]
Erica Fary

21

ฉันชอบที่จะใช้***_join ในdplyrทุกครั้งที่ฉันต้องการจับคู่ข้อมูล ลองทำสิ่งนี้ได้

left_join(data.frame(name=target),df,by="name")

โปรดทราบว่าการป้อนข้อมูลสำหรับ***_joinต้องมี tbls หรือ data.frame


ใช่ฟังก์ชั่น * _join dplyrนั้นดีมาก
ลงเอย

ในกรณีนี้แนะนำให้ประกาศคำสั่งเป้าหมายเป็นแบบ tibble เพื่อหลีกเลี่ยงการแปลง data.frame () เป็นปัจจัย target <- tibble(name = c("b", "c", "a", "d"))
ตำแย

2
และด้วยรูปแบบไปป์:df %>% right_join(tibble(name = target), by = "name")
Frank

18

วิธีนี้แตกต่างกันเล็กน้อยทำให้ฉันมีความยืดหยุ่นมากกว่าคำตอบก่อนหน้าเล็กน้อย โดยทำให้เป็นปัจจัยสั่งคุณสามารถใช้อย่างarrangeและใน ฉันใช้ reorder.factor จากgdataแพ็คเกจ

df <- data.frame(name=letters[1:4], value=c(rep(TRUE, 2), rep(FALSE, 2)))
target <- c("b", "c", "a", "d")

require(gdata)
df$name <- reorder.factor(df$name, new.order=target)

ถัดไปใช้ข้อเท็จจริงที่ได้รับคำสั่งแล้ว:

require(dplyr)
df %>%
  arrange(name)
    name value
1    b  TRUE
2    c FALSE
3    a  TRUE
4    d FALSE

หากคุณต้องการกลับไปที่การสั่งซื้อดั้งเดิม (ตัวอักษร) เพียงแค่ใช้as.character()เพื่อกลับไปที่สถานะเดิม


2
ไม่มีใครรู้รุ่น data.table นี้?
Reilstein

2
setDT(df)[ , name := factor(name, levels = target)]@Reilstein จากนั้นดูdata.tableคำตอบทั้งสองที่นี่
Henrik

4

เราสามารถปรับระดับปัจจัยตามtargetและใช้งานได้arrange

library(dplyr)
df %>% arrange(factor(name, levels = target))

#  name value
#1    b  TRUE
#2    c FALSE
#3    a  TRUE
#4    d FALSE

หรือorderมันและใช้มันในslice

df %>% slice(order(factor(name, levels = target)))

2
ทางออกที่ดีที่สุด IMO
stevec

1
ทางออกที่ดีที่สุดและง่ายที่สุดสำหรับฉัน
Matt_B

0

หากคุณไม่ต้องการที่จะใช้ห้องสมุดใด ๆ และคุณมี reoccurrences ในข้อมูลของคุณคุณสามารถใช้whichด้วยsapplyเช่นกัน

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.