R에서 행렬을 열 벡터 목록으로 변환하는 방법은 무엇입니까?


80

행렬을 목록으로 변환하려는 경우 목록의 각 요소에 하나의 열이 포함됩니다. 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)

)


1
최적화 된 Rccp 솔루션이 더 빠를 수 있는지 궁금합니다.
Marek

답변:


68

고양이를 스키닝하려면 배열을 dim 속성이없는 것처럼 벡터로 취급합니다.

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

9
이것이 무엇을 하는가의 핵심입니다 tapply. 그러나 더 간단합니다 :). 아마 느리지 만 잘 생긴 솔루션이 될 것입니다 split(x, col(x))(그리고 split(x, row(x))각각).
Marek

확인했습니다. 똑같이 빠르다 split(x, c(col(x))). 그러나 더 나빠 보입니다.
Marek

2
split (x, col (x))은 더 좋아 보입니다-벡터에 대한 암시 적 강제 변환은 괜찮습니다. . .
mdsumner 2011

2
많은 테스트 후, 특히 많은 행 또는 열에서 가장 빠르게 작동하는 것 같습니다.
Joris Meys 2012 년

2
x열 이름 이 있으면 이름 split(x, col(x, as.factor = TRUE))이 유지됩니다.
banbh

73

개빈의 대답은 간단하고 우아합니다. 그러나 열이 많은 경우 훨씬 빠른 솔루션은 다음과 같습니다.

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 

2
+1 다양한 솔루션의 상대적 효율성에 대한 좋은 점. 지금까지 최고의 답변입니다.
Gavin Simpson

하지만 동일한 결과를 얻으려면 lapply (seq_len (nrow (x)), function (i) x [i,])를 수행해야하고 속도가 더 느립니다.
skan

26

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 ()) 대 c () 비교를 확인하십시오). )? @Tommy가 제공하는 lapply 솔루션은 훨씬 더 빠릅니다. 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

개빈에게 5 초 패배. 젠장, "당신은 인간입니까"화면? :-)
Ari B. Friedman

1
추첨의 행운을 빕니다, 저는 @Joris가 Perter Flom의 Q에 대답하기 전에 저에게 몰래 들어간 후에 이것을 보았습니다. 또한 as.data.frame()데이터 프레임의 이름을 잃어 버리 므로 data.frame()조금 더 좋습니다.
Gavin Simpson

2
등가의가 manual.coerce(x)될 수있다 unclass(as.data.frame(x)).
Marek

고마워요 마렉. 아마도 함수 정의 / 호출을 피할 수 있기 때문에 약 6 % 더 빠릅니다.
아리 B. 프리드먼

16

데이터 프레임으로 변환 한 다음 목록으로 변환하는 것이 작동하는 것 같습니다.

> 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

12

사용 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" 

6

나는 이것이 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 

물론 이것은 열 이름을 삭제하지만 원래 질문에서는 중요하지 않은 것 같습니다.
alfymbohm

2
토미의 솔루션은보다 빠르고 컴팩트 :system.time( lapply(seq_len(ncol(x)), function(i) x[,i]) ) user: 1.668 system: 0.016 elapsed: 1.693
alfymbohm

다른 맥락에서 이것을 알아 내려고했지만 작동하지 않습니다 : stackoverflow.com/questions/63801018 .... 이것을 찾고 있습니다 :vec2 = castMatrixToSequenceOfLists(vecs);
mshaffer

5

새로운 기능 asplit()은 v3.6의 기본 R에 제공 될 것입니다. 그때까지 @mdsumner의 대답과 비슷한 정신으로 우리도 할 수 있습니다.

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

의 문서에 따라 asplit(). 그러나 이전에 표시된 것처럼 모든 split()기반 솔루션은 @ Tommy 's보다 훨씬 느립니다 lapply/`[`. 이것은 또한 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

4

asplit행렬을 벡터 목록으로 변환하는 데 사용

asplit(x, 1) # split into list of row vectors
asplit(x, 2) # split into list of column vectors

3

최소한의 소란으로 이것을 수행 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

(이 비슷한 질문에 대한 내 대답의 근처 어적 사본입니다 여기 )


2

nabble.com을 통해 액세스 할 수있는 일부 R 도움말 사이트 에서 다음을 찾습니다.

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

성능 비교 또는 그것이 얼마나 깨끗한 지에 대해 말할 수 없습니다 ;-)


2
흥미 롭군. 나는 이것이 또한 강제로 작동한다고 생각합니다. c(as.data.frame(x))동일 동작을 생산as.list(as.data.frame(x)
아리 B. 프리드

샘플리스트 / 매트릭스의 멤버가 같은 타입이기 때문에 그렇다고 생각 합니다만, 저는 경험이 아닙니다.
Dilettant

2

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

5
또는unlist(apply(x, 2, list), recursive = FALSE)
baptiste

네. 답변 @baptiste로 추가해야합니다.
Rich Scriven

1
하지만 페이지 하단으로 스크롤해야합니다! 나는 너무 게으르다
baptiste aug

내 컴퓨터에 "종료"버튼이 있습니다 ... :-)
Rich Scriven

빈 목록을 만들고 채우는 방법으로도 가능하다고 생각합니다. y <- vector("list", ncol(x))그런 다음 y[1:2] <- x[,1:2]정확히 작동하지는 않지만 라인을 따라 뭔가 .
Rich Scriven


1

열 수가 작고 일정한 사소한 경우 가장 빠른 옵션은 단순히 변환을 하드 코딩하는 것입니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.