如何对数据框中的列重新排序?

How does one reorder columns in a data frame?

提问人:Catherine 提问时间:4/11/2011 最后编辑:zx8754Catherine 更新时间:6/14/2022 访问量:832172

问:

如何更改此输入(顺序:时间、输入、输出、文件):

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

对于此输出(序列:time、out、in、files)?

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
排序 数据帧 R-FAQ

评论

4赞 Joris Meys 4/11/2011
help(Extract)也称为?'['
3赞 Gavin Simpson 4/11/2011
除了 @Joris 的建议之外,请尝试阅读“R 简介”手册的第 2.7 节和第 5 节: cran.r-project.org/doc/manuals/R-intro.html
5赞 CompSocialSciR 5/24/2016
另一个问题:所有答案都需要完整的列列表,否则会导致子集。如果我们只想列出几列作为第一列排序,但同时保留所有其他列,该怎么办?

答:

411赞 richiemorrisroe 9/21/2011 #1

您的 DataFrame 有四列,如下所示。 请注意,第一个逗号表示保留所有行,而 1、2、3、4 表示列。df[,c(1,2,3,4)]

要像上面问题一样更改顺序,请执行df2[,c(1,3,2,4)]

如果要将此文件输出为 csv,请执行write.csv(df2, file="somedf.csv")

评论

47赞 Herman Toothrot 8/30/2013
当您的列数有限时,这是可以的,但是如果您有 50 列,则键入所有列号或名称将花费太多时间。什么是更快的解决方案?
71赞 dalloliogm 2/25/2014
@user4050:在这种情况下,您可以使用“:”语法,例如df[,c(1,3,2,4,5:50)]。
1赞 kasterma 6/10/2014
将列放在 idcols 的开头: idcols <- c(“name”, “id2”, “start”, “duration”);cols <- c(idcols, names(cts)[-which(names(cts) %in% idcols)]);df <- df[cols]
17赞 arekolek 3/15/2016
@user4050:当您不知道有多少列时,您也可以使用。df[,c(1,3,2,4:ncol(df))]
1赞 Chris 7/27/2016
您还可以使用 dput(colnames(df)),它以 R 字符格式打印列名。然后,您可以重新排列名称。
211赞 Xavier Guardiola 6/22/2012 #2
# reorder by column name
data <- data[, c("A", "B", "C")] # leave the row index blank to keep all rows

#reorder by column index
data <- data[, c(1,3,2)] # leave the row index blank to keep all rows

评论

1赞 Bram Vanroy 12/21/2014
问题:作为初学者,您可以按索引和按名称组合排序吗?例如?data <- data[c(1,3,"Var1", 2)]
9赞 Terry Brown 1/19/2015
@BramVanroy,不会,将被读取为因为向量只能包含一种类型的数据,因此类型被提升为存在的最通用类型。由于没有字符名称为“1”、“3”等的列,因此将得到“未定义的列”。 保留不带类型升级的值,但不能在上面的上下文中使用 a。c(1,3,"Var1", 2)c("1","3","Var1", "2")list(1,3,"Var1", 2)list
2赞 landroni 8/30/2015
为什么子集有效?我本来以为会出现与不正确的尺寸或类似尺寸有关的错误......不应该吗?mtcars[c(1,3,2)]mtcars[,c(1,3,2)]
1赞 petermeissner 11/24/2015
data.frames 是后台的列表,列是一阶项
124赞 dalloliogm 7/3/2012 #3

您还可以使用子集函数:

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

与其他答案一样,最好使用 [] 运算符,但知道可以在单个命令中执行子集和列重新排序操作可能很有用。

更新:

您还可以使用 dplyr 软件包中的 select 函数:

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 的 select 语法的更多信息。

评论

6赞 MERose 11/17/2014
有一些原因不使用,请看这个问题subset()
2赞 dalloliogm 11/18/2014
谢谢。无论如何,我现在将使用 dplyr 包中的 select 函数,而不是 subset。
114赞 guyabel 2/19/2015
当你想把几列放在左手边而不把其他列放进去时,我觉得特别棒;everything()mtcars %>% select(wt, gear, everything())
2赞 Arthur Yip 6/5/2017
这是使用 everything() select_helper函数将列重新排列到右侧/末尾的另一种方法。stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 似乎您需要使用 2 个 select() 将某些列移动到右端,将其他列移动到左侧。
4赞 Arthur Yip 4/20/2020
新功能 dplyr::relocate 正是为此而生的。请参阅下面的 H 1 的答案
27赞 user3482899 2/26/2015 #4

也许您想要的列顺序恰好有按字母降序排列的列名,这也许是巧合。既然是这种情况,你可以做:

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

当我拥有包含许多列的大文件时,这就是我使用的。

评论

0赞 Zachary Ryan Smith 8/10/2018
!! WARNING !! data.table变成一个 int 向量: 要解决这个问题:TARGETTARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
20赞 usct01 5/20/2015 #5

您可以使用 data.table 包:

如何对 data.table 列重新排序(不复制)

require(data.table)
setcolorder(DT,myOrder)
50赞 landroni 5/4/2016 #6

本评论中所述,在 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

要另外交换和列,您可以这样做:TimeFiles

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

评论

0赞 CoderGuy123 7/6/2016
非常好的功能。我将此功能的修改版本添加到我的个人包中。
3赞 Mrmoleje 5/20/2019
这非常有用 - 当我只想将一列从非常宽的 tibs 的末尾移动到开头时,它将为我节省大量时间
1赞 Cybernetic 12/30/2016 #7

我唯一见过的效果很好的就是从这里开始的。

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

像魅力一样工作。

43赞 Ben G 6/8/2018 #8

dplyr 解决方案(软件包集的一部分)是使用 selecttidyverse

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

# or

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

评论

3赞 Garini 6/18/2018
对我来说是最好的选择。即使我必须安装它,这显然是最明显的可能性。
22赞 Paul Rougieux 8/16/2018
Tidyverse(实际上是 dplyr)还可以选择列组,例如将 Species 变量移动到前面:.另请注意,不需要引号。select(iris, Species, everything())
5赞 divibisan 3/22/2019
需要注意的是,这将删除所有未明确指定的列,除非您像 PaulRougieux 的注释中那样包含everything()
0赞 David Tonhofer 10/18/2019
dplyr还会重新排列变量,因此在链中使用它时要小心。group
0赞 otteheng 12/17/2020
从版本开始,他们添加了一个直观且易于阅读的功能。如果您只想在特定列之后或之前添加列,这将特别有用。dplyr1.0.0relocate()
19赞 Vrokipal 6/19/2018 #9

三个评分最高的答案有一个弱点。

如果数据帧如下所示

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

这也很好,因为它完全隔离了文字。相比之下,如果您使用 dplyr 的select

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

然后,您将设置那些稍后会阅读您的代码的人,包括您自己,以进行一些欺骗。列名被用作文本,而不出现在代码中。

3赞 Hossein Noorazar 3/25/2019 #10
data.table::setcolorder(table, c("Out", "in", "files"))

评论

0赞 Triamus 6/14/2019
请说明您从中获取函数的库。setcolorder
41赞 lotus 4/15/2020 #11

dplyrversion 包含轻松对列重新排序的功能:1.0.0relocate()

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)

评论

3赞 Sandy 7/5/2021
这是一个非常巧妙的解决方案。谢谢!
3赞 Dominique Paul 5/27/2022
这可能是最灵活、最简单的解决方案。谢谢!
3赞 Pau 6/14/2022 #12

Dplyr 有一个功能,允许您将特定列移动到其他列之前或之后。当您使用大数据框架时,这是一个关键工具(如果是 4 列,则如前所述使用 select 会更快)。

https://dplyr.tidyverse.org/reference/relocate.html

就您而言,它将是:

df <- df %>% relocate(Out, .after = In)

简单而优雅。它还允许您将几列一起移动,并将其移动到开头或结尾:

df <- df %>% relocate(any_of(c('ColX', 'ColY', 'ColZ')), .after = last_col())

再说一遍:当您使用大数据帧时,超级强大 :)