【问题标题】:How does one reorder columns in a data frame?如何对数据框中的列进行重新排序?
【发布时间】:2011-08-03 00:22:01
【问题描述】:

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

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

【问题讨论】:

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

标签: r sorting dataframe r-faq


【解决方案1】:

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

要更改上述问题中的顺序,请执行df2[,c(1,3,2,4)]

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

【讨论】:

  • 当您的列数有限时这是可以的,但是如果您有例如 50 列,那么键入所有列号或名称会花费太多时间。什么是更快的解决方案?
  • @user4050:在这种情况下,您可以使用“:”语法,例如df[,c(1,3,2,4,5:50)].
  • 将idcols中的列放在开头:idcols
  • @user4050:当你不知道有多少列时,你也可以使用df[,c(1,3,2,4:ncol(df))]
  • 您也可以使用 dput(colnames(df)),它以 R 字符格式打印列名。然后您可以重新排列名称。
【解决方案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

【讨论】:

  • 作为初学者的问题,可以结合按索引和按名称排序吗?例如。 data &lt;- data[c(1,3,"Var1", 2)]?
  • @BramVanroy 不,c(1,3,"Var1", 2) 将被读取为c("1","3","Var1", "2"),因为向量只能包含一种类型的数据,因此类型被提升为最通用的类​​型。因为没有字符名称为“1”、“3”等的列,所以您将得到“未定义的列”。 list(1,3,"Var1", 2) 在没有类型提升的情况下保留值,但您不能在上述上下文中使用 list
  • 为什么mtcars[c(1,3,2)] 子集有效?我本来预计会出现与错误尺寸或类似情况有关的错误...不应该是mtcars[,c(1,3,2)]吗?
  • data.frames 是引擎盖下的列表,列作为第一顺序项
【解决方案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's select syntax的信息。

【讨论】:

  • 有一些理由不使用subset(),见this question
  • 谢谢。无论如何,我现在将使用 dplyr 包中的 select 函数,而不是子集。
  • 当你想把几列放在左边而不放弃其他列时,我发现everything() 特别棒; mtcars %&gt;% select(wt, gear, everything())
  • 这是另一种使用everything() select_helper 函数将列重新排列到右侧/末尾的方法。 stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 似乎您需要使用 2 个 select() 将一些列移到右端,将其他列移到左边。
  • 新功能 dplyr::relocate 正是为此。请参阅下面 H 1 的答案
【解决方案4】:

this comment 中所述,对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

【讨论】:

  • 非常好的功能。我将此函数的修改版本添加到我的personal package
  • 这真的很有用——当我只想将一列从一个非常宽的小标题的末尾移动到开头时,它会为我节省很多时间
【解决方案5】:

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

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

# or

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

【讨论】:

  • 对我来说最好的选择。即使我必须安装它,这显然是最明显的可能性。
  • Tidyverse(实际上是 dplyr)还有选择列组的选项,例如将 Species 变量移到前面:select(iris, Species, everything())。另请注意,不需要引号。
  • 请务必注意,这将删除所有未明确指定的列,除非您在 PaulRougieux 的评论中包含 everything()
  • dplyrgroup 也会重新排列变量,因此在链中使用时要小心。
  • dplyr 版本1.0.0 开始,他们添加了一个直观且易于阅读的relocate() 函数。如果您只想在特定列之后或之前添加列,这将特别有用。
【解决方案6】:

您想要的列顺序恰好具有按字母降序排列的列名,这可能是一个巧合。既然是这种情况,您可以这样做:

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

当我有很多列的大文件时,我会使用它。

【讨论】:

  • !! WARNING !! data.tableTARGET 转换为 int 向量:TARGET &lt;- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] 修复该问题:TARGET &lt;- as.data.frame(TARGET) TARGET &lt;- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
【解决方案7】:

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)

【讨论】:

  • 这是一个非常巧妙的解决方案。谢谢!
【解决方案8】:

您可以使用data.table 包:

How to reorder data.table columns (without copying)

require(data.table)
setcolorder(DT,myOrder)

【讨论】:

    【解决方案9】:

    threetop-ratedanswers有一个弱点。

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

    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)
    

    那么你会设置那些稍后会阅读你的代码的人,包括你自己,有点欺骗。列名被用作文字而不出现在代码中。

    【讨论】:

      【解决方案10】:
      data.table::setcolorder(table, c("Out", "in", "files"))
      

      【讨论】:

      • 请说明您从哪个库获取函数setcolorder
      【解决方案11】:

      我见过的唯一一个运行良好的是来自here

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

      像魅力一样工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-10-13
        • 2022-07-25
        • 1970-01-01
        • 2016-09-05
        • 1970-01-01
        • 2022-09-28
        • 2014-02-18
        相关资源
        最近更新 更多