Ordnen Sie Datenrahmenzeilen nach Vektor mit bestimmter Reihenfolge


158

Gibt es eine einfachere Möglichkeit, um sicherzustellen, dass die Zeilen eines Datenrahmens nach einem "Ziel" -Vektor geordnet sind, wie ich ihn im folgenden kurzen Beispiel implementiert habe?

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

Das scheint irgendwie etwas zu "kompliziert" zu sein, um die Arbeit zu erledigen:

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

Antworten:


232

Versuchen Sie 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

Es funktioniert so lange, wie Sie targetgenau die gleichen Elemente enthalten wie df$nameund keine doppelten Werte enthalten.

Von ?match:

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

matchFindet daher die Zeilennummern, die mit targetden Elementen übereinstimmen , und kehrt dann dfin dieser Reihenfolge zurück.


Großartig, das ist eher so und genau das, wonach ich gesucht habe! Vielen Dank
Rappster

1
Eine Frage: Was ist, wenn die Spalte, mit der ich übereinstimmen möchte, Wiederholungswerte hat? wie b,c,a,d,b,c,a,d. Ich habe es versucht, matchaber es funktioniert nicht gut.
Yulong

@ Yulong: Ich denke, Sie müssten explizit sicherstellen, dass Duplikate vor dem Brennen entfernt werden match(). Was mir in den Sinn kommt duplicated(), ist unique()eine andere benutzerdefinierte Routine, die die gewünschten Elemente "beibehält", während die anderen weggeworfen werden. HTH
Rappster

@ Edward es ist eine schöne Lösung. Es ändert jedoch auch die Indizes. Wie kann ich sie auch in aufsteigender Reihenfolge (1, 2, 3, 4) halten?
Hasan Iqbal

2
Ich bin mir nicht sicher, ob es der sauberste Weg ist, aber mit nur "Basis" -Funktionen sollte dies funktionieren, wenn Sie Duplikate in df haben: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

Ich ziehe es vor Gebrauch ***_join in dplyrwann immer ich brauche Daten übereinstimmen. Ein möglicher Versuch dafür

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

Beachten Sie, dass für die Eingabe für ***_jointbls oder data.frame erforderlich ist


Ja, die * _join-Funktionen in dplyrsind wirklich nett. Verwenden Sie diese mittlerweile auch häufig
Rappster,

In diesem Fall empfehlen Sie, die Zielreihenfolge als tibble zu deklarieren, um die Konvertierung von data.frame () in Faktoren zu vermeiden. target <- tibble(name = c("b", "c", "a", "d"))
Brennnessel

2
Und mit Pipe-Syntax:df %>% right_join(tibble(name = target), by = "name")
Frank

18

Diese Methode ist etwas anders, sie hat mir etwas mehr Flexibilität als die vorherige Antwort gegeben. Indem Sie es zu einem geordneten Faktor machen, können Sie es gut in arrangeund so verwenden. Ich habe reorder.factor aus dem gdataPaket verwendet.

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)

Verwenden Sie als Nächstes die Tatsache, dass es jetzt bestellt ist:

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

Wenn Sie zur ursprünglichen (alphabetischen) Reihenfolge zurückkehren möchten, verwenden Sie einfach, um as.character()den ursprünglichen Zustand wiederherzustellen.


2
Kennt jemand eine data.table-Version davon?
Reilstein

2
@ Reilstein setDT(df)[ , name := factor(name, levels = target)]. Dann sehen Sie die zwei data.tableAntworten hier
Henrik

4

Wir können die Faktorstufen basierend darauf anpassen targetund in verwendenarrange

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

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

Oder orderes und benutze es inslice

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

2
Die beste Lösung IMO
stevec

1
Die besten und einfachsten Lösungen für mich.
Matt_B

0

Wenn Sie keine Bibliotheken verwenden möchten und Ihre Daten erneut auftreten, können Sie sie auch whichmit verwenden sapply.

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.