Memesan baris bingkai data menurut vektor dengan urutan tertentu

158

Apakah ada cara yang lebih mudah untuk memastikan bahwa baris bingkai data dipesan sesuai dengan vektor "target" seperti yang saya terapkan dalam contoh singkat di bawah ini?

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")

Ini sepertinya agak terlalu "rumit" untuk menyelesaikan pekerjaan:

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
Rappster
sumber

Jawaban:

232

Coba 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

Ini akan berfungsi selama Anda targetmengandung elemen yang persis sama df$name, dan tidak mengandung nilai duplikat.

Dari ?match:

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

Oleh karena itu matchtemukan nomor baris yang cocok targetdengan elemen, dan kemudian kita kembali dfdalam urutan itu.

Edward
sumber
Hebat, lebih seperti itu dan persis apa yang saya cari! Terima kasih banyak
Rappster
1
satu pertanyaan, bagaimana jika kolom yang ingin saya cocokkan memiliki nilai ulangi? seperti b,c,a,d,b,c,a,d. Saya sudah mencoba matchtetapi tidak berhasil.
Yulong
@ Yulong: Saya pikir Anda harus secara eksplisit memastikan bahwa duplikat dihapus sebelum menembak match(). Apa yang terlintas dalam pikiran adalah duplicated(), unique()atau kebiasaan rutin lainnya yang "menjaga" elemen yang diinginkan sambil membuang yang lain. HTH
Rappster
@Edward itu solusi yang bagus. Namun, itu juga mengubah indeks. Bagaimana saya bisa menjaga mereka dalam urutan menaik (1, 2, 3, 4)?
Hasan Iqbal
2
tidak yakin itu adalah cara terbersih, tetapi hanya dengan fungsi "basis", ini akan berfungsi jika Anda memiliki duplikat dalam 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

Saya lebih suka menggunakan ***_join di dplyrsetiap kali saya harus mencocokkan data. Satu kemungkinan mencoba untuk ini

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

Perhatikan bahwa input untuk ***_joinmemerlukan tbls atau data.frame

Lerong
sumber
Ya, fungsi * _join di dplyrbenar-benar bagus. Akhirnya banyak menggunakan ini sekarang juga
Rappster
Dalam hal ini, rekomendasikan mendeklarasikan target pesanan sebagai sebuah tibble, untuk menghindari konversi data.frame () menjadi faktor. target <- tibble(name = c("b", "c", "a", "d"))
Nettle
2
Dan dengan sintaksis pipa:df %>% right_join(tibble(name = target), by = "name")
Frank
18

Metode ini sedikit berbeda, itu memberi saya fleksibilitas lebih dari jawaban sebelumnya. Dengan membuatnya menjadi faktor yang diurutkan, Anda dapat menggunakannya dengan baik arrangedan semacamnya. Saya menggunakan reorder.factor dari gdatapaket.

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)

Selanjutnya, gunakan fakta bahwa sekarang sudah dipesan:

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

Jika Anda ingin kembali ke pemesanan (alfabet) asli, cukup gunakan as.character()untuk mengembalikannya ke keadaan semula.

MattV
sumber
2
Apakah ada yang tahu versi data.table ini?
Reilstein
2
@ Reilstein setDT(df)[ , name := factor(name, levels = target)]. Kemudian lihat dua data.tablejawaban di sini
Henrik
4

Kami dapat menyesuaikan level faktor berdasarkan targetdan menggunakannya dalamarrange

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

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

Atau orderdan menggunakannyaslice

df %>% slice(order(factor(name, levels = target)))
Ronak Shah
sumber
2
Solusi terbaik IMO
stevec
1
Solusi terbaik dan paling sederhana bagi saya.
Matt_B
0

Jika Anda tidak ingin menggunakan perpustakaan dan Anda memiliki terulangnya dalam data Anda, Anda dapat menggunakan whichdengan sapplyjuga.

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
Eonurk
sumber