Как изменить порядок столбцов в кадре данных?

311

Как изменить этот вход (с последовательностью: время, вход, выход, файлы):

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

К этому выводу (с последовательностью: время, out, in, файлы)?

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

Вот фиктивные данные 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)также известный как?'['
Joris Meys
3
В дополнение к предложению @ Joris, попробуйте прочитать разделы 2.7 и 5 руководства «Введение в R»: cran.r-project.org/doc/manuals/R-intro.html
Гэвин Симпсон,
3
Еще одна проблема: все ответы требуют полного списка столбцов, в противном случае они приводят к подмножеству. Что если мы хотим перечислить только несколько столбцов, которые должны быть упорядочены в качестве первых, но также сохранить все остальные?
000andy8484

Ответы:

341

Ваш фрейм данных имеет четыре столбца, как это df[,c(1,2,3,4)]. Обратите внимание, что первая запятая означает сохранить все строки, а 1,2,3,4 относится к столбцам.

Чтобы изменить порядок, как в предыдущем вопросе, сделайте df2[,c(1,3,2,4)]

Если вы хотите вывести этот файл в формате csv, выполните write.csv(df2, file="somedf.csv")

richiemorrisroe
источник
35
Это нормально, если у вас ограниченное количество столбцов, но что, если у вас, например, 50 столбцов, потребуется слишком много времени для ввода всех номеров или имен столбцов. Что было бы более быстрым решением?
Герман Тутрот
54
@ user4050: в этом случае вы можете использовать синтаксис ":", например, df [, c (1,3,2,4,5: 50)].
dalloliogm
1
поставить столбцы в idcols в начале: idcols <- c («name», «id2», «start», «duration»); cols <- c (idcols, names (cts) [- which (names (cts)% в% idcols)]); df <- df [cols]
кастерма
13
@ user4050: вы также можете использовать, df[,c(1,3,2,4:ncol(df))]когда вы не знаете, сколько столбцов.
ареколек
1
Вы также можете использовать dput (colnames (df)), он печатает имена столбцов в формате R. Затем вы можете изменить имена.
Крис
168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]
Ксавье Гвардиола
источник
1
Вопрос для новичка: можете ли вы объединить порядок по индексу и по имени? Например data <- data[c(1,3,"Var1", 2)]?
Брэм Ванрой
6
@BramVanroy Нет, c(1,3,"Var1", 2)будет читаться так, c("1","3","Var1", "2")потому что векторы могут содержать данные только одного типа, поэтому типы повышаются до наиболее общего существующего типа. Поскольку нет столбцов с именами символов «1», «3» и т. Д., Вы получите «неопределенные столбцы». list(1,3,"Var1", 2)сохраняет значения без продвижения типа, но вы не можете использовать listв приведенном выше контексте.
Терри Браун
1
Почему работает mtcars[c(1,3,2)]подмножество? Я бы ожидал ошибку, связанную с неправильными размерами или аналогичными ... Разве это не должно быть mtcars[,c(1,3,2)]?
Ландрони
data.frames - списки под колпаком с колонками в качестве элементов первого порядка
petermeissner
106

Вы также можете использовать функцию подмножества:

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

Вы должны лучше использовать оператор [], как и в других ответах, но может быть полезно знать, что вы можете выполнить подмножество и операцию переупорядочения столбцов в одной команде.

Обновить:

Вы также можете использовать функцию выбора из пакета dplyr:

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

Я не уверен в эффективности, но благодаря синтаксису dplyr это решение должно быть более гибким, особенно если у вас много столбцов. Например, следующее упорядочит столбцы набора данных mtcars в обратном порядке:

mtcars %>% select(carb:mpg)

И следующее будет переупорядочивать только некоторые столбцы и отбрасывать другие:

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

Узнайте больше о синтаксисе выбора dplyr .

dalloliogm
источник
5
Есть несколько причин, чтобы не использовать subset(), см. Этот вопрос .
MERose
2
Спасибо. В любом случае я бы теперь использовал функцию select из пакета dplyr вместо подмножества.
dalloliogm
87
Если вы хотите перенести пару столбцов на левую сторону и не уронить остальные, я нахожу это everything()особенно удивительным; mtcars %>% select(wt, gear, everything())
guyabel
2
Вот еще один способ использовать функцию everything () select_helper для перестановки столбцов вправо / в конец. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Похоже, вам нужно будет использовать 2 select () для перемещения некоторых столбцов вправо и других влево.
Артур Ип
1
Новая функция dplyr :: relocate как раз для этого. см. ответ H 1 ниже
Артур Ип
39

Как уже упоминалось в этом комментарии , стандартные предложения по переупорядочению столбцов в a data.frameобычно громоздки и подвержены ошибкам, особенно если у вас много столбцов.

Эта функция позволяет переставить столбцы по позициям: указать имя переменной и желаемую позицию, и не беспокоиться о других столбцах.

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

Теперь запрос OP становится таким простым:

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

Чтобы дополнительно поменять местами столбцы Timeи Filesстолбцы, вы можете сделать это:

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
источник
Очень хорошая функция. Я добавил модифицированную версию этой функции в свой личный пакет .
Deleet
1
Это действительно полезно - это сэкономит мне много времени, когда я просто захочу переместить одну колонку от конца действительно широкого фрагмента к началу
Мрмолье
Вау, я люблю это
OfTheAzureSky
37

dplyrРешение (часть tidyverseнабора пакетов) является использование select:

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

# or

select(table, Time, Out, In, Files)
Бен Г
источник
2
Лучший вариант для меня. Даже если бы мне пришлось установить его, это явно самая ясная возможность.
Гарини
15
Tidyverse (dplyr на самом деле) также имеет возможность выбора группы столбцов, например , для перемещения изменчивого вида на фронт: select(iris, Species, everything()). Также обратите внимание, что цитаты не нужны.
Поль Ружье,
3
Важно отметить, что при этом будут удалены все столбцы, которые не указаны явно, если вы не everything()включите их, как в комментарии
PaulRougieux
dplyrЭто groupтакже переставит переменные, так что будьте внимательны при использовании этого в цепочке.
Дэвид Тонхофер
26

Может быть, это совпадение, что в нужном порядке столбцов имена столбцов располагаются в алфавитном порядке по убыванию. Так как это так, вы могли бы просто сделать:

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

Это то, что я использую, когда у меня большие файлы со многими столбцами.

user3482899
источник
!! WARNING !! data.tableпревращается TARGETв вектор типа int: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] чтобы это исправить: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Захари Райан Смит
12

У трех самых популярных ответов есть слабость.

Если ваш фрейм данных выглядит так

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

тогда это плохое решение для использования

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

Это делает работу, но вы только что ввели зависимость от порядка столбцов в вашем входе.

Этого стиля хрупкого программирования следует избегать.

Явное именование столбцов - лучшее решение

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

Кроме того, если вы намереваетесь использовать свой код в более общих условиях, вы можете просто

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

что также довольно приятно, потому что полностью изолирует литералы. Напротив, если вы используете dplyrselect

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

тогда вы будете настраивать тех, кто будет читать ваш код позже, включая вас самих, для некоторого обмана. Имена столбцов используются как литералы, но не отображаются в коде как таковые.

Vrokipal
источник
3

dplyrВерсия 1.0.0включает в себя relocate()функцию легкого изменения порядка столбцов:

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)

или

dat %>%
  relocate(Out, .after = Time)
27 ϕ 9
источник
2
data.table::setcolorder(table, c("Out", "in", "files"))
Хоссейн Нооразар
источник
Просьба указать библиотеку, из которой вы берете функцию setcolorder.
Триамус
1

Единственный, кого я хорошо видел, это отсюда .

 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
}

Используйте как это:

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

Работает как шарм.

кибернетический
источник