Como faço para classificar um vetor com base nos valores de outro

112

Tenho um vetor x, que gostaria de classificar com base na ordem dos valores no vetor y. Os dois vetores não têm o mesmo comprimento.

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

O resultado esperado seria:

[1] 4 4 4 2 2 1 3 3 3
aprender
fonte

Respostas:

70

Aqui está um forro ...

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

[editar:] Isso se divide da seguinte forma:

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
Ian Fellows
fonte
1
Isso é muito sucinto, mas estou tendo dificuldade em descobrir o que está acontecendo lá. Você poderia elaborar um pouco?
Matt Parker
3
Isso é bonito e mostra um bom entendimento dos recursos internos do R. +1
Godeke,
6
Em geral, pode-se querer fazer isso mesmo que y não seja uma permutação de 1: comprimento (y). Nesse caso, esta solução não funciona, mas a solução de gd047 abaixo, x [order (match (x, y))], sim.
Rahul Savani
5
Na verdade, estou perplexo quanto ao motivo disso ter 40 votos positivos. Ele falha para tantas variações simples de xe y. x <- c(1,4,2); y <- c(1,2,4)por exemplo.
thelatemail
1
@thelatemail eu concordo. Pare com a insanidade e vote contra esta resposta!
Ian Fellows de
184

que tal este

x[order(match(x,y))]
George Dontas
fonte
29
Isso é muito bom, melhor do que a resposta aceita IMHO, pois é mais geral.
fmark
2
Eu iria mais longe a ponto de dizer que isso deveria estar no GNU-R básico.
falha catastrófica de
Essa resposta funcionou bem para mim ao usar vetores de caracteres para x e y. Adicionar uma decomposição / leve elaboração como na resposta aceita seria bom
mavericks
4

Você pode converter xem um fator ordenado:

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

Obviamente, transformar seus números em fatores pode mudar radicalmente a forma como o código posterior reage x. Mas, como você não nos forneceu nenhum contexto sobre o que acontece a seguir, pensei em sugerir isso como uma opção.

Matt Parker
fonte
1
esta deve ser a melhor resposta, pois funcionaria para casos não inteiros; ou também funciona quando há valores xnão no vetor de classificação ycom ligeira alteração: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

E se?:

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

(O de Ian provavelmente ainda é melhor)

Ben Bolker
fonte
2

No caso de você precisar obter o pedido em "y", não importa se são números ou caracteres:

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

Por etapas:

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
fonte
1

[ Editar: claramente Ian tem a abordagem certa, mas vou deixar isso para a posteridade.]

Você pode fazer isso sem loops, indexando em seu vetor y. Adicione um valor numérico crescente a y e mescle-os:

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

O resultado em z: 4 4 4 2 2 1 3 3 3

As etapas importantes:

  1. for (i in y) - Loops sobre os elementos de interesse.

  2. z <- c (z, ...) - Concatena cada subexpressão por vez

  3. rep (i, sum (x == i)) - Repete i (o elemento atual de interesse) sum (x == i) vezes (o número de vezes que encontramos i em x).

Godeke
fonte
0

Além disso, você pode usar sqldfe fazer isso por uma joinfunção sqlcomo a seguinte:

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]]
AMD
fonte