2016-09-17 20 views
8
df %>% split(.$x) 

एक्स के अनूठे मूल्यों की बड़ी संख्या के लिए धीमा हो जाता है। यदि हम डेटा फ्रेम को मैन्युअल रूप से छोटे सबसेट में विभाजित करते हैं और फिर प्रत्येक सबसेट पर विभाजित करते हैं तो हम कम से कम परिमाण के आदेश को कम करते हैं।कई समूहों के साथ बड़े डेटा फ्रेम पर विभाजित क्यों अक्षम है?

library(dplyr) 
library(microbenchmark) 
library(caret) 
library(purrr) 

N  <- 10^6 
groups <- 10^5 
df  <- data.frame(x = sample(1:groups, N, replace = TRUE), 
        y = sample(letters, N, replace = TRUE)) 
ids  <- df$x %>% unique 
folds10 <- createFolds(ids, 10) 
folds100 <- createFolds(ids, 100) 

रनिंग microbenchmark हमें

## Unit: seconds 

## expr             mean 
l1 <- df %>% split(.$x)        # 242.11805 

l2 <- lapply(folds10, function(id) df %>% 
     filter(x %in% id) %>% split(.$x)) %>% flatten # 50.45156 

l3 <- lapply(folds100, function(id) df %>% 
     filter(x %in% id) %>% split(.$x)) %>% flatten # 12.83866 

split बड़े समूहों के लिए नहीं बनाया गया है देता है? क्या मैन्युअल प्रारंभिक सबसेटिंग के अलावा कोई विकल्प हैं?

मेरे लैपटॉप, एक मैकबुक प्रो 2013 में है 2.4GHz 8GB

+0

मैं समानांतर में जिसके परिणामस्वरूप सूची आइटम, यानी 'list_of_dataframes%>% नक्शा (sequentially_process_each_row_of_df) को संसाधित करना चाहते हैं' – Rickard

+0

, पर विचार भी, '' 'df' split'ting से पहले order'ing, ताकि' .internal (विभाजन()) 'स्मृति को लगातार एक्सेस करता है -' system.time ({a = split (df, df $ x)}); system.time ({odf = df [ऑर्डर (डीएफ $ एक्स),]; बी = विभाजन (odf, odf $ x)}); समान (ए, बी) ' –

+0

@alexis_laz वास्तव में, मेमोरी एक्सेस पैटर्न में सुधार करने के बजाए पंक्ति नाम बनाता है -' .row_names_info (df) 'और' .row_names_info (df [order (df $ x),] की तुलना करें) ' ; पहले मामले में ऋणात्मक मूल्य इंगित करता है कि पंक्ति नामों को 'सी (एनए, 1000000)' के रूप में कॉम्पैक्टली रूप से संग्रहीत किया जाता है, दूसरे मामले में सकारात्मक मूल्य जिसे वे सचमुच एक पूर्णांक वेक्टर के रूप में संग्रहीत किया जाता है। –

उत्तर

2

यह सख्ती से split.data.frame समस्या नहीं है, कई समूहों के लिए डेटा.फ्रेम की स्केलेबिलिटी पर एक और सामान्य समस्या है।
यदि आप split.data.table का उपयोग करते हैं तो आप बहुत अच्छी गति प्राप्त कर सकते हैं। मैंने इस विधि को नियमित डेटा.table तरीकों के शीर्ष पर विकसित किया है और यह यहां बहुत अच्छी तरह से स्केल करने लगता है।

system.time(
    l1 <- df %>% split(.$x) 
) 
# user system elapsed 
#200.936 0.000 217.496 
library(data.table) 
dt = as.data.table(df) 
system.time(
    l2 <- split(dt, by="x") 
) 
# user system elapsed 
# 7.372 0.000 6.875 
system.time(
    l3 <- split(dt, by="x", sorted=TRUE) 
) 
# user system elapsed 
# 9.068 0.000 8.200 

sorted=TRUE data.frame पद्धति के रूप में एक ही क्रम की सूची वापस आ जाएगी, डिफ़ॉल्ट data.table विधि द्वारा आदेश इनपुट डेटा में मौजूद सुरक्षित करेगा। यदि आप डेटा.फ्रेम पर चिपकना चाहते हैं तो आप अंत में lapply(l2, setDF) का उपयोग कर सकते हैं।

पीएस। split.data.table 1.9 में जोड़ा गया था।7, devel संस्करण की स्थापना बहुत सरल Installation wiki में उस के बारे में

install.packages("data.table", type="source", repos="http://Rdatatable.github.io/data.table") 

अधिक है।

+1

split.data.table काफी तेजी से था। मैं data.table का उपयोग कर अपने कोड के कुछ हिस्सों को फिर से लिखना समाप्त कर दिया। – Rickard

8

अधिक एक जवाब से एक विवरण। एक बड़े data.frame उप-स्थापना के एक छोटे से डेटा फ्रेम

> df100 = df[1:100,] 
> idx = c(1, 10, 20) 
> microbenchmark(df[idx,], df100[idx,], times=10) 
Unit: microseconds 
     expr  min  lq  mean median  uq  max neval 
    df[idx, ] 428.921 441.217 445.3281 442.893 448.022 475.364 10 
df100[idx, ] 32.082 32.307 35.2815 34.935 37.107 42.199 10 

split() प्रत्येक समूह के लिए इस लागत का भुगतान करती है से उप-स्थापित करने की तुलना में अधिक महंगा है।

कारण Rprof()

> Rprof(); for (i in 1:1000) df[idx,]; Rprof(NULL); summaryRprof() 
$by.self 
     self.time self.pct total.time total.pct 
"attr"  1.26  100  1.26  100 

$by.total 
       total.time total.pct self.time self.pct 
"attr"    1.26  100  1.26  100 
"[.data.frame"  1.26  100  0.00  0 
"["     1.26  100  0.00  0 

$sample.interval 
[1] 0.02 

$sampling.time 
[1] 1.26 

समय के सभी attr() के लिए एक कॉल में खर्च किया जा रहा है चलाकर देखा जा सकता है। debug("[.data.frame") का उपयोग कर कोड के माध्यम से कदम से पता चलता है कि दर्द

attr(df, "row.names") 

यह छोटा सा उदाहरण की तरह एक फोन शामिल है एक चाल है कि आर पंक्ति ऐसे नाम हैं जो मौजूद नहीं हैं का प्रतिनिधित्व करने से बचने के लिए उपयोग करता है पता चलता है: c(NA, -5L) उपयोग करते हैं, बल्कि 1:5 से।

> dput(data.frame(x=1:5)) 
structure(list(x = 1:5), .Names = "x", row.names = c(NA, -5L), class = "data.frame") 

ध्यान दें कि attr() एक वेक्टर रिटर्न - row.names मक्खी पर बनाई गई हैं, और एक बड़े data.frame के लिए row.names की एक बड़ी संख्या बनाई गई हैं

> attr(data.frame(x=1:5), "row.names") 
[1] 1 2 3 4 5 

तो एक उम्मीद कर सकते हैं कि यहां तक ​​कि अतर्कसंगत row.names गणना

> dfns = df; rownames(dfns) = rev(seq_len(nrow(dfns))) 
> system.time(split(dfns, dfns$x)) 
    user system elapsed 
    4.048 0.000 4.048 
> system.time(split(df, df$x)) 
    user system elapsed 
87.772 16.312 104.100 

विभाजन एक सदिश गति होगा या मैट्रिक्स भी तेजी से होगा।

संबंधित मुद्दे