Supposons que vous souhaitiez convertir une matrice en liste, où chaque élément de la liste contient une colonne. list()
ou as.list()
ne fonctionnera évidemment pas, et jusqu'à présent j'utilise un hack utilisant le comportement de tapply
:
x <- matrix(1:10,ncol=2)
tapply(x,rep(1:ncol(x),each=nrow(x)),function(i)i)
Je ne suis pas complètement satisfait de cela. Quelqu'un connaît une méthode plus propre que je néglige?
(pour faire une liste remplie de lignes, le code peut évidemment être changé en:
tapply(x,rep(1:nrow(x),ncol(x)),function(i)i)
)
Dans l'intérêt de dépouiller le chat, traitez le tableau comme un vecteur comme s'il n'avait pas d'attribut dim:
split(x, rep(1:ncol(x), each = nrow(x)))
La réponse de Gavin est simple et élégante. Mais s'il y a beaucoup de colonnes, une solution beaucoup plus rapide serait:
lapply(seq_len(ncol(x)), function(i) x[,i])
La différence de vitesse est 6x dans l'exemple ci-dessous:
> 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
les cadres de données sont stockés sous forme de listes, je crois. Par conséquent, la coercition semble la meilleure:
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
Les résultats de l'analyse comparative sont intéressants. as.data.frame est plus rapide que data.frame, soit parce que data.frame doit créer un tout nouvel objet, soit parce que le suivi des noms de colonne est en quelque sorte coûteux (voyez le c(unname()) vs c() comparaison)? La solution lapply fournie par @Tommy est plus rapide d'un ordre de grandeur. Les résultats as.data.frame () peuvent être quelque peu améliorés en forçant manuellement.
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
La conversion en bloc de données, puis en liste, semble fonctionner:
> 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
L'utilisation de plyr
peut être très utile pour des choses comme ceci:
library("plyr")
alply(x,2)
$`1`
[1] 1 2 3 4 5
$`2`
[1] 6 7 8 9 10
attr(,"class")
[1] "split" "list"
Je sais que c'est un anathème dans R, et je n'ai pas vraiment beaucoup de réputation pour le sauvegarder, mais je trouve une boucle for pour être plus efficace. J'utilise la fonction suivante pour convertir le mat de matrice en une liste de ses colonnes:
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
}
Comparaison rapide avec mdsummer et la solution originale:
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
La nouvelle fonction asplit()
arrivera à la base R dans la v3.6. Jusque-là et dans un esprit similaire à la réponse de @mdsumner, nous pouvons également faire
split(x, slice.index(x, MARGIN))
selon les documents de asplit()
. Cependant, comme indiqué précédemment, toutes les solutions basées sur split()
sont beaucoup plus lentes que lapply/`[`
De @ Tommy. Cela vaut également pour la nouvelle asplit()
, au moins dans sa forme actuelle.
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
Sous Certains sites d'aide R accessibles via nabble.com Je trouve:
c(unname(as.data.frame(x)))
comme solution valide et dans mon installation de R v2.13.0, cela semble correct:
> y <- c(unname(as.data.frame(x)))
> y
[[1]]
[1] 1 2 3 4 5
[[2]]
[1] 6 7 8 9 10
Je ne peux rien dire sur les comparaisons de performances ou sur la propreté ;-)
Il y a une fonction array_tree()
dans le package purrr
de tidyverse qui fait cela avec un minimum de bruit:
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
Utilisation margin=1
pour afficher la liste par ligne. Fonctionne pour les tableaux à n dimensions. Il conserve les noms par défaut:
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
(ceci est une copie presque mot pour mot de ma réponse à une question similaire ici )
convertRowsToList {BBmisc}
Convertissez des lignes (colonnes) de data.frame ou matrice en listes.
BBmisc::convertColsToList(x)
réf: http://berndbischl.github.io/BBmisc/man/convertRowsToList.html
Vous pouvez utiliser apply
puis c
avec 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
Et il semble qu'il conservera les noms des colonnes, lorsqu'il sera ajouté à la matrice.
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
Dans le cas trivial où le nombre de colonnes est petit et constant, j'ai trouvé que l'option la plus rapide est de simplement coder en dur la 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