Допустим, вы хотите преобразовать матрицу в список, где каждый элемент списка содержит один столбец. list()
или as.list()
явно не сработает, и до сих пор я использую хак, использующий поведение tapply
:
x <- matrix(1:10,ncol=2)
tapply(x,rep(1:ncol(x),each=nrow(x)),function(i)i)
Меня это не совсем устраивает. Кто-нибудь знает более чистый метод, который я не замечаю?
(для создания списка, заполненного строками, код, очевидно, можно изменить на:
tapply(x,rep(1:nrow(x),ncol(x)),function(i)i)
)
Ответы:
Чтобы снять шкуру с кошки, рассматривайте массив как вектор, как если бы он не имел атрибута dim:
split(x, rep(1:ncol(x), each = nrow(x)))
источник
tapply
нужно делать. Но так проще :). Наверное, более медленное, но красивое решение будетsplit(x, col(x))
(иsplit(x, row(x))
соответственно).split(x, c(col(x)))
. Но это выглядит хуже.x
есть имена столбцов, именаsplit(x, col(x, as.factor = TRUE))
будут сохранены.Ответ Гэвина прост и элегантен. Но если столбцов много, гораздо более быстрым решением будет:
lapply(seq_len(ncol(x)), function(i) x[,i])
В приведенном ниже примере разница в скорости составляет 6 раз:
> 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 хранятся в виде списков. Поэтому принуждение кажется лучшим:
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
Результаты сравнительного анализа интересны. as.data.frame работает быстрее, чем data.frame, либо потому, что data.frame должен создать целый новый объект, либо потому, что отслеживание имен столбцов как-то дорого обходится (обратите внимание на сравнение c (unname ()) vs c () )? Решение lapply, предоставленное @Tommy, на порядок быстрее. Результаты as.data.frame () могут быть несколько улучшены путем принуждения вручную.
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
источник
as.data.frame()
теряет имена фрейма данных, такdata.frame()
что это немного лучше.manual.coerce(x)
мог бытьunclass(as.data.frame(x))
.Преобразование во фрейм данных, а затем в список, похоже, работает:
> 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
источник
Использование
plyr
может быть действительно полезно для таких вещей:library("plyr") alply(x,2) $`1` [1] 1 2 3 4 5 $`2` [1] 6 7 8 9 10 attr(,"class") [1] "split" "list"
источник
Я знаю, что это анафема в R, и у меня нет особой репутации, подтверждающей это, но я считаю, что цикл for более эффективен. Я использую следующую функцию для преобразования матрицы матриц в список ее столбцов:
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 }
Быстрое сравнение результатов теста 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
источник
system.time( lapply(seq_len(ncol(x)), function(i) x[,i]) ) user: 1.668 system: 0.016 elapsed: 1.693
vec2 = castMatrixToSequenceOfLists(vecs);
Новая функция
asplit()
появится в базе R в v3.6. До тех пор и в духе ответа @mdsumner мы также можем сделатьсогласно документации
asplit()
. Однако, как было показано ранее, всеsplit()
решения на основе намного медленнее, чем у @ Tommylapply/`[`
. То же самое и с новымasplit()
, по крайней мере, в его нынешнем виде.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
источник
Используется
asplit
для преобразования матрицы в список векторовasplit(x, 1) # split into list of row vectors asplit(x, 2) # split into list of column vectors
источник
В пакете
array_tree()
tidyverse есть функция,purrr
которая делает это без лишних хлопот: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
margin=1
Вместо этого используйте для вывода по строкам. Работает для n-мерных массивов. По умолчанию сохраняет имена: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
(это почти дословная копия моего ответа на аналогичный вопрос здесь )
источник
При некоторых R Помощь сайта доступны через nabble.com я найти:
c(unname(as.data.frame(x)))
в качестве допустимого решения и в моей установке R v2.13.0 это выглядит нормально:
> y <- c(unname(as.data.frame(x))) > y [[1]] [1] 1 2 3 4 5 [[2]] [1] 6 7 8 9 10
Ничего не могу сказать о сравнении производительности или о том, насколько она чистая ;-)
источник
c(as.data.frame(x))
производит такое же поведение, какas.list(as.data.frame(x)
Вы можете использовать,
apply
а затемc
с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
И похоже, что он сохранит имена столбцов при добавлении в матрицу.
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
источник
unlist(apply(x, 2, list), recursive = FALSE)
y <- vector("list", ncol(x))
а затем что-то вродеy[1:2] <- x[,1:2]
, хотя это точно не работает.convertRowsToList {BBmisc}
Преобразуйте строки (столбцы) data.frame или матрицы в списки.
ссылка: http://berndbischl.github.io/BBmisc/man/convertRowsToList.html
источник
В тривиальном случае, когда количество столбцов невелико и постоянно, я обнаружил, что самый быстрый вариант - просто жестко закодировать преобразование:
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
источник