Ordinare le righe della cornice dati in base al vettore con ordine specifico


158

Esiste un modo più semplice per garantire che le righe di un frame di dati vengano ordinate in base a un vettore "target" come quello che ho implementato nel breve esempio di seguito?

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

Questo in qualche modo sembra essere un po '"complicato" per portare a termine il lavoro:

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

Risposte:


232

Prova 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

Funzionerà fintanto che il tuo targetcontiene esattamente gli stessi elementi df$namee non contiene valori duplicati.

Da ?match:

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

Quindi matchtrova i numeri di riga che corrispondono targetagli elementi e quindi ritorniamo dfin quell'ordine.


Fantastico, è più simile ed è esattamente quello che stavo cercando! Grazie mille
Rappster,

1
una domanda, cosa succede se la colonna che vorrei abbinare ha valori ripetuti? come b,c,a,d,b,c,a,d. Ho provato matchma non funziona bene.
Yulong,

@Yulong: Penso che dovresti assicurarti esplicitamente che i duplicati vengano rimossi prima del licenziamento match(). Ciò che viene in mente è duplicated(), unique()o qualche altra routine personalizzata che "mantiene" gli elementi desiderati mentre butta via gli altri. HTH
Rappster,

@Edward è una bella soluzione. Tuttavia, cambia anche gli indici. Come posso mantenerli anche in ordine crescente (1, 2, 3, 4)?
Hasan Iqbal,

2
non sono sicuro che sia il modo più pulito, ma con solo funzioni "base", questo dovrebbe funzionare se hai duplicati in 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

Io preferisco usare ***_join in dplyrogni volta che ho bisogno di abbinare i dati. Un possibile tentativo per questo

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

Si noti che l'input per ***_joinrichiede tbls o data.frame


Sì, le funzioni * _join dplyrsono davvero carine.
Finisci per usarli

In questo caso, raccomandare di dichiarare l'ordine di destinazione come tibble, per evitare la conversione di data.frame () in fattori. target <- tibble(name = c("b", "c", "a", "d"))
Ortica,

2
E con la sintassi della pipe:df %>% right_join(tibble(name = target), by = "name")
Frank,

18

Questo metodo è un po 'diverso, mi ha fornito un po' più di flessibilità rispetto alla risposta precedente. Trasformandolo in un fattore ordinato, puoi usarlo bene arrangee così via. Ho usato reorder.factor dal gdatapacchetto.

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)

Quindi, usa il fatto che ora è ordinato:

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

Se vuoi tornare all'ordinamento (alfabetico) originale, usa semplicemente as.character()per riportarlo allo stato originale.


2
Qualcuno conosce una versione data.table di questo?
Reilstein,

2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]. Quindi vedi le due data.tablerisposte qui
Henrik,

4

Siamo in grado di regolare i livelli dei fattori in base targete utilizzarliarrange

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

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

Oppure orderusalo dentroslice

df %>% slice(order(factor(name, levels = target)))

2
La migliore soluzione IMO
stevec il

1
Le soluzioni migliori e più semplici per me.
Matt_B,

0

Se non si desidera utilizzare alcuna libreria e si verificano ricorrenze nei dati, è possibile utilizzare anche whichcon sapply.

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.