How to convert a matrix to a list of column vectors in R?

Suppose you want to convert a matrix to a list, where each list item contains one column. list() or as.list() obviously won't work, and so far I have been using hacks using the tapply behavior:

 x <- matrix(1:10,ncol=2) tapply(x,rep(1:ncol(x),each=nrow(x)),function(i)i) 

I'm not quite happy with that. Does anyone know a cleaner method that I skip?

(to create a list filled with lines, the code can obviously be changed to:

 tapply(x,rep(1:nrow(x),ncol(x)),function(i)i) 

)

+69
list matrix r
Jul 25 '11 at 17:08
source share
12 answers

In the interest of cat skinning, treat the array as a vector, as if it had no dim attribute:

  split(x, rep(1:ncol(x), each = nrow(x))) 
+60
Jul 25 '11 at 23:17
source share
β€” -

Gavin's answer is simple and elegant. But if there are many columns, a much faster solution would be:

 lapply(seq_len(ncol(x)), function(i) x[,i]) 

In the example below, the speed difference is 6x:

 > x <- matrix(1:1e6, 10) > system.time( as.list(data.frame(x)) ) user system elapsed 1.24 0.00 1.22 > system.time( lapply(seq_len(ncol(x)), function(i) x[,i]) ) user system elapsed 0.2 0.0 0.2 
+69
Jul 25 '11 at 19:41
source share

data.frames are stored as lists, I believe. Therefore, coercion seems best:

 as.list(as.data.frame(x)) > as.list(as.data.frame(x)) $V1 [1] 1 2 3 4 5 $V2 [1] 6 7 8 9 10 

Benchmarking results are interesting. as.data.frame is faster than data.frame, either because data.frame needs to create a whole new object, or because tracking column names is something expensive (see c (noname ()) vs c ( ))? The solution suggested by @Tommy is an order of magnitude faster. The results of as.data.frame () can be slightly improved by forced manual control.

 manual.coerce <- function(x) { x <- as.data.frame(x) class(x) <- "list" x } library(microbenchmark) x <- matrix(1:10,ncol=2) microbenchmark( tapply(x,rep(1:ncol(x),each=nrow(x)),function(i)i) , as.list(data.frame(x)), as.list(as.data.frame(x)), lapply(seq_len(ncol(x)), function(i) x[,i]), c(unname(as.data.frame(x))), c(data.frame(x)), manual.coerce(x), times=1000 ) expr min lq 1 as.list(as.data.frame(x)) 176221 183064 2 as.list(data.frame(x)) 444827 454237 3 c(data.frame(x)) 434562 443117 4 c(unname(as.data.frame(x))) 257487 266897 5 lapply(seq_len(ncol(x)), function(i) x[, i]) 28231 35929 6 manual.coerce(x) 160823 167667 7 tapply(x, rep(1:ncol(x), each = nrow(x)), function(i) i) 1020536 1036790 median uq max 1 186486 190763 2768193 2 460225 471346 2854592 3 449960 460226 2895653 4 271174 277162 2827218 5 36784 37640 1165105 6 171088 176221 457659 7 1052188 1080417 3939286 is.list(manual.coerce(x)) [1] TRUE 
+25
Jul 25 2018-11-17T00:
source share

It seems that the conversion to a data frame and then to a list works:

 > as.list(data.frame(x)) $X1 [1] 1 2 3 4 5 $X2 [1] 6 7 8 9 10 > str(as.list(data.frame(x))) List of 2 $ X1: int [1:5] 1 2 3 4 5 $ X2: int [1:5] 6 7 8 9 10 
+15
Jul 25 2018-11-17T00:
source share

Using plyr can be really useful for things like this:

 library("plyr") alply(x,2) $`1` [1] 1 2 3 4 5 $`2` [1] 6 7 8 9 10 attr(,"class") [1] "split" "list" 
+11
Jul 27 2018-11-11T00:
source share

I know this is an anathema in R, and I don't have a great reputation to support this, but I believe the for loop will be more efficient. I use the following function to convert a matrix mat to a list of its columns:

 mat2list <- function(mat) { list_length <- ncol(mat) out_list <- vector("list", list_length) for(i in 1:list_length) out_list[[i]] <- mat[,i] out_list } 

Quick test compared to mdsummer and the original solution:

 x <- matrix(1:1e7, ncol=1e6) system.time(mat2list(x)) user system elapsed 2.728 0.023 2.720 system.time(split(x, rep(1:ncol(x), each = nrow(x)))) user system elapsed 4.812 0.194 4.978 system.time(tapply(x,rep(1:ncol(x),each=nrow(x)),function(i)i)) user system elapsed 11.471 0.413 11.817 
+5
Aug 02 '13 at 11:36 on
source share

The new asplit() function will appear in the R database in version 3.6. Up to this point and in a spirit similar to @ mdsumner's answer, we can also do

 split(x, slice.index(x, MARGIN)) 

according to asplit() . However, as shown earlier, all split() -based solutions are much slower than @Tommy lapply/'[' . This is also true for the new asplit() , at least in its current form.

 split_1 <- function(x) asplit(x, 2L) split_2 <- function(x) split(x, rep(seq_len(ncol(x)), each = nrow(x))) split_3 <- function(x) split(x, col(x)) split_4 <- function(x) split(x, slice.index(x, 2L)) split_5 <- function(x) lapply(seq_len(ncol(x)), function(i) x[, i]) dat <- matrix(rnorm(n = 1e6), ncol = 100) #> Unit: milliseconds #> expr min lq mean median uq max neval #> split_1(dat) 16.250842 17.271092 20.26428 18.18286 20.185513 55.851237 100 #> split_2(dat) 52.975819 54.600901 60.94911 56.05520 60.249629 105.791117 100 #> split_3(dat) 32.793112 33.665121 40.98491 34.97580 39.409883 74.406772 100 #> split_4(dat) 37.998140 39.669480 46.85295 40.82559 45.342010 80.830705 100 #> split_5(dat) 2.622944 2.841834 3.47998 2.88914 4.422262 8.286883 100 dat <- matrix(rnorm(n = 1e6), ncol = 1e5) #> Unit: milliseconds #> expr min lq mean median uq max neval #> split_1(dat) 204.69803 231.3023 261.6907 246.4927 289.5218 413.5386 100 #> split_2(dat) 229.38132 235.3153 253.3027 242.0433 259.2280 339.0016 100 #> split_3(dat) 208.29162 216.5506 234.2354 221.7152 235.3539 342.5918 100 #> split_4(dat) 214.43064 221.9247 240.7921 231.0895 246.2457 323.3709 100 #> split_5(dat) 89.83764 105.8272 127.1187 114.3563 143.8771 209.0670 100 
+3
Feb 01 '19 at 16:15
source share

In Some R-Help site, available through nabble.com I find:

 c(unname(as.data.frame(x))) 

as a valid solution, and in my R v2.13.0 install this looks fine:

 > y <- c(unname(as.data.frame(x))) > y [[1]] [1] 1 2 3 4 5 [[2]] [1] 6 7 8 9 10 

You can’t say anything about comparing performance or how clean it is ,-)

+2
Jul 25 2018-11-17T00:
source share

convertRowsToList {BBmisc}

Convert rows (columns) in data.frame or matrix to lists.

 BBmisc::convertColsToList(x) 

ref: http://berndbischl.imtqy.com/BBmisc/man/convertRowsToList.html

+2
Feb 23 '18 at 3:24
source share

The tidyverse purrr package has an array_tree() function that does this with minimal effort:

 x <- matrix(1:10,ncol=2) xlist <- purrr::array_tree(x, margin=2) xlist #> [[1]] #> [1] 1 2 3 4 5 #> #> [[2]] #> [1] 6 7 8 9 10 

Use margin=1 to list by line. Works for n-dimensional arrays. By default, names are saved:

 x <- matrix(1:10,ncol=2) colnames(x) <- letters[1:2] xlist <- purrr::array_tree(x, margin=2) xlist #> $a #> [1] 1 2 3 4 5 #> #> $b #> [1] 6 7 8 9 10 

(this is almost a verbatim copy of my answer to a similar question here )

+2
Feb 12 '19 at 14:49
source share

You can use apply and then c with do.call

 x <- matrix(1:10,ncol=2) do.call(c, apply(x, 2, list)) #[[1]] #[1] 1 2 3 4 5 # #[[2]] #[1] 6 7 8 9 10 

And it looks like it will save the column names when added to the matrix.

 colnames(x) <- c("a", "b") do.call(c, apply(x, 2, list)) #$a #[1] 1 2 3 4 5 # #$b #[1] 6 7 8 9 10 
+1
Aug 31 '14 at 23:10
source share

In the trivial case, when the number of columns is small and constant, I found that the fastest option is a simple code conversion:

 mat2list <- function (mat) lapply(1:2, function (i) mat[, i]) mat2list2 <- function (mat) list(mat[, 1], mat[, 2]) ## Microbenchmark results; unit: microseconds # expr min lq mean median uq max neval ## mat2list(x) 7.464 7.932 8.77091 8.398 8.864 29.390 100 ## mat2list2(x) 1.400 1.867 2.48702 2.333 2.333 27.525 100 
0
Nov 06 '17 at 17:27
source share



All Articles