Jak zmienić kolejność kolumn w ramce danych?

311

Jak zmienić to wejście (z sekwencją: czas, wejście, wyjście, pliki):

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

Do tego wyjścia (z sekwencją: czas, wyjście, wejście, pliki)?

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

Oto fikcyjne dane 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
Katarzyna
źródło
4
help(Extract)znany również jako?'['
Joris Meys,
3
Oprócz sugestii @ Jorisa, spróbuj przeczytać sekcje 2.7 i 5 instrukcji „An Introduction to R”: cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson
3
Jeden dodatkowy problem: wszystkie odpowiedzi wymagają pełnej listy kolumn, w przeciwnym razie powodują podzbiór. Co zrobić, jeśli chcemy wymienić tylko kilka kolumn, które należy zamówić jako pierwsze, ale zachowując wszystkie pozostałe?
000andy8484

Odpowiedzi:

341

Twoja ramka danych ma cztery kolumny df[,c(1,2,3,4)]. Zauważ, że pierwszy przecinek oznacza zachowanie wszystkich wierszy, a 1,2,3,4 odnosi się do kolumn.

Aby zmienić kolejność jak w powyższym pytaniu, zrób to df2[,c(1,3,2,4)]

Jeśli chcesz wydrukować ten plik jako plik csv, zrób write.csv(df2, file="somedf.csv")

richiemorrisroe
źródło
35
Jest to w porządku, gdy masz ograniczoną liczbę kolumn, ale co jeśli na przykład masz 50 kolumn, wpisanie wszystkich numerów lub nazw kolumn zajęłoby zbyt dużo czasu. Jakie byłoby szybsze rozwiązanie?
Herman Toothrot
53
@ user4050: w takim przypadku możesz użyć składni „:”, np. df [, c (1,3,2,4,5: 50)].
dalloliogm
1
umieścić kolumny w idcols na początku: idcols <- c („name”, „id2”, „start”, „duration”); cols <- c (idcols, names (cts) [- which (names (cts)% in% idcols)]); df <- df [cols]
kasterma
13
@ user4050: możesz także użyć, df[,c(1,3,2,4:ncol(df))]gdy nie wiesz, ile jest kolumn.
arekolek
1
Możesz także użyć dput (nazwy kolumn (df)), drukuje nazwy kolumn w formacie R. Następnie możesz zmienić kolejność nazw.
Chris
168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]
Xavier Guardiola
źródło
1
Pytanie jako początkujący, czy możesz łączyć porządkowanie według indeksu i nazwy? Na przykład data <- data[c(1,3,"Var1", 2)]?
Bram Vanroy
6
@BramVanroy nie, c(1,3,"Var1", 2)zostanie odczytany jako, c("1","3","Var1", "2")ponieważ wektory mogą zawierać dane tylko jednego typu, więc typy są promowane do najbardziej ogólnego typu. Ponieważ nie ma kolumny z charakteru nazwy „1”, „3”, itd. Dostaniesz „niezdefiniowanych kolumny”. list(1,3,"Var1", 2)zachowuje wartości bez promocji typu, ale nie można użyć listw powyższym kontekście.
Terry Brown
1
Dlaczego mtcars[c(1,3,2)]podzestaw działa? Liczyłam błąd odnoszący się do niewłaściwych wymiarów lub podobnego ... nie powinno być mtcars[,c(1,3,2)]?
landroni
ramki danych to listy pod maską z kolumnami jako pozycje pierwszego zamówienia
petermeissner
106

Możesz także użyć funkcji podzestawu:

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

Lepiej użyj operatora [] jak w innych odpowiedziach, ale warto wiedzieć, że możesz wykonać operację podzbioru i zmiany kolejności kolumn w jednym poleceniu.

Aktualizacja:

Możesz także użyć funkcji wyboru z pakietu dplyr:

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

Nie jestem pewien co do wydajności, ale dzięki składni dplyr to rozwiązanie powinno być bardziej elastyczne, szczególnie jeśli masz dużo kolumn. Na przykład następujące polecenie zmieni kolejność kolumn zestawu danych mtcars w odwrotnej kolejności:

mtcars %>% select(carb:mpg)

Następujące elementy uporządkują tylko niektóre kolumny i odrzucą inne:

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

Przeczytaj więcej o wybranej składni dplyr .

dalloliogm
źródło
5
Istnieje kilka powodów, aby nie używać subset(), patrz to pytanie .
MERose
2
Dziękuję Ci. W każdym razie użyłbym teraz funkcji select z pakietu dplyr, zamiast podzbioru.
dalloliogm
87
Kiedy chcesz przenieść kilka kolumn po lewej stronie, a nie upuścić pozostałych, uważam, że jest to everything()szczególnie niesamowite; mtcars %>% select(wt, gear, everything())
guyabel
2
Oto inny sposób użycia funkcji select_helper () do zmiany rozmieszczenia kolumn w prawo / koniec. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Wygląda na to, że będziesz musiał użyć 2 select (), aby przesunąć niektóre kolumny na prawy koniec, a inne na lewo.
Arthur Yip
1
właśnie do tego służy nowa funkcja dplyr :: relocate. patrz odpowiedź H 1 poniżej
Arthur Yip
39

Jak wspomniano w tym komentarzu , standardowe sugestie dotyczące ponownego zamawiania kolumn w a data.framesą na ogół uciążliwe i podatne na błędy, szczególnie jeśli masz dużo kolumn.

Ta funkcja pozwala ponownie rozmieścić kolumny według pozycji: podaj nazwę zmiennej i żądaną pozycję i nie martw się o inne kolumny.

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

Teraz prośba PO staje się tak prosta:

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

Aby dodatkowo zamienić Timei Fileskolumny, możesz to zrobić:

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
źródło
Bardzo fajna funkcja. Dodałem zmodyfikowaną wersję tej funkcji do mojego osobistego pakietu .
Deleet,
1
Jest to bardzo przydatne - pozwoli mi zaoszczędzić dużo czasu, gdy chcę tylko przesunąć jedną kolumnę z końca naprawdę szerokiej tibble na początek
Mrmoleje
Wow, uwielbiam to.
OfTheAzureSky
37

dplyrRoztworu (część tidyversezestawu pakietu) jest użycie select:

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

# or

select(table, Time, Out, In, Files)
Ben G.
źródło
2
Najlepsza opcja dla mnie. Nawet gdybym musiał go zainstalować, jest to wyraźnie najczystsza możliwość.
Garini
15
Tidyverse (dplyr w rzeczywistości) ma również możliwość wyboru grupy kolumn, na przykład, aby przesunąć zmienny gatunek do przodu: select(iris, Species, everything()). Należy również pamiętać, że cytaty nie są potrzebne.
Paul Rougieux,
3
Ważne jest, aby pamiętać, że spowoduje to usunięcie wszystkich kolumn, które nie są wyraźnie określone, chyba że everything()podasz je w komentarzu
PaulRougieux
dplyr„s groupbędzie także zmienić kolejność zmiennych, więc należy zwrócić uwagę, że w przypadku korzystania z sieci.
David Tonhofer
26

Może to przypadek, że żądana kolejność kolumn ma nazwy kolumn w malejącej kolejności alfabetycznej. Ponieważ właśnie tak możesz zrobić:

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

Tego używam, gdy mam duże pliki z wieloma kolumnami.

użytkownik3482899
źródło
!! WARNING !! data.tablezamienia się TARGETw wektor int: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] aby to naprawić: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith
12

Te trzy najlepiej ocenione odpowiedzi mają słabość.

Jeśli twoja ramka danych wygląda tak

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

to jest kiepskie rozwiązanie do użycia

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

Wykonuje zadanie, ale właśnie wprowadziłeś zależność od kolejności kolumn w danych wejściowych.

Tego stylu łamliwego programowania należy unikać.

Wyraźne nazewnictwo kolumn jest lepszym rozwiązaniem

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

Ponadto, jeśli zamierzasz ponownie użyć kodu w bardziej ogólnym ustawieniu, możesz po prostu

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

co jest również całkiem miłe, ponieważ w pełni izoluje literały. Natomiast jeśli używasz dplyr'sselect

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

wtedy powinieneś ustawić tych, którzy będą czytać Twój kod później, łącznie z tobą, dla trochę oszustwa. Nazwy kolumn są używane jako literały, ale nie pojawiają się w kodzie jako takim.

Vrokipal
źródło
3

dplyrwersja 1.0.0zawiera relocate()funkcję łatwego zmieniania kolejności kolumn:

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)

lub

dat %>%
  relocate(Out, .after = Time)
27 ϕ 9
źródło
2
data.table::setcolorder(table, c("Out", "in", "files"))
Hossein Noorazar
źródło
proszę podać bibliotekę, z której pobierana jest funkcja setcolorder.
Triamus
1

Jedyny, który widziałem dobrze działa, jest stąd .

 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
}

Użyj w ten sposób:

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

Działa jak marzenie.

Cybernetyczny
źródło