在 data.frame()中移动列而不重新键入

有没有一种方法可以将一列从 data.frame 中的一个位置移动到下一个位置——而不需要键入一个全新的 data.frame ()

例如:

a <- b <- c <- d <- e <- f <- g <- 1:100
df <- data.frame(a,b,c,d,e,f,g)

现在假设我想把“ G”放在“ A”前面

我可以重新打一遍

df <- data.frame(g,a,b,c,d,e,f)

但是没有更快的方法吗? (想象一下1500多列)

111634 次浏览

这里有一个方法:

> col_idx <- grep("g", names(df))
> df <- df[, c(col_idx, (1:ncol(df))[-col_idx])]
> names(df)
[1] "g" "a" "b" "c" "d" "e" "f"

subset函数有一个很好的 select参数,它为按名称选择列的范围提供了一种方便的方法:

df <- subset(df, select=c(g,a:f))

我最近编写了这个称为 moveme的函数。它被设计用于处理向量,目的是改变列的顺序。

功能如下:

moveme <- 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
}

用法很简单,试试这些:

moveme(names(df), "g first")
moveme(names(df), "g first; a last; e before c")

当然,使用它来重新排列 data.frame中的列非常简单:

df[moveme(names(df), "g first")]

对于 data.table(按照参考移动,不复制) :

setcolorder(dt, moveme(names(dt), "g first"))

基本的选择是:

  • 第一
  • 最后
  • 之前
  • 之后

复合移动用分号分隔。

这是我的解决办法

df[c(7,1:6)]

或者也可以按列名重新排序:

df[c("g",names(df)[-7])]

使用 Dplyr包中的 select及其 everything()函数将特定列移动到 data.frame 的开始或结束。

从头开始:

library(dplyr)
df %>%
select(g, everything())

走到最后:

df %>%
select(-a, everything())

如果没有 %>%管道操作员,那么它们将分别是 select(df, g, everything())select(df, -a, everything())

这样稍微优雅一些,允许排列最左边的几列,其余的不排列在右边。

ordered_columns_leftside=c('var10','var34','var8')
df=df[c(ordered_columns_leftside, setdiff(names(df),ordered_columns_leftside))]

如果重新排序是一个移位,就像您的示例一样,您可以使用 taRifx包中的 shift函数。它作用于向量,因此将它应用于列名:

> a <- b <- c <- d <- e <- f <- g <- 1:5
> df <- data.frame(a,b,c,d,e,f,g)
> df[, taRifx::shift(seq_along(df),-1)]
g a b c d e f
1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5

事实上,shift函数也可以应用于数据帧,但不如预期的那样。你可以写一个函数:

> shift_df <- function(df, n) df[, taRifx::shift(seq_along(df),n)]
> shift_df(df, -1)
g a b c d e f
1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5
> shift_df(df, 2)
c d e f g a b
1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5

@ David 问如何将“ G”移动到任意位置,比如4,

new.pos <- 4
col_idx <- grep("g", names(df))
df      <- df[ , c((1:new.pos)[-col_idx], col_idx, c((new.pos):ncol(df))[-col_idx])]

这里有一个类似的方法,我用来移动’n’th 列到第2位置在一个巨大的数据框架的基础上的列名称。

将一列移动到第一个位置:

## Move a column with name "col_name"  to first column
colX <- grep("^col_name", colnames(df.original))
# get the column position from name


df.reordered.1 <- df.original[,c(colX,1:(colX-1), (colX+1):length(df.original))]
# get new reordered data.frame
# if the column is the last one, error "undefined columns selected" will show up. Then do the following command instead of this


df.reordered.1 <- df.original[,c(colX,1:(colX-1)]
# get new reordered data.frame, if the column is the last one

从任何地方到达目的地

## Move a column with name "col_name"  to column position "n",
## where n > 1 (in a data.frame "df.original")


colX <- grep("^col_name", colnames(df.original))
# get the column position from name


n <- 2
# give the new expected column position (change to the position you need)


df.reordered.2 <- df.original[,c(1:(n-1), colX, n:(colX-1), (colX+1):length(df.original))]
# get new reordered data.frame


## Optional; to replace the original data frame with sorted data.frame
## if the sorting looks good
df.original <- df.reordered.2
rm(df.reordered.2) # remove df

这里有一个可能有帮助的函数

  • Df : 数据框架
  • ColName : 要移动的列的名称
  • 位置 : 您希望移动的列显示的列号

moveCol <- function(df,ColName,Position=1) {
D <- dim(df)[2]
DFnames <- names(df)
if (Position>D+1 | Position<1) {
warning(paste0('Column position ',sprintf('%d',Position), ' is out of range [1-',sprintf('%d',D),']'))
return()
}
for (i in ColName) {
x <- i==DFnames
if (all(!x)) {
warning(paste0('Column \"', i, '\" not found'))
} else {
D1 <- seq(D)
D1[x] = Position - 0.5
df<- df[order(D1)]
}
}
return(df)
}

我想提出另一种通用的工作方法,类似于 rcs、 Manuel 和 Scott Kaiser 以前的答案,这种方法只在特定情况下有效:

move<-function(new.pos,nameofcolumn,dfname) {
col_idx <- grep(nameofcolumn, names(dfname))
if (length(col_idx)==0){print("invalid column name");return(dfname)} else {
if(new.pos>ncol(dfname)){print("invalid column number");return(dfname)} else {
if (new.pos==1) {
b<-dfname[ , c( col_idx, c((new.pos):ncol(dfname))[-(abs(new.pos-1-col_idx))] )]
}
else if(col_idx==1 & new.pos==ncol(dfname)){
b<-dfname[ , c((1:(new.pos-1)+1), col_idx )]
}
else if(col_idx==1){
b<-dfname[ , c((1:(new.pos-1)+1), col_idx, c((new.pos+1):ncol(dfname)) )]
}
else if(new.pos==ncol(dfname)){
b<-dfname[ , c((1:(new.pos))[-col_idx], col_idx)]
}
else if(new.pos>col_idx){
b<-dfname[ , c((1:(new.pos))[-col_idx], col_idx, c((new.pos+1):ncol(dfname)) )]
}
else{
b<-dfname[ , c((1:(new.pos-1)), col_idx, c((new.pos):ncol(dfname))[-(abs(new.pos-1-col_idx))] )]
}
return(b)
if(length(ncol(b))!=length(ncol(dfname))){print("error")}
}
}}

用法:

a <- b <- c <- d <- e <- f <- g <- 1:5
df <- data.frame(a,b,c,d,e,f,g)
move(1,"g",df)

这是一个非常老的职位,但我开发了这个代码,动态改变列的位置在一个数据框架。只需更改 n 和 Column Name (“ g”在这里)的值,并使用新的列安排获取 dataframe。

df1 = subset(df, select = c(head(names(df),n=3),"g", names(df) [! names(df) %in% c(head(names(df),n=3),"g")]))

下面是我编写的一个简单但灵活的函数,用于将列移动到数据框架中的任何位置。

move.col <- function(df, move_this, next_to_this, before = FALSE) {
if (before==FALSE)
df[,c(match(setdiff(names(df)[1:which(names(df)==next_to_this)],move_this),names(df)),
match(move_this,names(df)),
match(setdiff(names(df)[which(names(df)==next_to_this):ncol(df)],c(next_to_this,move_this)),names(df)))]
else
df[,c(match(setdiff(names(df)[1:(which(names(df)==next_to_this))],c(next_to_this,move_this)),names(df)),
match(move_this,names(df)),
match(setdiff(names(df)[(which(names(df)==next_to_this)):ncol(df)],move_this),names(df)))]
}

用法: 指定数据帧(df)、要移动的列名(move_this)和要移动其旁边的列名(next_to_this)。默认情况下,该函数将把 move_this之后移动到 next_to_this列。您可以指定 before = TRUEnext_to_this之前移动 move_this

例子:

  1. 将“ b”移到“ g”之后(即,将“ b”移到最后一列)。

move.col(df, "b", "g")

  1. 把“ c”移到“ e”后面。

move.col(df, "c", "e")

  1. 在“ a”前移动“ g”(即,使“ g”成为第一列)。

move.col(df, "g", "a", before=TRUE)

  1. 在“ b”之前移动“ d”和“ f”(即移动多个列)。

move.col(df,c("d","f"),"b", before=TRUE)

大多数解决方案似乎过于冗长或缺乏封装

push_left <- function(df, pushColNames){
df[, c(pushColNames, setdiff(names(df), pushColNames))]
}


push_left(iris, c("Species", "Sepal.Length"))

我找到了一个非常简单的方法来满足我的需要,而且不需要花费太多的时间。

列名如下: “ a”、“ b”、“ c”、“ d”、“ e”、“ f”、“ g”、“ h”、“ i”、“ j”

移动“ d”到第二个位置(在“ a”之后) :

attach(df)


df <- cbind(a, d, df[,c(2:3,5:10)])

移动“ j”到第4位(在“ c”之后) :

df <- cbind(df[,c(1:3)], j, df[,c(4:9)])

使用 dplyr包中的 relocate

mtcars %>%
# dplyr::relocate(disp) %>% ## simply make disp the first column
relocate(starts_with("c"), .after = disp)  %>% ## more complex column order shuffling
head(3)

注意,该函数是在版本1.0中添加的,请参见 https://www.tidyverse.org/blog/2020/03/dplyr-1-0-0-select-rename-relocate/

对于 data.table用户:

使用 setcolorder()

setDT(df) # convert into data.table


setcolorder(df,'g') # column g comes first if not all column names are mentioned
setcolorder(df, 7)  # same as above


df


g   a   b   c   d   e   f
1:   1   1   1   1   1   1   1
2:   2   2   2   2   2   2   2
3:   3   3   3   3   3   3   3
4:   4   4   4   4   4   4   4
5:   5   5   5   5   5   5   5
6:   6   6   6   6   6   6   6
7:   7   7   7   7   7   7   7

如果“ a”和“ b”栏应移到最右边:

setcolorder(df,3:7)
df


c   d   e   f   g   a   b
1:   1   1   1   1   1   1   1
2:   2   2   2   2   2   2   2
3:   3   3   3   3   3   3   3
4:   4   4   4   4   4   4   4
5:   5   5   5   5   5   5   5
6:   6   6   6   6   6   6   6
7:   7   7   7   7   7   7   7