Wie ordnet man Spalten in einem Datenrahmen neu an?

311

Wie würde man diese Eingabe ändern (mit der Reihenfolge: time, in, out, files):

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

Zu dieser Ausgabe (mit der Reihenfolge: time, out, in, files)?

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

Hier sind die Dummy-R-Daten:

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
Catherine
quelle
4
help(Extract)auch bekannt als?'['
Joris Meys
3
Lesen Sie zusätzlich zu den Vorschlägen von @ Joris die Abschnitte 2.7 und 5 des Handbuchs "Eine Einführung in R": cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson
3
Ein weiteres Problem: Alle Antworten erfordern die vollständige Liste der Spalten, andernfalls führen sie zu einer Teilmenge. Was ist, wenn wir nur einige Spalten auflisten möchten, die als erste bestellt werden sollen, aber auch alle anderen beibehalten möchten?
000andy8484

Antworten:

341

Ihr Datenrahmen hat vier Spalten df[,c(1,2,3,4)]. Beachten Sie, dass das erste Komma bedeutet, dass alle Zeilen beibehalten werden und das 1,2,3,4 sich auf die Spalten bezieht.

Um die Reihenfolge wie in der obigen Frage zu ändern, tun Sie df2[,c(1,3,2,4)]

Wenn Sie diese Datei als CSV ausgeben möchten, tun Sie dies write.csv(df2, file="somedf.csv")

richiemorrisroe
quelle
35
Dies ist in Ordnung, wenn Sie eine begrenzte Anzahl von Spalten haben. Wenn Sie jedoch beispielsweise 50 Spalten haben, würde es zu lange dauern, alle Spaltennummern oder -namen einzugeben. Was wäre eine schnellere Lösung?
Herman Toothrot
54
@ user4050: In diesem Fall können Sie die Syntax ":" verwenden, z. B. df [, c (1,3,2,4,5: 50)].
Dallasoli
1
um die Spalten am Anfang in idcols zu setzen: idcols <- c ("name", "id2", "start", "duration"); cols <- c (idcols, names (cts) [- which (names (cts)% in% idcols)]); df <- df [cols]
kasterma
13
@ user4050: Sie können auch verwenden, df[,c(1,3,2,4:ncol(df))]wenn Sie nicht wissen, wie viele Spalten vorhanden sind.
Arekolek
1
Sie können auch dput (Spaltennamen (df)) verwenden. Es werden Spaltennamen im R-Zeichenformat gedruckt. Sie können dann die Namen neu anordnen.
Chris
168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]
Xavier Guardiola
quelle
1
Frage als Anfänger, können Sie die Reihenfolge nach Index und Name kombinieren? ZB data <- data[c(1,3,"Var1", 2)]?
Bram Vanroy
6
@BramVanroy Nein, c(1,3,"Var1", 2)wird so gelesen, als ob c("1","3","Var1", "2")Vektoren nur Daten eines Typs enthalten können, sodass Typen zum allgemeinsten vorhandenen Typ heraufgestuft werden . Da es keine Spalten mit den sind Zeichennamen „1“, „3“ usw. werden Sie „undefined Spalten“ erhalten. list(1,3,"Var1", 2)behält Werte ohne Typwerbung bei, aber Sie können listim obigen Kontext keine verwenden .
Terry Brown
1
Warum funktioniert die mtcars[c(1,3,2)]Teilmenge? Ich hätte einen Fehler in Bezug auf falsche Abmessungen oder ähnliches erwartet ... Sollte es nicht sein mtcars[,c(1,3,2)]?
Landroni
data.frames sind Listen unter der Haube mit Spalten als Artikel erster Ordnung
Petermeissner
106

Sie können auch die Teilmengenfunktion verwenden:

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

Verwenden Sie den Operator [] besser wie in den anderen Antworten. Es kann jedoch hilfreich sein zu wissen, dass Sie eine Teilmenge und eine Spaltenumordnungsoperation in einem einzigen Befehl ausführen können.

Aktualisieren:

Sie können auch die Auswahlfunktion aus dem dplyr-Paket verwenden:

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

Ich bin mir über die Effizienz nicht sicher, aber dank der Syntax von dplyr sollte diese Lösung flexibler sein, insbesondere wenn Sie viele Spalten haben. Im Folgenden werden beispielsweise die Spalten des mtcars-Datasets in umgekehrter Reihenfolge neu angeordnet:

mtcars %>% select(carb:mpg)

Im Folgenden werden nur einige Spalten neu angeordnet und andere verworfen:

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

Lesen Sie mehr über die Auswahlsyntax von dplyr .

dalloliogm
quelle
5
Es gibt einige Gründe, nicht zu verwenden subset(), siehe diese Frage .
MERose
2
Vielen Dank. In jedem Fall würde ich jetzt die Auswahlfunktion aus dem dplyr-Paket anstelle der Teilmenge verwenden.
Dalloliogm
87
Wenn Sie ein paar Spalten auf die linke Seite bringen und die anderen nicht fallen lassen möchten, finde ich das everything()besonders großartig. mtcars %>% select(wt, gear, everything())
Guyabel
2
Hier ist eine andere Möglichkeit, mit der Funktion every () select_helper die Spalten nach rechts / Ende neu anzuordnen. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Es scheint, als müssten Sie 2 select () verwenden, um einige Spalten nach rechts und andere nach links zu verschieben.
Arthur Yip
1
Die neue Funktion dplyr :: relocate ist genau dafür. siehe die Antwort von H 1 unten
Arthur Yip
39

Wie in diesem Kommentar erwähnt , sind die Standardvorschläge für die Neuordnung von Spalten in adata.frame im Allgemeinen umständlich und fehleranfällig, insbesondere wenn Sie viele Spalten haben.

Mit dieser Funktion können Sie Spalten nach Position neu anordnen: Geben Sie einen Variablennamen und die gewünschte Position an und sorgen Sie sich nicht um die anderen Spalten.

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

Jetzt wird die Anfrage des OP so einfach:

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

Um zusätzlich Timeund FilesSpalten zu tauschen, können Sie dies tun:

arrange.vars(table, c("Out"=2, "Files"=1, "Time"=4))
##  Files Out In Time
##1     4   3  2    1
##2     5   4  3    2
Landroni
quelle
Sehr schöne Funktion. Ich habe meinem persönlichen Paket eine modifizierte Version dieser Funktion hinzugefügt .
Deleet
1
Das ist wirklich nützlich - es wird mir viel Zeit sparen, wenn ich nur eine Spalte vom Ende eines wirklich breiten Tibbles zum Anfang verschieben
möchte
Wow, ich liebe das.
OfTheAzureSky
37

Eine dplyrLösung (Teil des tidyversePaketsets) besteht darin, Folgendes zu verwenden select:

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

# or

select(table, Time, Out, In, Files)
Ben G.
quelle
2
Die beste Option für mich. Selbst wenn ich es installieren musste, ist es eindeutig die klarste Möglichkeit.
Garini
15
Tidyverse (dplyr in der Tat) hat auch die Möglichkeit, Gruppen von Spalten auszuwählen, um beispielsweise die Variable Species nach vorne zu verschieben : select(iris, Species, everything()). Beachten Sie auch, dass keine Anführungszeichen erforderlich sind.
Paul Rougieux
3
Es ist wichtig zu beachten, dass dadurch alle Spalten everything()gelöscht werden, die nicht explizit angegeben sind, es sei denn, Sie fügen wie in PaulRougieux 'Kommentar
divibisan
dplyr's groupordnet auch die Variablen neu an. Achten Sie also darauf, wenn Sie diese in einer Kette verwenden.
David Tonhofer
26

Vielleicht ist es ein Zufall, dass die gewünschte Spaltenreihenfolge Spaltennamen in absteigender alphabetischer Reihenfolge enthält. Da dies der Fall ist, können Sie einfach Folgendes tun:

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

Das verwende ich, wenn ich große Dateien mit vielen Spalten habe.

user3482899
quelle
!! WARNING !! data.tableverwandelt sich TARGETin einen int-Vektor: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] um das zu beheben: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith
12

Die drei am besten bewerteten Antworten haben eine Schwäche.

Wenn Ihr Datenrahmen so aussieht

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

dann ist es eine schlechte Lösung zu verwenden

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

Es macht den Job, aber Sie haben gerade eine Abhängigkeit von der Reihenfolge der Spalten in Ihrer Eingabe eingeführt.

Diese Art der spröden Programmierung ist zu vermeiden.

Die explizite Benennung der Spalten ist eine bessere Lösung

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

Wenn Sie beabsichtigen, Ihren Code in einer allgemeineren Einstellung wiederzuverwenden, können Sie dies einfach tun

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

Das ist auch sehr schön, weil es Literale vollständig isoliert. Im Gegensatz dazu, wenn Sie dplyr's verwendenselect

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

Dann würden Sie diejenigen einrichten, die Ihren Code später lesen, auch Sie selbst, für eine kleine Täuschung. Die Spaltennamen werden als Literale verwendet, ohne im Code als solcher zu erscheinen.

Vrokipal
quelle
3

dplyrVersion 1.0.0enthält die relocate()Funktion zum einfachen Neuordnen von Spalten:

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)

oder

dat %>%
  relocate(Out, .after = Time)
27 ϕ 9
quelle
2
data.table::setcolorder(table, c("Out", "in", "files"))
Hossein Noorazar
quelle
Bitte geben Sie die Bibliothek an, aus der Sie die Funktion übernehmen setcolorder.
Triamus
1

Das einzige, was ich gut gesehen habe, ist von hier .

 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
}

Verwenden Sie wie folgt:

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

Klappt wunderbar.

Kybernetisch
quelle