Ordonner les lignes de trame de données en fonction du vecteur avec un ordre spécifique

158

Existe-t-il un moyen plus simple de s'assurer que les lignes d'un bloc de données sont ordonnées selon un vecteur «cible» comme celui que j'ai implémenté dans le court exemple ci-dessous?

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

Cela semble en quelque sorte un peu trop "compliqué" pour faire le travail:

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
la source

Réponses:

232

Essayez 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

Cela fonctionnera tant que votre targetcontient exactement les mêmes éléments que df$name, et qu'aucun des deux ne contient de valeurs en double.

De ?match:

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

Par conséquent, matchtrouve les numéros de ligne qui correspondent targetaux éléments de, puis nous retournons dfdans cet ordre.

Edward
la source
Génial, c'est plus comme ça et exactement ce que je cherchais! Merci beaucoup
Rappster
1
une question, que se passe-t-il si la colonne que je souhaite faire correspondre a des valeurs de répétition? comme b,c,a,d,b,c,a,d. J'ai essayé matchmais ça ne marche pas bien.
Yulong
@Yulong: Je pense que vous devriez explicitement vous assurer que les doublons sont supprimés avant le tir match(). Ce qui me vient à l'esprit duplicated(), c'est unique()une autre routine personnalisée qui "garde" les éléments désirés tout en jetant les autres. HTH
Rappster
@Edward c'est une bonne solution. Cependant, cela modifie également les indices. Comment puis-je également les conserver dans l'ordre croissant (1, 2, 3, 4)?
Hasan Iqbal
2
pas sûr que ce soit le moyen le plus propre, mais avec uniquement des fonctions "de base", cela devrait fonctionner si vous avez des doublons dans 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

Je préfère utiliser ***_join dans dplyrchaque fois que je dois faire correspondre les données. Un essai possible pour cela

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

Notez que l'entrée pour ***_joinrequire tbls ou data.frame

Lerong
la source
Ouais, les fonctions * _join dans dplyrsont vraiment sympas. Finissez par les utiliser beaucoup maintenant aussi
Rappster
Dans ce cas, il est recommandé de déclarer l'ordre cible sous forme de tibble, pour éviter la conversion de data.frame () en facteurs. target <- tibble(name = c("b", "c", "a", "d"))
Nettle
2
Et avec la syntaxe du tuyau:df %>% right_join(tibble(name = target), by = "name")
Frank
18

Cette méthode est un peu différente, elle m'a fourni un peu plus de flexibilité que la réponse précédente. En le transformant en un facteur ordonné, vous pouvez l'utiliser à bon escient arrangeet ainsi de suite. J'ai utilisé reorder.factor du gdatapackage.

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)

Ensuite, utilisez le fait qu'il est maintenant commandé:

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

Si vous souhaitez revenir à l'ordre d'origine (alphabétique), utilisez simplement as.character()pour le remettre à l'état d'origine.

MattV
la source
2
Quelqu'un en connaît-il une version data.table?
Reilstein
2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]. Alors voyez les deux data.tableréponses ici
Henrik
4

Nous pouvons ajuster les niveaux de facteur en fonction targetet l'utiliser dansarrange

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

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

Ou orderet utilisez-le dansslice

df %>% slice(order(factor(name, levels = target)))
Ronak Shah
la source
2
La meilleure solution IMO
stevec
1
Les meilleures et les plus simples solutions pour moi.
Matt_B
0

Si vous ne souhaitez utiliser aucune bibliothèque et que vos données se reproduisent, vous pouvez également utiliser whichavec sapply.

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