Comment trier un vecteur en fonction des valeurs d'un autre

112

J'ai un vecteur x, que je voudrais trier en fonction de l'ordre des valeurs dans le vecteur y. Les deux vecteurs ne sont pas de la même longueur.

x <- c(2, 2, 3, 4, 1, 4, 4, 3, 3)
y <- c(4, 2, 1, 3)

Le résultat attendu serait:

[1] 4 4 4 2 2 1 3 3 3
apprendre
la source

Réponses:

70

Voici un one-liner ...

y[sort(order(y)[x])]

[modifier:] Cela se décompose comme suit:

order(y)             #We want to sort by y, so order() gives us the sorting order
order(y)[x]          #looks up the sorting order for each x
sort(order(y)[x])    #sorts by that order
y[sort(order(y)[x])] #converts orders back to numbers from orders
Boursiers Ian
la source
1
C'est très succinct, mais j'ai du mal à comprendre ce qui se passe là-bas. Pourriez-vous élaborer un peu?
Matt Parker
3
C'est joli et montre une bonne compréhension des fonctions intégrées de R. +1
Godeke
6
En général, on peut vouloir faire cela même si y n'est pas une permutation de 1: length (y). Dans ce cas, cette solution ne fonctionne pas, mais la solution de gd047 ci-dessous, x [order (match (x, y))], le fait.
Rahul Savani
5
Je suis en fait perplexe quant à la raison pour laquelle cela a 40 votes positifs. Il échoue pour tant de variations simples sur xet y. x <- c(1,4,2); y <- c(1,2,4)par exemple.
thelatemail
1
@thelatemail Je suis d'accord. Arrêtez la folie et votez contre cette réponse!
Ian Fellows
184

et celui-là

x[order(match(x,y))]
George Dontas
la source
29
C'est très gentil, mieux que la réponse acceptée à mon humble avis car elle est plus générale.
fmark
2
J'irais jusqu'à dire que cela devrait être dans la base GNU-R.
panne catastrophique
Cette réponse a bien fonctionné pour moi lors de l'utilisation de vecteurs de caractères pour x et y. Ajouter une décomposition / légère élaboration comme dans la réponse acceptée serait bien
conformistes
4

Vous pouvez convertir xen un facteur ordonné:

x.factor <- factor(x, levels = y, ordered=TRUE)
sort(x)
sort(x.factor)

Évidemment, changer vos nombres en facteurs peut changer radicalement la façon dont le code en aval réagit x. Mais puisque vous ne nous avez pas donné de contexte sur ce qui se passe ensuite, j'ai pensé que je suggérerais cela comme une option.

Matt Parker
la source
1
cela devrait être la meilleure réponse car cela fonctionnerait pour les cas non entiers; ou fonctionne aussi quand il y a des valeurs xpas dans le vecteur de tri yavec un léger changement:x <- c(2, 2, 3, 4, 1, 4, 4, 3, 3, 6); y <- c(4, 2, 1, 3); as.numeric(as.character(sort(factor(x, unique(c(y, x))))))
rawr
2

Que diriez-vous?:

rep(y,table(x)[as.character(y)])

(Ian est probablement encore meilleur)

Ben Bolker
la source
2

Au cas où vous auriez besoin de commander sur "y", qu'il s'agisse de chiffres ou de caractères:

x[order(ordered(x, levels = y))]
4 4 4 2 2 1 3 3 3

Par étapes:

a <- ordered(x, levels = y) # Create ordered factor from "x" upon order in "y".
[1] 2 2 3 4 1 4 4 3 3
Levels: 4 < 2 < 1 < 3

b <- order(a) # Define "x" order that match to order in "y".
[1] 4 6 7 1 2 5 3 8 9

x[b] # Reorder "x" according to order in "y".
[1] 4 4 4 2 2 1 3 3 3
Georgie Shimanovsky
la source
1

[ Edit: Clairement Ian a la bonne approche, mais je laisserai cela pour la postérité.]

Vous pouvez le faire sans boucles en indexant sur votre vecteur y. Ajoutez une valeur numérique incrémentielle à y et fusionnez-les:

y <- data.frame(index=1:length(y), x=y)
x <- data.frame(x=x)
x <- merge(x,y)
x <- x[order(x$index),"x"]
x
[1] 4 4 4 2 2 1 3 3 3
Shane
la source
0
x <- c(2, 2, 3, 4, 1, 4, 4, 3, 3)
y <- c(4, 2, 1, 3)
for(i in y) { z <- c(z, rep(i, sum(x==i))) }

Le résultat en z: 4 4 4 2 2 1 3 3 3

Les étapes importantes:

  1. for (i in y) - Boucle sur les éléments d'intérêt.

  2. z <- c (z, ...) - Concatène chaque sous-expression tour à tour

  3. rep (i, sum (x == i)) - Répète i (l'élément actuel d'intérêt) sum (x == i) fois (le nombre de fois où nous avons trouvé i dans x).

Godeke
la source
0

Vous pouvez également l'utiliser sqldfet le faire par une joinfonction sqlcomme suit:

library(sqldf)
x <- data.frame(x = c(2, 2, 3, 4, 1, 4, 4, 3, 3))
y <- data.frame(y = c(4, 2, 1, 3))

result <- sqldf("SELECT x.x FROM y JOIN x on y.y = x.x")
ordered_x <- result[[1]]
OMG
la source