data.frame पंक्तियों को एक सूची में


123

मेरे पास एक data.frame है जिसे मैं पंक्तियों द्वारा एक सूची में बदलना चाहता हूं, जिसका अर्थ है कि प्रत्येक पंक्ति अपने स्वयं के सूची तत्वों के अनुरूप होगी। दूसरे शब्दों में, मैं एक सूची चाहूंगा जो डेटा के रूप में लंबे समय तक हो। बफ़र में पंक्तियाँ हैं।

अब तक, मैंने इस समस्या से निम्नलिखित तरीके से निपट लिया है, लेकिन मैं सोच रहा था कि क्या इस से निपटने का एक बेहतर तरीका है।

xy.df <- data.frame(x = runif(10),  y = runif(10))

# pre-allocate a list and fill it with a loop
xy.list <- vector("list", nrow(xy.df))
for (i in 1:nrow(xy.df)) {
    xy.list[[i]] <- xy.df[i,]
}

जवाबों:


164

ऐशे ही:

xy.list <- split(xy.df, seq(nrow(xy.df)))

और यदि आप चाहते हैं कि xy.dfउत्पादन सूची के नाम के साथ ही, आप कर सकते हैं:

xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))

4
ध्यान दें कि, splitप्रत्येक तत्व का उपयोग करने के बाद data.frame with 1 rows and N columnsइसके बजाय टाइप करेंlist of length N
करोल दानिलुक

मैं केवल इतना ही जोड़ूंगा कि यदि आप उपयोग करते splitहैं तो आपको शायद करना चाहिए drop=Tअन्यथा कारकों के लिए आपके मूल स्तर में गिरावट नहीं होगी
डेनिस

51

यूरेका!

xy.list <- as.list(as.data.frame(t(xy.df)))

1
लागू करने के लिए उपयोग करने के लिए देखभाल कैसे करें?
रोमन लुसट्रिक

3
unlist(apply(xy.df, 1, list), recursive = FALSE)। हालाँकि फ्लोडेल का घोल उपयोग करने की तुलना में अधिक कुशल है applyया t
अरुण

11
यहाँ समस्या यह है कि एक को tधर्मान्तरित किया data.fameजाता है matrixताकि आपकी सूची में तत्व परमाणु वैक्टर हों, न कि ओपी द्वारा अनुरोधित सूची। यह आमतौर पर एक समस्या नहीं है जब तक कि आपके xy.dfमिश्रित प्रकार शामिल न हों ...
Calimo

2
यदि आप मूल्यों पर लूप करना चाहते हैं, तो मैं अनुशंसा नहीं करता हूं apply। यह वास्तव में आर में कार्यान्वित लूप के लिए है। lapplyसी में लूपिंग करता है, जो काफी तेज है। यदि आप बहुत अधिक लूपिंग कर रहे हैं तो यह सूची-पंक्तियों का प्रारूप वास्तव में बेहतर है।
लिज़ सैंडर

1
भविष्य से एक और टिप्पणी apply.mapply(data.frame, xy.df, NULL)
जोड़ते हुए

15

यदि आप data.frame (जैसा कि मैं करते हैं) को पूरी तरह से दुरुपयोग करना चाहते हैं और $ कार्यक्षमता रखना पसंद करते हैं, तो एक तरीका यह है कि आप data.frame को एक-लाइन data.find में विभाजित करें: एक सूची में एकत्र किया गया।

> df = data.frame(x=c('a','b','c'), y=3:1)
> df
  x y
1 a 3
2 b 2
3 c 1

# 'convert' into a list of data.frames
ldf = lapply(as.list(1:dim(df)[1]), function(x) df[x[1],])

> ldf
[[1]]
x y
1 a 3    
[[2]]
x y
2 b 2
[[3]]
x y
3 c 1

# and the 'coolest'
> ldf[[2]]$y
[1] 2

यह न केवल बौद्धिक हस्तमैथुन है, बल्कि $ तर्जनी को रखने के लिए डेटा.फ्रेम को अपनी लाइनों की एक सूची में बदलने की अनुमति देता है, जो कि शिथिलता के साथ आगे के उपयोग के लिए उपयोगी हो सकता है (यह मानते हुए कि आप जिस कार्य को शिथिलता से पारित करते हैं वह इस $ अनुक्रमण का उपयोग करता है)


हम उन्हें फिर से एक साथ कैसे वापस रख सकते हैं? data.frameएकल में सूची की बारी data.frame?
हारून मैकडैड

4
@AaronMcDaid आप do.call और rbind का उपयोग कर सकते हैं: df == do.call ("rbind", ldf)
random_forest_fanatic

@AaronMcDaid या data.table :: rbindlist ()। यदि आपका मूल डेटा फ़्रेम बड़ा था, तो गति लाभ महत्वपूर्ण होगा।
एम्पिरोमैन

8

एक अधिक आधुनिक समाधान केवल उपयोग करता है purrr::transpose:

library(purrr)
iris[1:2,] %>% purrr::transpose()
#> [[1]]
#> [[1]]$Sepal.Length
#> [1] 5.1
#> 
#> [[1]]$Sepal.Width
#> [1] 3.5
#> 
#> [[1]]$Petal.Length
#> [1] 1.4
#> 
#> [[1]]$Petal.Width
#> [1] 0.2
#> 
#> [[1]]$Species
#> [1] 1
#> 
#> 
#> [[2]]
#> [[2]]$Sepal.Length
#> [1] 4.9
#> 
#> [[2]]$Sepal.Width
#> [1] 3
#> 
#> [[2]]$Petal.Length
#> [1] 1.4
#> 
#> [[2]]$Petal.Width
#> [1] 0.2
#> 
#> [[2]]$Species
#> [1] 1

8

मैं इस पर लाखों टिप्पणियों और 35 स्तंभों के साथ एक डेटा.फ्रेम (वास्तव में एक डेटाटेबल) के लिए काम कर रहा था। मेरा लक्ष्य एक पंक्ति के साथ data.frames (data.tables) की एक सूची वापस करना था। यही है, मैं प्रत्येक पंक्ति को एक अलग data.frame में विभाजित करना चाहता था और इन्हें एक सूची में संग्रहीत करता था।

यहाँ दो विधियाँ हैं split(dat, seq_len(nrow(dat))), जो उस डेटा सेट की तुलना में लगभग 3 गुना तेज थीं । नीचे, मैं 7500 पंक्ति पर तीन तरीकों को बेंचमार्क करता हूं, 5 कॉलम डेटा सेट ( आईरिस को 50 बार दोहराया गया)।

library(data.table)
library(microbenchmark)

microbenchmark(
split={dat1 <- split(dat, seq_len(nrow(dat)))},
setDF={dat2 <- lapply(seq_len(nrow(dat)),
                  function(i) setDF(lapply(dat, "[", i)))},
attrDT={dat3 <- lapply(seq_len(nrow(dat)),
           function(i) {
             tmp <- lapply(dat, "[", i)
             attr(tmp, "class") <- c("data.table", "data.frame")
             setDF(tmp)
           })},
datList = {datL <- lapply(seq_len(nrow(dat)),
                          function(i) lapply(dat, "[", i))},
times=20
) 

यह लौटाता है

Unit: milliseconds
       expr      min       lq     mean   median        uq       max neval
      split 861.8126 889.1849 973.5294 943.2288 1041.7206 1250.6150    20
      setDF 459.0577 466.3432 511.2656 482.1943  500.6958  750.6635    20
     attrDT 399.1999 409.6316 461.6454 422.5436  490.5620  717.6355    20
    datList 192.1175 201.9896 241.4726 208.4535  246.4299  411.2097    20

हालांकि मेरे पिछले परीक्षण में अंतर उतना बड़ा नहीं है, setDFअधिकतम (सेटडीएफ) <मिनट (विभाजन) के साथ रनों के वितरण के सभी स्तरों पर सीधी विधि काफी तेज है और attrविधि आम तौर पर दोगुनी से अधिक तेज है।

एक चौथा तरीका चरम चैंपियन है, जो lapplyएक नेस्टेड सूची है, एक नेस्टेड सूची लौटाता है। यह विधि एक सूची से data.frame के निर्माण की लागत का उदाहरण देती है। इसके अलावा, सभी तरीके जो मैंने data.frameफंक्शन के साथ आजमाए , वे data.tableतकनीक की तुलना में मोटे तौर पर धीमी गति के थे ।

डेटा

dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))

6

का मौजूदा संस्करण लगता है purrr(0.2.2) पैकेज सबसे तेज़ समाधान है:

by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out

आइए सबसे दिलचस्प समाधानों की तुलना करें:

data("Batting", package = "Lahman")
x <- Batting[1:10000, 1:10]
library(benchr)
library(purrr)
benchmark(
    split = split(x, seq_len(.row_names_info(x, 2L))),
    mapply = .mapply(function(...) structure(list(...), class = "data.frame", row.names = 1L), x, NULL),
    purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out
)

Rsults:

Benchmark summary:
Time units : milliseconds 
  expr n.eval   min  lw.qu median   mean  up.qu  max  total relative
 split    100 983.0 1060.0 1130.0 1130.0 1180.0 1450 113000     34.3
mapply    100 826.0  894.0  963.0  972.0 1030.0 1320  97200     29.3
 purrr    100  24.1   28.6   32.9   44.9   40.5  183   4490      1.0

इसके अलावा, हम इसके साथ एक ही परिणाम प्राप्त कर सकते हैं Rcpp:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
List df2list(const DataFrame& x) {
    std::size_t nrows = x.rows();
    std::size_t ncols = x.cols();
    CharacterVector nms = x.names();
    List res(no_init(nrows));
    for (std::size_t i = 0; i < nrows; ++i) {
        List tmp(no_init(ncols));
        for (std::size_t j = 0; j < ncols; ++j) {
            switch(TYPEOF(x[j])) {
                case INTSXP: {
                    if (Rf_isFactor(x[j])) {
                        IntegerVector t = as<IntegerVector>(x[j]);
                        RObject t2 = wrap(t[i]);
                        t2.attr("class") = "factor";
                        t2.attr("levels") = t.attr("levels");
                        tmp[j] = t2;
                    } else {
                        tmp[j] = as<IntegerVector>(x[j])[i];
                    }
                    break;
                }
                case LGLSXP: {
                    tmp[j] = as<LogicalVector>(x[j])[i];
                    break;
                }
                case CPLXSXP: {
                    tmp[j] = as<ComplexVector>(x[j])[i];
                    break;
                }
                case REALSXP: {
                    tmp[j] = as<NumericVector>(x[j])[i];
                    break;
                }
                case STRSXP: {
                    tmp[j] = as<std::string>(as<CharacterVector>(x[j])[i]);
                    break;
                }
                default: stop("Unsupported type '%s'.", type2name(x));
            }
        }
        tmp.attr("class") = "data.frame";
        tmp.attr("row.names") = 1;
        tmp.attr("names") = nms;
        res[i] = tmp;
    }
    res.attr("names") = x.attr("row.names");
    return res;
}

अब इसके साथ caompare purrr:

benchmark(
    purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out,
    rcpp = df2list(x)
)

परिणाम:

Benchmark summary:
Time units : milliseconds 
 expr n.eval  min lw.qu median mean up.qu   max total relative
purrr    100 25.2  29.8   37.5 43.4  44.2 159.0  4340      1.1
 rcpp    100 19.0  27.9   34.3 35.8  37.2  93.8  3580      1.0

150 पंक्तियों के एक छोटे से डेटा सेट पर बेंचमार्किंग करने का कोई मतलब नहीं है क्योंकि कोई भी माइक्रोसेकंड में कोई अंतर नहीं
देखेगा

4
by_row()अब चले गए हैंlibrary(purrrlyr)
MrHopko

और इसके अलावा purrrlyr में होने के कारण, यह पदावनत होने वाला है। अब tidyr के संयोजन की अन्य विधियाँ हैं: nest, dplyr :: mutate purrr :: समान परिणाम प्राप्त करने के लिए नक्शा
माइक स्टैनली

3

अधिक विकल्पों की एक जोड़ी:

साथ में asplit

asplit(xy.df, 1)
#[[1]]
#     x      y 
#0.1137 0.6936 

#[[2]]
#     x      y 
#0.6223 0.5450 

#[[3]]
#     x      y 
#0.6093 0.2827 
#....

के साथ splitऔरrow

split(xy.df, row(xy.df)[, 1])

#$`1`
#       x      y
#1 0.1137 0.6936

#$`2`
#       x     y
#2 0.6223 0.545

#$`3`
#       x      y
#3 0.6093 0.2827
#....

डेटा

set.seed(1234)
xy.df <- data.frame(x = runif(10),  y = runif(10))

2

मेरे लिए सबसे अच्छा तरीका था:

उदाहरण डेटा:

Var1<-c("X1",X2","X3")
Var2<-c("X1",X2","X3")
Var3<-c("X1",X2","X3")

Data<-cbind(Var1,Var2,Var3)

ID    Var1   Var2  Var3 
1      X1     X2    X3
2      X4     X5    X6
3      X7     X8    X9

हम BBmiscपुस्तकालय कहते हैं

library(BBmisc)

data$lists<-convertRowsToList(data[,2:4])

और परिणाम होगा:

ID    Var1   Var2  Var3  lists
1      X1     X2    X3   list("X1", "X2", X3") 
2      X4     X5    X6   list("X4","X5", "X6") 
3      X7     X8    X9   list("X7,"X8,"X9) 

1

एक वैकल्पिक तरीका यह है कि df को एक मैट्रिक्स में परिवर्तित किया जाए और फिर उस पर सूची लागू करने वाले lappyफ़ंक्शन को लागू किया जाए:ldf <- lapply(as.matrix(myDF), function(x)x)


1

एक अन्य विकल्प का उपयोग करना library(purrr)(जो बड़े data.frames पर थोड़ा तेज लगता है)

flatten(by_row(xy.df, ..f = function(x) flatten_chr(x), .labels = FALSE))

3
`by_row ()` अब `लाइब्रेरी (purrrlyr)`
MrHopko

1

जैसे @flodel ने लिखा: यह आपके डेटाफ़्रेम को एक ऐसी सूची में परिवर्तित करता है, जिसमें डेटाफ़्रेम में पंक्तियों की संख्या के समान तत्व होते हैं:

NewList <- split(df, f = seq(nrow(df)))

आप केवल उन स्तंभों का चयन करने के लिए एक फ़ंक्शन जोड़ सकते हैं जो सूची के प्रत्येक तत्व में NA नहीं हैं :

NewList2 <- lapply(NewList, function(x) x[,!is.na(x)])

0

by_rowसे समारोह purrrlyrपैकेज आपके लिए यह कार्य होगा।

यह उदाहरण प्रदर्शित करता है

myfn <- function(row) {
  #row is a tibble with one row, and the same number of columns as the original df
  l <- as.list(row)
  return(l)
}

list_of_lists <- purrrlyr::by_row(df, myfn, .labels=FALSE)$.out

डिफ़ॉल्ट रूप से, से दिए गए मान को df नामक myfnनए सूची कॉलम में डाल दिया जाता है .out$.outऊपर बयान के अंत में तुरंत इस स्तंभ का चयन करता है, सूची की एक सूची लौट रहा।

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