Sortuj wiersze ramek danych według wektora w określonej kolejności

158

Czy istnieje łatwiejszy sposób na zapewnienie, że wiersze ramki danych są uporządkowane zgodnie z wektorem „docelowym”, jak ten, który zaimplementowałem w poniższym krótkim przykładzie?

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

Wydaje się to w jakiś sposób zbyt „skomplikowane”, aby wykonać zadanie:

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
źródło

Odpowiedzi:

232

Spróbuj 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

Będzie działać, o ile Twój targetzawiera dokładnie te same elementy, codf$name i żadne z nich nie będą zduplikowane.

Od ?match:

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

Dlatego matchznajduje numery wierszy pasujące do targetelementów, a następnie zwracamy dfw tej kolejności.

Edward
źródło
Świetnie, bardziej mi się podoba i dokładnie to, czego szukałem!
Wielkie
1
jedno pytanie, co jeśli kolumna, którą chciałbym dopasować, ma powtarzające się wartości? jak b,c,a,d,b,c,a,d. Próbowałem, matchale to nie działa dobrze.
Yulong
@Yulong: Myślę, że musisz wyraźnie upewnić się, że duplikaty zostaną usunięte przed odpaleniem match(). Co przychodzi na myśl to duplicated(), unique()czy jakiś inny zwyczaj rutyna, że „trzyma” żądane elementy podczas wyrzucać innych. HTH
Rappster
@Edward to fajne rozwiązanie. Jednak zmienia też indeksy. Jak mogę również zachować je w porządku rosnącym (1, 2, 3, 4)?
Hasan Iqbal
2
nie jestem pewien, czy to najczystszy sposób, ale tylko z funkcjami „podstawowymi”, powinno to działać, jeśli masz duplikaty w 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

Wolę używać ***_join w, dplyrgdy potrzebuję dopasować dane. Jedna z możliwych prób

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

Zwróć uwagę, że dane wejściowe dla ***_joinrequire tbls lub data.frame

Lerong
źródło
Tak, funkcje * _join w dplyrsą naprawdę fajne. Kończy się przy użyciu te dużo już także
Rappster
W takim przypadku zalecamy zadeklarowanie kolejności docelowej jako tibble, aby uniknąć konwersji data.frame () na czynniki. target <- tibble(name = c("b", "c", "a", "d"))
Pokrzywa
2
I ze składnią potoku:df %>% right_join(tibble(name = target), by = "name")
Frank,
18

Ta metoda jest nieco inna, zapewniła mi nieco większą elastyczność niż poprzednia odpowiedź. Robiąc z niego uporządkowany czynnik, możesz go ładnie używać w arrangei takich. Użyłem reorder.factor z gdatapakietu.

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)

Następnie skorzystaj z faktu, że jest teraz zamówiony:

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

Jeśli chcesz wrócić do pierwotnego (alfabetycznego) uporządkowania, po prostu użyj as.character()go, aby przywrócić pierwotny stan.

MattV
źródło
2
Czy ktoś zna wersję data.table tego?
Reilstein
2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]. Następnie zobacz dwie data.tableodpowiedzi tutaj
Henrik
4

Możemy dostosować poziomy współczynników na podstawie targeti wykorzystać je warrange

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

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

Lub orderto i użyj go wslice

df %>% slice(order(factor(name, levels = target)))
Ronak Shah
źródło
2
Najlepsze rozwiązanie IMO
stevec
1
Najlepsze i najprostsze rozwiązania dla mnie.
Matt_B
0

Jeśli nie chcesz używać żadnych bibliotek i masz powtórzenia w swoich danych, możesz również użyć whichwith sapply.

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
eonurk
źródło