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

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

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

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.


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)


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

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

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


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]]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.