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á, y hasta ahora uso un truco 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 conoce un método más limpio que estoy pasando por alto?
(para hacer una lista llena de filas, el código obviamente se puede cambiar a:
tapply(x,rep(1:nrow(x),ncol(x)),function(i)i)
)
Respuestas:
Con el fin 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)))
fuente
tapply
hacemos. Pero es más simple :). Probablemente una solución más lenta pero atractiva serásplit(x, col(x))
(ysplit(x, row(x))
respectivamente).split(x, c(col(x)))
. Pero parece peor.x
tiene nombres de columnasplit(x, col(x, as.factor = TRUE))
, los conservará.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
fuente
los data.frames se almacenan como listas, creo. Por tanto, la coerción parece 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 la evaluación comparativa 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 (atestigua la comparación c (unname ()) vs c () )? La solución de solapamiento proporcionada por @Tommy es más rápida en un orden de magnitud. Los resultados de as.data.frame () se pueden mejorar un poco coaccionando manualmente.
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
fuente
as.data.frame()
pierde los nombres del marco de datos, por lo quedata.frame()
es un poco mejor.manual.coerce(x)
podría serunclass(as.data.frame(x))
.La conversión a un marco de datos de ahí 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
fuente
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"
fuente
Sé que esto es un anatema en R, y realmente no tengo mucha reputación para respaldar esto, pero encuentro que un bucle for es bastante más eficiente. Estoy usando la siguiente función para convertir el tapete 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 }
Comparación rápida de referencia con mdsummer y la solución original:
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
fuente
system.time( lapply(seq_len(ncol(x)), function(i) x[,i]) ) user: 1.668 system: 0.016 elapsed: 1.693
vec2 = castMatrixToSequenceOfLists(vecs);
La nueva función
asplit()
llegará a la base R en la v3.6. Hasta entonces y en espíritu similar a la respuesta de @mdsumner también podemos hacersegún los documentos de
asplit()
. Sin embargo, como se mostró anteriormente, todas lassplit()
soluciones basadas son mucho más lentas que las de @ Tommylapply/`[`
. Esto también es válido para lo nuevoasplit()
, al menos en su forma actual.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
fuente
Úselo
asplit
para convertir una matriz en una lista de vectoresasplit(x, 1) # split into list of row vectors asplit(x, 2) # split into list of column vectors
fuente
Hay una función
array_tree()
en elpurrr
paquete de tidyverse que hace esto con un mínimo de esfuerzo: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
para listar por fila en su lugar. Funciona para matrices de n dimensiones. Conserva los nombres por defecto: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
(esta es una copia casi palabra por palabra de mi respuesta a una pregunta similar aquí )
fuente
En el 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 las comparaciones de rendimiento o lo limpio que es ;-)
fuente
c(as.data.frame(x))
produce un comportamiento idéntico aas.list(as.data.frame(x)
Podrías usar
apply
y luegoc
condo.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
fuente
unlist(apply(x, 2, list), recursive = FALSE)
y <- vector("list", ncol(x))
y luego algo parecido ay[1:2] <- x[,1:2]
, aunque no funciona de esa manera exacta.convertRowsToList {BBmisc}
Convierta filas (columnas) de data.frame o matriz en listas.
ref: http://berndbischl.github.io/BBmisc/man/convertRowsToList.html
fuente
En el caso trivial en el 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
fuente