How to select random columns from a dataset

Viewed 2364

I have a dataset like:

ID  val1    val2    val3    val4
1   4       9       10      16
2   1.5     6       2.3     99
3   8       7       7       10

I would like to check whether the number of columns (i.e., val columns) is less than 6 and if that is the case, I want to randomly select the number of columns left from the existing columns and add them again to the dataset.

In the case above, the number of columns left is 2 (6 - 4 columns of val). In this case, I would like to select 2 random columns from the val columns and add them to the dataset. one possible solution would be:

ID  val1    val2    val3    val4   val2   val1
1   4       9       10      16     9      4
2   1.5     6       2.3     99     6      1.5
3   8       7       7       10     7      8

Columns val2 and val1 are randomly selected and added to the dataset. The problem that I'm facing is how to select random columns. I know how to select random rows by using sample_n function, but I couldn't find any function to select random columns.

What I did so far is:

t <- read.csv("path", header=TRUE) # load file
numCols <- 6
cc <- ncol(t[,-1]) #no need for ID column
if(cc < numCols){
   # I need some function to select random columns 
}
2 Answers

A dataframe is just a special kind of list, so you can think of it as selecting random list elements rather than columns. That means a random sample of two columns is as simple as sample(df, 2). In your case you can keep things terse by cbinding a sample from df[-1], assuming ID is the first column:

nc <- 6 + 1 # val columns plus ID column

if(ncol(df) < nc){
    cbind(df, sample(df[-1], nc - ncol(df)))
}

#### OUTPUT ####

  ID val1 val2 val3 val4 val3 val2
1  1  4.0    9 10.0   16 10.0    9
2  2  1.5    6  2.3   99  2.3    6
3  3  8.0    7  7.0   10  7.0    7

If you don't know where ID is you could use something like df[-grep("ID", names(df))] instead of df[-1]. If there are other columns that are neither ID nor do they start with val, then you should use df[grep("^val", names(df))]. In the latter case you'll also need to make adjustments to nc, i.e. instead of just 1 you'll need to add the number of columns that don't start with val.

We can make a function

add_columns <- function(df, n) {
   cols <- grep("val", names(df), value = TRUE)
   if (length(cols) < n)
     return(cbind(df, df[sample(cols, n - length(cols))]))
   return(df)
}

add_columns(df, 6)
#  ID val1 val2 val3 val4 val2 val1
#1  1  4.0    9 10.0   16    9  4.0
#2  2  1.5    6  2.3   99    6  1.5
#3  3  8.0    7  7.0   10    7  8.0

add_columns(df, 5)
#  ID val1 val2 val3 val4 val3
#1  1  4.0    9 10.0   16 10.0
#2  2  1.5    6  2.3   99  2.3
#3  3  8.0    7  7.0   10  7.0
Related