如何在数据框中重新排列列?

389

如何更改这个输入(顺序为:时间,进入,退出,文件):

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

如何得到这样的输出(时间、输出、输入、文件顺序)?

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的建议之外,还可以尝试阅读《R语言简介》手册的2.7节和5节:http://cran.r-project.org/doc/manuals/R-intro.html。 - Gavin Simpson
5
另外还有一个问题:所有答案都需要完整列出所有的列,否则会导致子集。如果我们只想将几列列为第一列,但同时保留所有其他列,该怎么办? - 000andy8484
12个回答

410

您的数据框有四列,就像这样 df[,c(1,2,3,4)]。 注意第一个逗号表示保留所有行,而1,2,3,4是指列。

如果要按照上面的问题更改顺序,请执行 df2[,c(1,3,2,4)]

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


47
如果你只有少量的列需要操作,这种方法还行,但如果你有50个列呢?那就需要花费太多时间来输入所有的列编号或名称。有没有更快的解决方案呢? - Herman Toothrot
70
在这种情况下,您可以使用“:”语法,例如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) %in% idcols)]); df <- df[cols] - kasterma
17
当你不知道有多少列时,你也可以使用df[,c(1,3,2,4:ncol(df))] - arekolek
1
你也可以使用dput(colnames(df)),它会以R字符格式打印列名。然后你可以重新排列这些名称。 - Chris
显示剩余5条评论

211
# 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
作为一个初学者,你能否将按索引和按名称排序结合起来使用呢?例如,data <- data[c(1,3,"Var1", 2)] - Bram Vanroy
9
@BramVanroy 不是的,c(1,3,"Var1", 2) 会被读作 c("1","3","Var1", "2") 因为向量只能包含相同类型的数据,所以类型会被提升为出现的最通用类型。由于没有使用字符名称“1”,“3”等的列,因此会得到“未定义的列”。list(1,3,"Var1", 2) 保留值而不进行类型提升,但在上述情况下无法使用 list - Terry Brown
2
为什么 mtcars[c(1,3,2)] 的子集取值方式有效?我本来以为会出现与维度不匹配或类似的错误...难道不应该是 mtcars[,c(1,3,2)] 吗? - landroni
1
data.frames在底层是列表,其中列是第一级项目。 - petermeissner

124

你也可以使用subset函数:

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的选择语法


6
使用 subset() 不是一个好选择,详见这个问题 - MERose
2
谢谢。无论如何,我现在会使用dplyr包中的select函数,而不是subset。 - dalloliogm
114
当你想把几列移动到左侧而不删除其他列时,我发现 everything() 特别好用;mtcars %>% select(wt, gear, everything()) - guyabel
2
这里有另一种使用everything() select_helper函数将列重新排列到右侧/末尾的方法。 https://dev59.com/xpbfa4cB1Zd3GeqP1Pda#44353144 https://github.com/tidyverse/dplyr/issues/2838 似乎您需要使用2个select()函数将某些列移动到右侧末尾,其他列则移动到左侧。 - Arthur Yip
4
新的函数dplyr::relocate就是为了这个而设计的。请参见下面H1的回答。 - Arthur Yip

50

此评论中所提到的,通常在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)
}

现在,楼主的请求变得非常简单:

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

非常好的函数。我将这个函数的修改版本添加到了我的个人包中。 - CoderGuy123
3
这非常有用-当我只想将一个列从非常宽的数据框(tibble)的末尾移动到开头时,它将为我节省很多时间。 - Mrmoleje

43

使用selectdplyr解决方案(tidyverse包集的一部分)。

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

# or

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

3
对我来说最好的选择。即使我必须安装它,这显然是最清晰的可能性。 - Garini
22
Tidyverse(实际上是dplyr)还可以选择列组,例如将Species变量移动到前面:select(iris, Species, everything())。另外请注意,不需要使用引号。 - Paul Rougieux
5
需要注意的是,如果不包括 everything(),则此操作将删除除了显式指定的所有列。这是 PaulRougieux 在评论中提到的。 - divibisan
dplyrgroup 函数也会重新排列变量,因此在使用链式操作时要小心。 - David Tonhofer
dplyr 版本 1.0.0 开始,他们添加了一个直观且易于阅读的 relocate() 函数。如果您只想在特定列之后或之前添加列,则它尤其有用。 - otteheng

40

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)

2
这是一个非常简洁的解决方案。谢谢! - Sandy
2
这可能是最灵活和简单的解决方案。谢谢! - Dominique Paul

27

也许你想要的列顺序恰好是按字母顺序降序排列的。如果是这种情况,你可以执行以下操作:

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

当我有很多列的大文件时,这就是我使用的工具。


!! WARNING !! data.table turns TARGET into an int vector: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]to fix that:TARGET <- as.data.frame(TARGET)TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] - Zachary Ryan Smith

20

19

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

如果您的数据框看起来像这样

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

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())

再次强调:在处理大型数据框时非常强大 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接