Sắp xếp các hàng khung dữ liệu theo vector với thứ tự cụ thể


158

Có cách nào dễ dàng hơn để đảm bảo rằng các hàng của khung dữ liệu được sắp xếp theo vectơ "đích" như cách tôi thực hiện trong ví dụ ngắn dưới đây không?

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

Điều này bằng cách nào đó có vẻ hơi "phức tạp" để hoàn thành công việc:

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

Câu trả lời:


232

Hãy thử 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

Nó sẽ hoạt động miễn là bạn targetchứa chính xác các yếu tố giống như df$namevà không chứa các giá trị trùng lặp.

Từ ?match:

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

Do đó, matchtìm các số hàng khớp với targetcác phần tử, và sau đó chúng tôi trả về dftheo thứ tự đó.


Tuyệt vời, nó giống như nó hơn và chính xác những gì tôi đang tìm kiếm! Cảm ơn rất nhiều
Rappster

1
một câu hỏi, nếu cột mà tôi muốn khớp có giá trị lặp lại thì sao? như b,c,a,d,b,c,a,d. Tôi đã thử matchnhưng nó không hoạt động tốt.
Yulong

@Yulong: Tôi nghĩ rằng bạn phải chắc chắn đảm bảo rằng các bản sao được xóa trước khi bắn match(). Điều gì đến với tâm trí là duplicated(), unique()hoặc một số thói quen tùy chỉnh khác "giữ" các yếu tố mong muốn trong khi vứt bỏ những thứ khác. HTH
Rappster 17/11/13

@Edward đó là một giải pháp tốt đẹp. Tuy nhiên, nó cũng thay đổi các chỉ số. Làm thế nào tôi cũng có thể giữ chúng theo thứ tự tăng dần (1, 2, 3, 4)?
Hasan Iqbal

2
không chắc chắn đó là cách sạch nhất, nhưng chỉ với các chức năng "cơ sở", điều này sẽ hoạt động nếu bạn có các bản sao trong 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 14/2/19

21

Tôi thích sử dụng ***_join trong dplyrbất cứ khi nào tôi cần phải phù hợp với dữ liệu. Một thử có thể cho việc này

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

Lưu ý rằng đầu vào cho ***_joinyêu cầu tbls hoặc data.frame


Vâng, các chức năng * _join trong dplyrthực sự tốt đẹp. Cuối cùng cũng sử dụng những thứ này rất nhiều
Rappster

Trong trường hợp này, khuyên bạn nên khai báo thứ tự mục tiêu dưới dạng một tibble, để tránh chuyển đổi data.frame () thành các yếu tố. target <- tibble(name = c("b", "c", "a", "d"))
Cây tầm ma

2
Và với cú pháp ống:df %>% right_join(tibble(name = target), by = "name")
Frank

18

Phương pháp này hơi khác một chút, nó cung cấp cho tôi sự linh hoạt hơn một chút so với câu trả lời trước đó. Bằng cách biến nó thành một yếu tố có trật tự, bạn có thể sử dụng nó một cách độc đáo arrangevà như vậy. Tôi đã sử dụng reorder.factor từ gdatagói.

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)

Tiếp theo, sử dụng thực tế là nó đã được đặt hàng:

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

Nếu bạn muốn quay lại thứ tự ban đầu (bảng chữ cái), chỉ cần sử dụng as.character()để đưa nó trở lại trạng thái ban đầu.


2
Có ai biết một phiên bản data.table của điều này?
Reilstein

2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]. Sau đó, xem hai data.tablecâu trả lời ở đây
Henrik

4

Chúng tôi có thể điều chỉnh các mức yếu tố dựa trên targetvà sử dụng nó trongarrange

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

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

Hoặc ordernó và sử dụng nó trongslice

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

2
Giải pháp tốt nhất IMO
stevec

1
Các giải pháp tốt nhất và đơn giản nhất cho tôi.
Matt_B

0

Nếu bạn không muốn sử dụng bất kỳ thư viện và bạn có reoccurrences trong dữ liệu của bạn, bạn có thể sử dụng whichvới sapplylà tốt.

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.