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


311

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

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,2011年

3
除了@Joris的建议之外,请尝试阅读“ R简介”手册的2.7节和第5节:cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson

3
另一个问题:所有答案都需要完整的列列表,否则将导致子集。如果我们只想列出要排序的几列作为第一列,而又保留所有其他列怎么办?
000andy8484

Answers:


341

您的数据框具有四列,如下所示df[,c(1,2,3,4)]。请注意,第一个逗号表示保留所有行,而1,2,3,4则指向列。

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

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


35
当列数有限时可以,但是例如,如果有50列,键入所有列号或名称将花费太多时间。什么是更快的解决方案?
13年

53
@ user4050:在这种情况下,您可以使用“:”语法,例如df [,c(1,3,2,4,5,50:50)]。
dalloliogm 2014年

1
将列以idcols开头:idcols <-c(“ name”,“ id2”,“ start”,“ duration”); cols <-c(idcols,names(cts)[-which(names(cts)%in%idcols)])); df <
-df

13
@ user4050:df[,c(1,3,2,4:ncol(df))]如果您不知道有多少列,也可以使用。
arekolek '16

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)]
Bram Vanroy 2014年

6
@BramVanroy nope,c(1,3,"Var1", 2)将被读为,c("1","3","Var1", "2")因为向量只能包含一种类型的数据,因此类型被提升为当前最通用的类​​型。因为没有字符名称为“ 1”,“ 3”等的列,所以您将获得“未定义的列”。 list(1,3,"Var1", 2)保留值而不进行类型提升,但是您不能list在上述情况下使用a 。
特里·布朗

1
为什么mtcars[c(1,3,2)]子集起作用?我本应预料到与尺寸错误或类似错误有关的错误,不是mtcars[,c(1,3,2)]吗?
landroni 2015年

data.frames是列表中的列表,列为第一批商品
petermeissner,2015年

106

您还可以使用子集功能:

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


5
有一些不使用的原因subset(),请参阅此问题
MERose

2
谢谢。无论如何,我现在将使用dplyr包中的select函数,而不是子集。
dalloliogm

87
当您想在左侧增加几列而不要将其他列放下时,我觉得everything()特别棒; mtcars %>% select(wt, gear, everything())
guyabel 2015年

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

要另外交换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

非常好的功能。我在个人包装中添加了此功能的修改版本。
Deleet '16

1
这真的很有用-当我只想将一列从一个很宽的
小标题的末尾移到

哇,我喜欢这个
OfTheAzureSky

37

一个dplyr解决方案(tidyverse软件包组合的一部分)是使用select

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

# or

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

2
对我来说最好的选择。即使必须安装它,也显然是最明显的可能性。
加里尼

15
Tidyverse(实际上是dplyr)还具有选择列组的选项,例如,将Species变量移到最前面:select(iris, Species, everything())。另请注意,不需要引号。
Paul Rougieux

3
重要的是要注意,这将删除所有未明确指定的列,除非您everything()在PaulRougieux的评论中加入
divibisan

dplyrgroup也将重新安排的变量,使用在一个链时,因此要小心。
David Tonhofer

26

您想要的列顺序恰好具有按字母降序排列的列名称,这可能是一个巧合。由于是这种情况,您可以执行以下操作:

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

当我的大型文件有很多列时,这就是我使用的方式。


!! WARNING !! data.table变成TARGET一个int向量:TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] 修复以下问题: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith,


12

3 收视率最高的 答案有一个弱点。

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

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)

那么您将设置那些稍后将阅读您的代码(包括您自己)的人,以免造成欺骗。列名被用作文字,而没有出现在代码中。


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)


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

奇迹般有效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.