Add column inside named matrix

Viewed 82

Suppose I have the following matrix:

m <- matrix(1:12, nrow = 3, dimnames = list(c("a", "b", "c"), c("w", "x", "y", "z")))
#   w x y  z
# a 1 4 7 10
# b 2 5 8 11
# c 3 6 9 12

How can I add a column with the values c(13, 14, 15) between column x and y without knowing where x and y are?

Using number ranges I know how to do this using cbind.

cbind(m[,1:2], c(13, 14, 15), m[,3:4])
#   w x    y  z
# a 1 4 13 7 10
# b 2 5 14 8 11
# c 3 6 15 9 12

For named columns, it'd be neat if I could supply the column ranges with m[,:"x"] and m[,"y":] of some sort, but unfortunately that doesn't work.

Additionally, if possible, giving that column its own header name during the insertion process would be nice.

EDIT: I should have specified that x and y always are in order, so adding the column after x would have been enough. Thanks for the more general answers as well!

5 Answers

When you can not assume that x comes before y and there is no need that they are following each without a gap you can try:

i <- seq_len(min(match(c("x", "y"), colnames(m))))
cbind(m[,i], v=c(13, 14, 15), m[,-i])
#  w x  v y  z
#a 1 4 13 7 10
#b 2 5 14 8 11
#c 3 6 15 9 12

In case they are ordered, that it will be enough to put it after x like:

i <- seq_len(match("x", colnames(m)))
cbind(m[,i], v=c(13, 14, 15), m[,-i])

It's not exactly pretty but you can do this

cbind(m[,1:(which(dimnames(m)[[2]]=="x"))], 
      t=c(13, 14, 15),
      m[,(which(dimnames(m)[[2]]=="y")):dim(m)[2]])

You can use which to find the desired column and assign a name in cbind, i.e.

cbind(m[, seq(which(colnames(m) == 'x'))], 
      w = c(13, 14, 15), 
      m[, (which(colnames(m) == 'y'):ncol(m))])

#  w x  w y  z
#a 1 4 13 7 10
#b 2 5 14 8 11
#c 3 6 15 9 12

you may found the columns positions by names and insert the new column properly:

x_pos <- which(colnames(m) == "x")
y_pos <- which(colnames(m) == "y")
m <- cbind(m[,1:x_pos], new=c(13, 14, 15), m[,y_pos:ncol(m)])

You can use this function :

insert_a_column <- function(mat, first_col,second_col, new_col, vec) {
   #Get index of first column to match
   one <- match(first_col, colnames(mat))
   #Get index of second column to match
   two <- match(second_col, colnames(mat))
   #Add the middle column and combine the data
   new_mat <- cbind(mat[,1:one, drop = FALSE], vec, 
                    mat[, (one + 1):ncol(mat), drop = FALSE])
   #rename the new column
   colnames(new_mat)[one + 1] <- new_col
   #Return the matrix.
   return(new_mat)
}

insert_a_column(m, "x", "y", "a", c(13, 14, 15))

#  w x  a y  z
#a 1 4 13 7 10
#b 2 5 14 8 11
#c 3 6 15 9 12

insert_a_column(m, "y", "z", "a", c(13, 14, 15))
#  w x y  a  z
#a 1 4 7 13 10
#b 2 5 8 14 11
#c 3 6 9 15 12
Related