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
मुझे आश्चर्य है कि अगर अनुकूलित आरसीसीपी समाधान तेज हो सकता है।
मारेक

जवाबों:


68

बिल्ली की खाल उतारने के हितों में, एक वेक्टर के रूप में सरणी का इलाज करें जैसे कि इसकी कोई मंद विशेषता नहीं थी:

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

9
यह क्या tapplyकरना है की कोर है। लेकिन यह आसान है :)। संभवतः धीमा लेकिन अच्छा दिखने वाला समाधान split(x, col(x))(और split(x, row(x))क्रमशः) होगा।
मारेक

मैंने इसे जाँचा था। समान रूप से उपवास होगा split(x, c(col(x)))। लेकिन यह बदतर लग रहा है।
मारेक

2
विभाजन (x, col (x)) बेहतर दिखता है - वेक्टर के लिए अंतर्निहित जबरदस्ती ठीक है। । ।
मॉडसमर

2
बहुत परीक्षण के बाद, यह सबसे तेज काम करने लगता है, विशेष रूप से बहुत अधिक पंक्ति या स्तंभों के साथ।
जोरिस मेय्स

2
ध्यान दें कि यदि xस्तंभ नाम हैं तो नामों split(x, col(x, as.factor = TRUE))को संरक्षित करेगा।
प्रतिबंध

73

गेविन का जवाब सरल और सुरुचिपूर्ण है। लेकिन अगर कई स्तंभ हैं, तो बहुत तेज़ समाधान होगा:

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

नीचे दिए गए उदाहरण में गति अंतर 6x है:

> 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 विभिन्न समाधानों की सापेक्ष दक्षता के बारे में अच्छी बात। इस प्रकार सबसे अच्छा उत्तर।
गैविन सिम्पसन

लेकिन मुझे लगता है कि समान परिणाम प्राप्त करने के लिए आपको lapply (seq_len (nrow (x)), फ़ंक्शन (i) x [i,]) करने की आवश्यकता है और फिर धीमी है।
21

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 डेटा से अधिक तेज़ है। वर्कफ़्लो, या तो क्योंकि डेटा.फ़्रेम को पूरी तरह से एक नया ऑब्जेक्ट बनाना पड़ता है, या क्योंकि कॉलम नामों पर नज़र रखना किसी तरह महंगा है (ग (unname ()) बनाम c () तुलना देखें )? @ टॉमी द्वारा प्रदान किया जाने वाला लेप्ली समाधान परिमाण के एक क्रम से तेज है। As.data.frame () परिणाम मैन्युअल रूप से coercing द्वारा कुछ सुधार किया जा सकता है।

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 सेकंड से हराया। डारन यू, "आर यू ए ह्यूमन" स्क्रीन? :-)
अरी बी। फ्रीडमैन

1
मुझे लगता है कि ड्रा की किस्मत, मैं इसे देख रहा था, जब @ जॉरिस ने मेरे आगे छलांग लगाई, तो उन्होंने पेर्टर फ्लॉम के प्रश्न का उत्तर दिया। इसके अलावा, as.data.frame()डेटा फ्रेम के नामों को खो देता है, इसलिए data.frame()थोड़ा अच्छा है।
गैविन सिम्पसन

2
के बराबर manual.coerce(x)हो सकता है unclass(as.data.frame(x))
मारेक

धन्यवाद मारेक। यह लगभग 6% तेज है, संभवत: क्योंकि मैं फ़ंक्शन परिभाषा / कॉल का उपयोग करने से बच सकता हूं।
अरी बी। फ्रीडमैन

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

मुझे पता है कि यह आर में एंथम है, और मुझे इसे वापस करने के लिए वास्तव में बहुत प्रतिष्ठा नहीं है, लेकिन मैं लूप की तलाश कर रहा हूं बल्कि अधिक कुशल होना चाहता हूं। मैट्रिक्स चटाई को उसके स्तंभों की सूची में बदलने के लिए मैं निम्नलिखित फ़ंक्शन का उपयोग कर रहा हूं:

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
अल्फाइमोहोम

यह एक अलग संदर्भ में जानने की कोशिश कर रहा है, काम नहीं करता है: stackoverflow.com/questions/63801018 .... इस की तलाश में:vec2 = castMatrixToSequenceOfLists(vecs);
mshaffer

5

नया फंक्शन asplit()v3.6 में बेस R पर आने वाला है। तब तक और @mdsumner के उत्तर के समान भावना में हम भी कर सकते हैं

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

डॉक्स के अनुसार asplit()। जैसा कि पहले दिखाया गया है, सभी split()आधारित समाधान @ टॉमी की तुलना में बहुत धीमे हैं 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इसके बजाय पंक्ति द्वारा सूचीबद्ध करने के लिए उपयोग करें । एन-आयामी सरणियों के लिए काम करता है। यह डिफ़ॉल्ट रूप से नामों को संरक्षित करता है:

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 के माध्यम से सुलभ कुछ आर हेल्प साइट के तहत मुझे लगता है:

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

एक वैध समाधान के रूप में और मेरे आर 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)
अरी बी। फ्रीडमैन

मुझे लगता है कि यह ऐसा है, क्योंकि नमूना सूचियों / मैट्रिक्स के सदस्य एक ही प्रकार के हैं, लेकिन मैं एक एक्सपर्ट नहीं हूं।
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 के रूप में जोड़ना चाहिए।
रिच स्क्रिप्‍ट

1
लेकिन उस पृष्ठ के नीचे स्क्रॉल करने की आवश्यकता होगी! मैं उस के लिए बहुत आलसी हूँ
बैपटिस्ट

मेरी मशीन पर "END" बटन है ... :-)
रिच स्क्रिप्‍ट

मुझे लगता है कि यह शायद एक खाली सूची बनाने और इसे भरने से भी हो सकता है। y <- vector("list", ncol(x))और फिर की तर्ज पर कुछ y[1:2] <- x[,1:2], हालांकि यह उस सटीक तरीके से काम नहीं करता है।
रिच स्क्रिप्‍ट


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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.