Reputation: 44648
Is there a method for moving a column from one position in a data.frame to the next - without typing an entirely new data.frame()
For example:
a <- b <- c <- d <- e <- f <- g <- 1:100
df <- data.frame(a,b,c,d,e,f,g)
Now let's say I wanted "g" in front of "a"
I could retype it, as
df <- data.frame(g,a,b,c,d,e,f)
But is there not a quicker way? (Imagine 1500+ columns)
Upvotes: 76
Views: 115995
Reputation: 1425
For data.table
users :
Use 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
In case when column 'a' and 'b' should be moved to rightmost:
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
Upvotes: 1
Reputation: 11192
Use relocate
from dplyr
package
mtcars %>%
# dplyr::relocate(disp) %>% ## simply make disp the first column
relocate(starts_with("c"), .after = disp) %>% ## more complex column order shuffling
head(3)
Note, that the function was added with version 1.0, see https://www.tidyverse.org/blog/2020/03/dplyr-1-0-0-select-rename-relocate/
Upvotes: 8
Reputation: 3043
I would like to contribute another universal working approach, similar to the previous answers of rcs, Manuel and Scott Kaiser, which only work in specific cases:
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")}
}
}}
Usage:
a <- b <- c <- d <- e <- f <- g <- 1:5
df <- data.frame(a,b,c,d,e,f,g)
move(1,"g",df)
Upvotes: 0
Reputation: 9
Here is one function that might help
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)
}
Upvotes: -1
Reputation: 51
I found a pretty simple way of doing this that suited my needs and doesn't take much time.
You have the following column names: "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"
Move "d" to second position (after "a"):
attach(df)
df <- cbind(a, d, df[,c(2:3,5:10)])
Move "j" to 4th position (after "c"):
df <- cbind(df[,c(1:3)], j, df[,c(4:9)])
Upvotes: 1
Reputation: 11192
Most solutions seem overly verbose or lack encapsulation. Here's another way to solve the problem
push_left <- function(df, pushColNames){
df[, c(pushColNames, setdiff(names(df), pushColNames))]
}
push_left(iris, c("Species", "Sepal.Length"))
Upvotes: 1
Reputation: 2096
Here is a simple but flexible function I wrote to move a column anywhere in a data frame.
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)))]
}
Usage:
Specify the data frame (df
), the column name you want to move (move_this
), and the column name of which you want to move beside (next_to_this
). By default, the function will move the move_this
column after the next_to_this
column. You can specify before = TRUE
to move move_this
before next_to_this
.
Examples:
move.col(df, "b", "g")
move.col(df, "c", "e")
move.col(df, "g", "a", before=TRUE)
move.col(df,c("d","f"),"b", before=TRUE)
Upvotes: 1
Reputation: 193
This is a very old post , but I developed this code which dynamically changes column position within a dataframe. Just change the value of n and Column Name ("g" here) and get dataframe with new column arrangements.
df1 = subset(df, select = c(head(names(df),n=3),"g", names(df) [! names(df) %in% c(head(names(df),n=3),"g")]))
Upvotes: 4
Reputation: 23975
The subset
function has a nice select
argument that gives a convenient way to select ranges of columns by name:
df <- subset(df, select=c(g,a:f))
Upvotes: 79
Reputation: 926
Here's a similar way I used to move 'n'th column to 2nd position in a huge data frame based on the column name.
Move a column to first position:
## 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
From anywhere to To 'n'th position
## 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
Upvotes: 1
Reputation: 317
@David asked how to move "G" to an arbitrary position, such as 4. Building on @rcs answer,
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])]
Upvotes: -2
Reputation: 84529
If the reordering is a shift, as in your example, you can use the shift
function from the taRifx
package. It acts on vectors, hence apply it to the column names:
> 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
In fact the shift
function can also be applied to a data frame, but not as expected. You can write a function:
> 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
Upvotes: 0
Reputation: 31
This is slightly more elegant and allows to arrange first few leftmost columns and leave the rest unarranged to the right.
ordered_columns_leftside=c('var10','var34','var8')
df=df[c(ordered_columns_leftside, setdiff(names(df),ordered_columns_leftside))]
Upvotes: 3
Reputation: 23014
Use select
from the dplyr package and its everything()
function to move specific columns to the start or end of a data.frame.
Move to the beginning:
library(dplyr)
df %>%
select(g, everything())
Move to the end:
df %>%
select(-a, everything())
Or without the %>%
pipe operator, those would be select(df, g, everything())
and select(df, -a, everything())
respectively.
Upvotes: 61
Reputation: 1649
Here is my solution
df[c(7,1:6)]
or you can also reorder by column name:
df[c("g",names(df)[-7])]
Upvotes: 15
Reputation: 193517
I wrote this function recently called moveme
. It's designed to work on vectors, with the intent of shuffling column orders around.
Here's the function:
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
}
Usage is simple. Try these out:
moveme(names(df), "g first")
moveme(names(df), "g first; a last; e before c")
Of course, using it to reorder the columns in your data.frame
is straightforward:
df[moveme(names(df), "g first")]
And for data.table
s (moves by reference, no copy) :
setcolorder(dt, moveme(names(dt), "g first"))
The basic options are:
Compounded moves are separated by a semicolon.
Upvotes: 65
Reputation: 68839
Here is one way to do it:
> 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"
Upvotes: 61