Solicitar linhas do quadro de dados de acordo com o vetor com pedido específico

158

Existe uma maneira mais fácil de garantir que as linhas de um quadro de dados sejam ordenadas de acordo com um vetor "alvo" como o que eu implementei no pequeno exemplo abaixo?

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

De alguma forma, isso parece um pouco "complicado" demais para fazer o trabalho:

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
fonte

Respostas:

232

Tente 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

Funcionará desde que você targetcontenha exatamente os mesmos elementos que df$namee nem contenha valores duplicados.

De ?match:

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

Portanto, matchencontra os números de linha que correspondem targetaos elementos de e, em seguida, retornamos dfnessa ordem.

Edward
fonte
Ótimo, é mais assim e exatamente o que eu estava procurando! Muito obrigado
Rappster
1
uma pergunta, e se a coluna que eu gostaria de corresponder tiver valores repetidos? gosto b,c,a,d,b,c,a,d. Eu tentei, matchmas não funciona bem.
Yulong
@Yulong: Eu acho que você precisaria garantir explicitamente que as duplicatas são removidas antes do disparo match(). O que vem à mente é duplicated(), unique()ou alguma outra rotina personalizada que "mantém" os elementos desejados enquanto joga fora os outros. HTH
Rappster 17/11/2013
@ Edward é uma boa solução. No entanto, também altera os índices. Como também posso mantê-los em ordem crescente (1, 2, 3, 4)?
amigos estão dizendo sobre Hasan Iqbal
2
não tenho certeza que é a forma mais limpa, mas apenas com funções de "base", isso deve funcionar se você tiver duplicatas no 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

Eu prefiro usar ***_join em dplyrsempre que eu preciso para combinar dados. Uma tentativa possível para isso

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

Observe que a entrada para ***_joinrequer tbls ou data.frame

Lerong
fonte
Sim, as funções * _join dplyrsão muito legais. Acabam usando estes muito por agora, bem
Rappster
Nesse caso, recomende declarar a ordem de destino como uma tagarelice, para evitar a conversão de data.frame () em fatores. target <- tibble(name = c("b", "c", "a", "d"))
Nettle
2
E com sintaxe de canal:df %>% right_join(tibble(name = target), by = "name")
Frank
18

Esse método é um pouco diferente, me proporcionou um pouco mais de flexibilidade do que a resposta anterior. Ao transformá-lo em um fator ordenado, você pode usá-lo perfeitamentearrange . Eu usei reorder.factor do gdatapacote.

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)

Em seguida, use o fato de que agora está ordenado:

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

Se você quiser voltar à ordem original (alfabética), use-a as.character()para voltar ao estado original.

MattV
fonte
2
Alguém conhece uma versão data.table disso?
Reilstein
2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]. Então veja as duas data.tablerespostas aqui
Henrik
4

Podemos ajustar os níveis dos fatores com base targete usá-los emarrange

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

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

Ou orderuse-o emslice

df %>% slice(order(factor(name, levels = target)))
Ronak Shah
fonte
2
A melhor solução IMO
stevec
1
As melhores e mais simples soluções para mim.
Matt_B 18/06
0

Se você não quiser usar qualquer bibliotecas e você tem reoccurrences em seus dados, você pode usar whichcom o sapplybem.

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