Come si riordinano le colonne in un frame di dati?


311

Come si modifica questo input (con la sequenza: time, in, out, files):

Time   In    Out  Files
1      2     3    4
2      3     4    5

A questo output (con la sequenza: time, out, in, files)?

Time   Out   In  Files
1      3     2    4
2      4     3    5

Ecco i dati fittizi R:

table <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))
table
##  Time In Out Files
##1    1  2   3     4
##2    2  3   4     5

4
help(Extract)noto anche come?'['
Joris Meys l'

3
Oltre al suggerimento di @ Joris, prova a leggere le sezioni 2.7 e 5 del manuale "An Introduction to R": cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson,

3
Un ulteriore problema: tutte le risposte richiedono l'elenco completo delle colonne, altrimenti causano un sottoinsieme. E se volessimo elencare solo alcune colonne da ordinare come prime, mantenendo anche tutte le altre?
000andy8484,

Risposte:


341

Il tuo frame di dati ha quattro colonne in questo modo df[,c(1,2,3,4)]. Nota la prima virgola significa mantenere tutte le righe e 1,2,3,4 si riferisce alle colonne.

Per cambiare l'ordine come nella domanda precedente fare df2[,c(1,3,2,4)]

Se vuoi produrre questo file come un csv, fallo write.csv(df2, file="somedf.csv")


35
Questo va bene quando hai un numero limitato di colonne, ma cosa succede se hai ad esempio 50 colonne, ci vorrebbe troppo tempo per digitare tutti i numeri o nomi di colonne. Quale sarebbe una soluzione più rapida?
Herman Toothrot,

54
@ user4050: in tal caso è possibile utilizzare la sintassi ":", ad esempio df [, c (1,3,2,4,5: 50)].
dalloliogm,

1
per mettere le colonne negli idoli all'inizio: idcols <- c ("name", "id2", "start", "duration"); cols <- c (idcols, names (cts) [- che (names (cts)% in% idcols)]); df <- df [cols]
kasterma

13
@ user4050: puoi anche usarlo df[,c(1,3,2,4:ncol(df))]quando non sai quante colonne ci sono.
Arekolek,

1
Puoi anche usare dput (colnames (df)), stampa i nomi delle colonne in formato carattere R. È quindi possibile riorganizzare i nomi.
Chris,

168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]

1
Domanda come principiante, puoi combinare l'ordinamento per indice e per nome? Ad esempio data <- data[c(1,3,"Var1", 2)]?
Bram Vanroy,

6
@BramVanroy no, c(1,3,"Var1", 2)verrà letto c("1","3","Var1", "2")perché i vettori possono contenere dati di un solo tipo, quindi i tipi vengono promossi al tipo più generale presente. Poiché non ci sono colonne con i nomi dei caratteri "1", "3", ecc. Otterrai "colonne indefinite". list(1,3,"Var1", 2)mantiene i valori senza la promozione del tipo, ma non è possibile utilizzare a listnel contesto sopra.
Terry Brown,

1
Perché funziona il mtcars[c(1,3,2)]subsetting? Mi sarei aspettato un errore relativo a dimensioni errate o simili ... Non dovrebbe essere mtcars[,c(1,3,2)]?
landroni,

data.frames sono elenchi sotto il cofano con colonne come articoli del primo ordine
petermeissner

106

È inoltre possibile utilizzare la funzione del sottoinsieme:

data <- subset(data, select=c(3,2,1))

Dovresti usare meglio l'operatore [] come nelle altre risposte, ma può essere utile sapere che puoi eseguire un sottoinsieme e un'operazione di riordino delle colonne in un singolo comando.

Aggiornare:

Puoi anche usare la funzione select dal pacchetto dplyr:

data = data %>% select(Time, out, In, Files)

Non sono sicuro dell'efficienza, ma grazie alla sintassi di dplyr questa soluzione dovrebbe essere più flessibile, specialmente se hai molte colonne. Ad esempio, quanto segue riordinerà le colonne del set di dati mtcars nell'ordine opposto:

mtcars %>% select(carb:mpg)

E quanto segue riordinerà solo alcune colonne e ne eliminerà altre:

mtcars %>% select(mpg:disp, hp, wt, gear:qsec, starts_with('carb'))

Maggiori informazioni sulla sintassi selezionata di dplyr .


5
Ci sono alcuni motivi per non usare subset(), vedi questa domanda .
MERose,

2
Grazie. In ogni caso ora vorrei usare la funzione select dal pacchetto dplyr, invece che sottoinsieme.
dalloliogm

87
Quando vuoi portare un paio di colonne sul lato sinistro e non far cadere le altre, trovo everything()particolarmente fantastico; mtcars %>% select(wt, gear, everything())
guyabel,

2
Ecco un altro modo per utilizzare la funzione everything () select_helper per riorganizzare le colonne a destra / fine. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Sembra che dovrai usare 2 select () per spostare alcune colonne all'estremità destra e altre a sinistra.
Arthur Yip,

1
la nuova funzione dplyr :: relocate è esattamente per questo. vedi la risposta di H 1 qui sotto
Arthur Yip,

39

Come menzionato in questo commento , i suggerimenti standard per riordinare le colonne in a data.framesono generalmente ingombranti e soggetti a errori, soprattutto se si dispone di molte colonne.

Questa funzione consente di riorganizzare le colonne in base alla posizione: specifica un nome variabile e la posizione desiderata e non preoccuparti delle altre colonne.

##arrange df vars by position
##'vars' must be a named vector, e.g. c("var.name"=1)
arrange.vars <- function(data, vars){
    ##stop if not a data.frame (but should work for matrices as well)
    stopifnot(is.data.frame(data))

    ##sort out inputs
    data.nms <- names(data)
    var.nr <- length(data.nms)
    var.nms <- names(vars)
    var.pos <- vars
    ##sanity checks
    stopifnot( !any(duplicated(var.nms)), 
               !any(duplicated(var.pos)) )
    stopifnot( is.character(var.nms), 
               is.numeric(var.pos) )
    stopifnot( all(var.nms %in% data.nms) )
    stopifnot( all(var.pos > 0), 
               all(var.pos <= var.nr) )

    ##prepare output
    out.vec <- character(var.nr)
    out.vec[var.pos] <- var.nms
    out.vec[-var.pos] <- data.nms[ !(data.nms %in% var.nms) ]
    stopifnot( length(out.vec)==var.nr )

    ##re-arrange vars by position
    data <- data[ , out.vec]
    return(data)
}

Ora la richiesta del PO diventa semplice come questa:

table <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))
table
##  Time In Out Files
##1    1  2   3     4
##2    2  3   4     5

arrange.vars(table, c("Out"=2))
##  Time Out In Files
##1    1   3  2     4
##2    2   4  3     5

Per scambiare ulteriormente Timee Filescolonne puoi fare questo:

arrange.vars(table, c("Out"=2, "Files"=1, "Time"=4))
##  Files Out In Time
##1     4   3  2    1
##2     5   4  3    2

Funzione molto bella. Ho aggiunto una versione modificata di questa funzione al mio pacchetto personale .
Deleet,

1
Questo è davvero utile - mi farà risparmiare un sacco di tempo quando voglio solo spostare una colonna dalla fine di una tabella molto ampia all'inizio
Mrmoleje,

Wow, lo adoro.
OfTheAzureSky

37

Una dplyrsoluzione (parte del tidyverseset di pacchetti) è utilizzare select:

select(table, "Time", "Out", "In", "Files") 

# or

select(table, Time, Out, In, Files)

2
L'opzione migliore per me. Anche se ho dovuto installarlo, è chiaramente la possibilità più chiara.
Garini,

15
Tidyverse (dplyr appunto) ha anche la possibilità di selezionare gruppi di colonne, ad esempio per spostare la variabile specie al fronte: select(iris, Species, everything()). Si noti inoltre che le virgolette non sono necessarie.
Paul Rougieux,

3
È importante notare che questo everything()eliminerà tutte le colonne che non sono esplicitamente specificate a meno che non includa come nel commento di
PaulRougieux

dplyr's groupsarà anche riorganizzare le variabili, quindi attenzione quando si utilizza che in una catena.
David Tonhofer,

26

Forse è una coincidenza che l'ordine delle colonne desiderato abbia i nomi delle colonne in ordine alfabetico decrescente. Dato che è così, potresti semplicemente fare:

df<-df[,order(colnames(df),decreasing=TRUE)]

Questo è quello che uso quando ho file di grandi dimensioni con molte colonne.


!! WARNING !! data.tablesi trasforma TARGETin un vettore int: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] per risolvere il problema: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith il


12

Le tre risposte più votate hanno un punto debole.

Se il tuo frame di dati è simile a questo

df <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))

> df
  Time In Out Files
1    1  2   3     4
2    2  3   4     5

allora è una soluzione scadente da usare

> df2[,c(1,3,2,4)]

Fa il lavoro, ma hai appena introdotto una dipendenza dall'ordine delle colonne nel tuo input.

Questo stile di programmazione fragile deve essere evitato.

La denominazione esplicita delle colonne è una soluzione migliore

data[,c("Time", "Out", "In", "Files")]

Inoltre, se hai intenzione di riutilizzare il tuo codice in un'impostazione più generale, puoi semplicemente

out.column.name <- "Out"
in.column.name <- "In"
data[,c("Time", out.column.name, in.column.name, "Files")]

che è anche abbastanza bello perché isola completamente i letterali. Al contrario, se usi dplyr'sselect

data <- data %>% select(Time, out, In, Files)

allora imposteresti quelli che leggeranno il tuo codice in seguito, incluso te stesso, per un po 'di inganno. I nomi delle colonne vengono utilizzati come letterali senza apparire nel codice in quanto tale.


3

dplyrla versione 1.0.0include la relocate()funzione per riordinare facilmente le colonne:

dat <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))

library(dplyr) # from version 1.0.0 only

dat %>%
  relocate(Out, .before = In)

o

dat %>%
  relocate(Out, .after = Time)

2
data.table::setcolorder(table, c("Out", "in", "files"))

per favore indica la libreria da cui prendi la funzione setcolorder.
Triamus,

1

L'unico che ho visto funzionare bene è da qui .

 shuffle_columns <- function (invec, movecommand) {
      movecommand <- lapply(strsplit(strsplit(movecommand, ";")[[1]],
                                 ",|\\s+"), function(x) x[x != ""])
  movelist <- lapply(movecommand, function(x) {
    Where <- x[which(x %in% c("before", "after", "first",
                              "last")):length(x)]
    ToMove <- setdiff(x, Where)
    list(ToMove, Where)
  })
  myVec <- invec
  for (i in seq_along(movelist)) {
    temp <- setdiff(myVec, movelist[[i]][[1]])
    A <- movelist[[i]][[2]][1]
    if (A %in% c("before", "after")) {
      ba <- movelist[[i]][[2]][2]
      if (A == "before") {
        after <- match(ba, temp) - 1
      }
      else if (A == "after") {
        after <- match(ba, temp)
      }
    }
    else if (A == "first") {
      after <- 0
    }
    else if (A == "last") {
      after <- length(myVec)
    }
    myVec <- append(temp, values = movelist[[i]][[1]], after = after)
  }
  myVec
}

Usa così:

new_df <- iris[shuffle_columns(names(iris), "Sepal.Width before Sepal.Length")]

Funziona come un fascino.

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.