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

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.


Ś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


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.


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

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,]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.