¿Cómo convertir una matriz a una lista de columnas-vectores en R?

Supongamos que desea convertir una matriz en una lista, donde cada elemento de la lista contiene una columna. list() o as.list() obviamente no funcionarán, y hasta ahora utilizo un hack usando el comportamiento de tapply :

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

No estoy completamente feliz con esto. ¿Alguien sabe un método más limpio que estoy pasando por alto?

(para hacer una lista llena con las filas, el código obviamente puede ser cambiado a:

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

)

Con el objective de despellejar al gato, trate la matriz como un vector como si no tuviera un atributo tenue:

  split(x, rep(1:ncol(x), each = nrow(x))) 

La respuesta de Gavin es simple y elegante. Pero si hay muchas columnas, una solución mucho más rápida sería:

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

La diferencia de velocidad es 6x en el siguiente ejemplo:

 > 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 

data.frames se almacenan como listas, creo. Por lo tanto, la coerción parece ser la mejor:

 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 

Los resultados de benchmarking son interesantes. as.data.frame es más rápido que data.frame, ya sea porque data.frame tiene que crear un objeto completamente nuevo, o porque hacer un seguimiento de los nombres de las columnas es de alguna manera costoso (vea la comparación c (unname ()) vs c () )? La solución de aplicación provista por @Tommy es más rápida en un orden de magnitud. Los resultados de as.data.frame () pueden mejorarse algo mediante la coerción manual.

 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 

Convertir a un cuadro de datos a una lista parece funcionar:

 > 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 

Usar plyr puede ser realmente útil para cosas como esta:

 library("plyr") alply(x,2) $`1` [1] 1 2 3 4 5 $`2` [1] 6 7 8 9 10 attr(,"class") [1] "split" "list" 

Sé que esto es anatema en R, y realmente no tengo mucha reputación para respaldar esto, pero estoy encontrando que un bucle for es bastante más eficiente. Estoy usando la siguiente función para convertir la matriz de matriz en una lista de sus columnas:

 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 } 

Referencia rápida comparada con la solución original y de mdsummer:

 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 

Debajo del sitio de Ayuda de Some R accesible a través de nabble.com encuentro:

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

como una solución válida y en mi R v2.13.0 instalar esto se ve bien:

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

No puedo decir nada sobre comparaciones de rendimiento o qué tan limpio es 😉

Puede usar apply y luego c con 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 

Y parece que conservará los nombres de las columnas cuando se agreguen a la matriz.

 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 

convertRowsToList {BBmisc}

Convierte filas (columnas) de data.frame o matrix en listas.

 BBmisc::convertColsToList(x) 

ref: http://berndbischl.github.io/BBmisc/man/convertRowsToList.html

En el caso trivial en que el número de columnas es pequeño y constante, he descubierto que la opción más rápida es simplemente codificar la conversión:

 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