data.table पर dplyr, क्या मैं वास्तव में data.table का उपयोग कर रहा हूं?


91

अगर मैं का उपयोग dplyr एक की चोटी पर वाक्य रचना datatable , मैं जबकि अभी भी dplyr का सिंटैक्स का उपयोग datatable के सभी गति लाभ मिलता है? दूसरे शब्दों में, क्या मैं डिटैचेबल का गलत उपयोग करता हूं यदि मैं इसे dplyr सिंटैक्स के साथ क्वेरी करता हूं? या क्या मुझे इसकी सभी शक्ति का उपयोग करने के लिए शुद्ध डेटासेट सिंटैक्स का उपयोग करने की आवश्यकता है।

किसी भी सलाह के लिए अग्रिम धन्यवाद। कोड उदाहरण:

library(data.table)
library(dplyr)

diamondsDT <- data.table(ggplot2::diamonds)
setkey(diamondsDT, cut) 

diamondsDT %>%
    filter(cut != "Fair") %>%
    group_by(cut) %>%
    summarize(AvgPrice = mean(price),
                 MedianPrice = as.numeric(median(price)),
                 Count = n()) %>%
    arrange(desc(Count))

परिणाम:

#         cut AvgPrice MedianPrice Count
# 1     Ideal 3457.542      1810.0 21551
# 2   Premium 4584.258      3185.0 13791
# 3 Very Good 3981.760      2648.0 12082
# 4      Good 3928.864      3050.5  4906

यहाँ मैं जिस समतुल्य समानता के साथ आया हूँ। सुनिश्चित नहीं है कि यह डीटी अच्छे अभ्यास का अनुपालन करता है। लेकिन मुझे आश्चर्य है कि अगर कोड वास्तव में दृश्य के पीछे dplyr सिंटैक्स से अधिक कुशल है:

diamondsDT [cut != "Fair"
        ] [, .(AvgPrice = mean(price),
                 MedianPrice = as.numeric(median(price)),
                 Count = .N), by=cut
        ] [ order(-Count) ]

7
आप डेटा टेबल सिंटैक्स का उपयोग क्यों नहीं करेंगे? यह सुरुचिपूर्ण और कुशल है। सवाल वास्तव में जवाबदेह नहीं है क्योंकि यह बहुत व्यापक है। हां, dplyrडेटा टेबल के लिए तरीके हैं , लेकिन डेटा टेबल के अपने स्वयं के तुलनीय तरीके भी हैं
रिच स्क्रिपवेन

7
मैं डेटाटेबल सिंटैक्स या कोर्स का उपयोग कर सकता हूं। लेकिन किसी भी तरह, मैं dplyr वाक्यविन्यास अधिक सुंदर लगता है। सिंटैक्स के लिए मेरी प्राथमिकता के बावजूद। जो मैं वास्तव में जानना चाहता हूं वह यह है: क्या मुझे डाटिटेबल पावर का 100% लाभ प्राप्त करने के लिए शुद्ध डेटाटेबल सिंटैक्स का उपयोग करने की आवश्यकता है।
पोलीमरेज़

3
हाल के बेंचमार्क के लिए जहां s और संबंधित s dplyrपर उपयोग किया जाता है , यहां देखें (और इसमें संदर्भ)। data.framedata.table
हेनरिक

2
@ पॉलीमरेज़ - मुझे लगता है कि इस सवाल का जवाब निश्चित रूप से "हां" है
रिच स्क्रिप्‍न

1
@ हेनरिक: मुझे बाद में एहसास हुआ कि मैंने उस पृष्ठ की गलत व्याख्या की थी, क्योंकि उन्होंने केवल डेटाफ़्रेम निर्माण के लिए कोड प्रदर्शित किया था, लेकिन वे कोड नहीं जिनका उपयोग उन्होंने डेटाटेबल निर्माण के लिए किया था। जब मुझे इसका एहसास हुआ, तो मैंने अपनी टिप्पणी हटा दी (आशा है कि आपने इसे नहीं देखा होगा)।
IRTFM

जवाबों:


77

कोई सीधा / सरल उत्तर नहीं है क्योंकि इन दोनों पैकेजों के दर्शन कुछ पहलुओं में भिन्न हैं। तो कुछ समझौते अपरिहार्य हैं। यहां कुछ चिंताओं के बारे में बताया गया है, जिन पर आपको ध्यान देना चाहिए।

संचालन i(== filter()और slice()dplyr में)

मान लें DTकि 10 कॉलम हैं। इन डेटा पर विचार करें। अभिव्यक्ति:

DT[a > 1, .N]                    ## --- (1)
DT[a > 1, mean(b), by=.(c, d)]   ## --- (2)

(1) DTजहां कॉलम में पंक्तियों की संख्या देता है a > 1। (2) के रूप में (1) में एक ही अभिव्यक्ति के लिए mean(b)समूहीकृत रिटर्न ।c,di

आमतौर पर इस्तेमाल किए जाने वाले dplyrभाव होंगे:

DT %>% filter(a > 1) %>% summarise(n())                        ## --- (3) 
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b)) ## --- (4)

स्पष्ट रूप से, डेटाटेबल कोड छोटे होते हैं। इसके अलावा वे भी अधिक स्मृति कुशल 1 हैं । क्यों? क्योंकि (3) और (4) दोनों में, पहले सभी 10 कॉलमों के लिए पंक्तियों कोfilter() लौटाता है , जब (3) हमें बस पंक्तियों की संख्या की आवश्यकता होती है, और (4) हमें बस क्रमिक संचालन के लिए कॉलमों की आवश्यकता होती है । इसे दूर करने के लिए, हमें एप्रीरी को कॉलम करना होगा:b, c, dselect()

DT %>% select(a) %>% filter(a > 1) %>% summarise(n()) ## --- (5)
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b)) ## --- (6)

दो पैकेजों के बीच एक प्रमुख दार्शनिक अंतर को उजागर करना आवश्यक है:

  • में data.table, हम इन संबंधित कार्यों को एक साथ रखना पसंद करते हैं, और यह j-expression(एक ही फ़ंक्शन कॉल से) को देखने की अनुमति देता है और महसूस करता है कि (1) में किसी भी कॉलम की कोई आवश्यकता नहीं है। अभिव्यक्ति की iगणना हो जाती है, और .Nकेवल उस तार्किक वेक्टर का योग होता है जो पंक्तियों की संख्या देता है; पूरे सबसेट का एहसास कभी नहीं होता है। (2) b,c,dमें, उप-कॉलम में सिर्फ कॉलम को भौतिक किया जाता है, अन्य स्तंभों को अनदेखा किया जाता है।

  • लेकिन dplyr, दर्शन में एक कार्य को अच्छी तरह से ठीक करना है । यह बताने का कोई तरीका नहीं है (कम से कम वर्तमान में) यह बताने का कोई तरीका नहीं है कि ऑपरेशन के बाद filter()उन सभी कॉलमों की आवश्यकता है जिन्हें हमने फ़िल्टर किया था। यदि आप ऐसे कार्यों को कुशलतापूर्वक करना चाहते हैं तो आपको आगे सोचने की आवश्यकता होगी। मैं व्यक्तिगत रूप से इसे इस मामले में प्रति-संवेदनशील मानता हूं।

ध्यान दें कि (5) और (6) में, हम अभी भी कॉलम को सब्मिट aकरते हैं, जिसकी हमें आवश्यकता नहीं है। लेकिन मुझे यकीन नहीं है कि इससे कैसे बचा जाए। यदि filter()फ़ंक्शन के पास लौटने के लिए कॉलम का चयन करने का तर्क था, तो हम इस मुद्दे से बच सकते हैं, लेकिन फिर फ़ंक्शन केवल एक कार्य नहीं करेगा (जो कि एक dplyr डिजाइन विकल्प भी है)।

संदर्भ द्वारा उप-असाइन करें

dplyr संदर्भ द्वारा कभी अद्यतन नहीं होगा । यह दो पैकेजों के बीच एक और बड़ा (दार्शनिक) अंतर है।

उदाहरण के लिए, data.table में आप कर सकते हैं:

DT[a %in% some_vals, a := NA]

जो उन पंक्तियों पर a संदर्भ द्वारा कॉलम अद्यतन करता है जो स्थिति को संतुष्ट करते हैं। इस समय पूरी तरह से एक नया कॉलम जोड़ने के लिए आंतरिक रूप से संपूर्ण डेटा को कॉपी करता है। @ ब्रॉडीजी ने पहले ही अपने जवाब में इसका उल्लेख किया।

लेकिन FR # 617 लागू होने पर गहरी प्रति को उथली प्रति से बदला जा सकता है। इसके अलावा प्रासंगिक: dplyr: एफआर # 614 । ध्यान दें कि फिर भी, आपके द्वारा संशोधित किया गया कॉलम हमेशा कॉपी किया जाएगा (इसलिए टैड धीमी / कम मेमोरी कुशल)। संदर्भ द्वारा कॉलम अपडेट करने का कोई तरीका नहीं होगा।

अन्य कार्यशीलता

  • Data.table में, आप शामिल होने के दौरान एकत्र कर सकते हैं, और यह समझने के लिए अधिक सीधा है और मेमोरी कुशल है क्योंकि इंटरमीडिएट जॉइन परिणाम कभी भी भौतिक नहीं होता है। एक उदाहरण के लिए इस पोस्ट की जाँच करें । आप (समय पर?) नहीं कर सकते कि dplyr के data.table / data.frame वाक्यविन्यास का उपयोग कर।

  • data.table का रोलिंग जॉइन फीचर dplyr के सिंटैक्स में भी समर्थित नहीं है।

  • हमने हाल ही में डेटाटैब पर ओवरलैप जोड़ दिए हैं। अंतराल रेंज ( यहां एक उदाहरण ) में शामिल होने के लिए , जो foverlaps()इस समय एक अलग फ़ंक्शन है , और इसलिए इसे पाइप ऑपरेटरों (मैग्रीट्र / पाइपआर के साथ इस्तेमाल किया जा सकता है? - कभी भी इसे खुद करने की कोशिश नहीं की गई)।

    लेकिन आखिरकार, हमारा लक्ष्य इसे एकीकृत करना है [.data.tableताकि हम अन्य सुविधाओं जैसे कि समूह बनाना, जुड़ते समय एकत्र करना आदि की कटाई कर सकें .. जिसमें ऊपर उल्लिखित समान सीमाएं होंगी।

  • 1.9.4 के बाद से, डेटाटेबल नियमित आर सिंटैक्स पर तेजी से द्विआधारी खोज आधारित सबसेट के लिए माध्यमिक कुंजी का उपयोग करके स्वचालित अनुक्रमण को लागू करता है। Ex: DT[x == 1]और DT[x %in% some_vals]पहले रन पर स्वचालित रूप से एक इंडेक्स बनाएगा, जिसे बाद में बाइनरी सर्च का उपयोग करते हुए एक ही कॉलम से फास्ट सब्सेट पर क्रमिक सबसेट पर उपयोग किया जाएगा। यह सुविधा विकसित होती रहेगी। इस सुविधा के एक संक्षिप्त अवलोकन के लिए इस जिस्ट की जाँच करें

    जिस तरह filter()से data.tables के लिए कार्यान्वित किया जाता है, वह इस सुविधा का लाभ नहीं उठाता है।

  • एक dplyr विशेषता यह है कि यह उसी सिंटैक्स का उपयोग करके डेटाबेस को इंटरफ़ेस भी प्रदान करता है , जो डेटाटेबल फिलहाल नहीं है।

तो, आपको इन (और शायद अन्य बिंदुओं) में तौलना होगा और यह तय करना होगा कि ये व्यापार-बंद आपको स्वीकार्य हैं या नहीं।

HTH


(1) ध्यान दें कि मेमोरी कुशल होना सीधे गति को प्रभावित करता है (विशेषकर डेटा बड़ा हो जाता है), क्योंकि ज्यादातर मामलों में अड़चन मुख्य मेमोरी से कैश पर डेटा को स्थानांतरित कर रही है (और जितना संभव हो उतना कैश में डेटा का उपयोग करना - कैश मिस को कम करना - ताकि मुख्य मेमोरी तक पहुंच कम हो सके)। यहां विवरण में नहीं जा रहा है।


4
बिल्कुल प्रतिभाशाली। इसके लिए धन्यवाद
डेविड एरेन्बर्ग

6
यह एक अच्छा जवाब है, लेकिन यह होगा संभव (यदि संभावना नहीं) एक कुशल लागू करने के लिए dplyr के लिए filter()प्लस summarise()एक ही दृष्टिकोण है कि एसक्यूएल के लिए dplyr का उपयोग करता है का उपयोग कर - यानी एक अभिव्यक्ति के निर्माण और उसके बाद ही मांग पर एक बार क्रियान्वित। इसकी संभावना नहीं है कि इसे निकट भविष्य में लागू किया जाएगा क्योंकि मेरे लिए duspr काफी तेज है और क्वेरी प्लानर / ऑप्टिमाइज़र को लागू करना अपेक्षाकृत कठिन है।
हदी

मेमोरी कुशल होने से दूसरे महत्वपूर्ण क्षेत्र में भी मदद मिलती है - वास्तव में मेमोरी से बाहर निकलने से पहले कार्य पूरा करना। जब बड़े डेटासेट्स के साथ काम करते हुए मैंने उस मुद्दे का सामना dplyr के साथ-साथ पांडा के साथ किया है, जबकि data.table कार्य को इनायत से पूरा करेगा।
जकी

25

कर के देखो।

library(rbenchmark)
library(dplyr)
library(data.table)

benchmark(
dplyr = diamondsDT %>%
    filter(cut != "Fair") %>%
    group_by(cut) %>%
    summarize(AvgPrice = mean(price),
                 MedianPrice = as.numeric(median(price)),
                 Count = n()) %>%
    arrange(desc(Count)),
data.table = diamondsDT[cut != "Fair", 
                        list(AvgPrice = mean(price),
                             MedianPrice = as.numeric(median(price)),
                             Count = .N), by = cut][order(-Count)])[1:4]

इस समस्या पर ऐसा लगता है कि data.table डेटा के उपयोग से dplyr से 2.4 गुना तेज़ है।

        test replications elapsed relative
2 data.table          100    2.39    1.000
1      dplyr          100    5.77    2.414

पॉलिमर की टिप्पणी के आधार पर संशोधित


2
microbenchmarkपैकेज का उपयोग करते हुए , मैंने पाया कि ओपी के dplyrकोड को मूल (डेटा फ़्रेम) संस्करण पर चलाकर diamonds0.012 सेकंड का औसत समय लिया गया, जबकि diamondsडेटा तालिका में परिवर्तित होने के बाद यह 0.024 सेकंड का औसत समय लगा । चल रहे जी। ग्रोथेंडिक के data.tableकोड ने 0.013 सेकंड का समय लिया। कम से कम मेरे सिस्टम पर, यह जैसा दिखता है dplyrऔर data.tableउसी प्रदर्शन के बारे में है। लेकिन dplyrजब डेटा फ्रेम को पहले डेटा टेबल में बदल दिया जाता है तो यह धीमा क्यों होगा ?
eipi10

प्रिय जी। ग्रोथेंडिक, यह अद्भुत है। मुझे इस बेंचमार्क उपयोगिता को दिखाने के लिए धन्यवाद। BTW आप भूल गए [आदेश (-Count)] डेटाप्रेर संस्करण में dplyr की व्यवस्था (desc (Count)) के समतुल्य बनाने के लिए। इसे जोड़ने के बाद, datatable अभी भी X1.8 (2.9 के बजाय) के द्वारा तेज है।
पोलीमरेज़

@ eipi10 आप अपनी बेंच को फिर से डिटैटेबल वर्जन के साथ यहाँ फिर से जोड़ सकते हैं (अंतिम चरण में desc count द्वारा जोड़ा गया): डायमंड्स डीटीटी [कट! = "फेयर", लिस्ट (औसत मूल्य = माध्य) (कीमत), मेडियनप्रीस = as.numeric (माध्यिका) (कीमत)), गणना = .N), = कटौती] [आदेश (-Count)] द्वारा
पोलीमरेज़

फिर भी 0.013 सेकंड। ऑर्डरिंग ऑपरेशन में मुश्किल से कोई समय लगता है क्योंकि यह केवल अंतिम तालिका को फिर से व्यवस्थित कर रहा है, जिसमें केवल चार पंक्तियाँ हैं।
eipi10

1
Dplyr सिंटैक्स से डेटा टेबल सिंटैक्स में रूपांतरण के लिए कुछ निश्चित ओवरहेड है, इसलिए यह समस्या के अलग-अलग आकारों को आज़माने के लायक हो सकता है। इसके अलावा, मैंने dplyr में सबसे कुशल डेटा टेबल कोड लागू नहीं किया है; पैच का हमेशा स्वागत किया जाता है
हैडले

22

अपने सवालों के जवाब देने के लिए:

  • हां, आप उपयोग कर रहे हैं data.table
  • लेकिन उतनी कुशलता से नहीं जितना कि आप शुद्ध data.tableवाक्य रचना के साथ करेंगे

कई मामलों में यह dplyrवाक्यविन्यास चाहने वालों के लिए एक स्वीकार्य समझौता होगा, हालांकि यह संभवतः dplyrसादे डेटा फ्रेम की तुलना में धीमा होगा ।

एक बड़ा कारक ऐसा प्रतीत होता है जो समूहन करते समय डिफ़ॉल्ट रूप dplyrसे कॉपी हो जाएगा data.table। विचार करें (माइक्रोबेनमार्क का उपयोग करके):

Unit: microseconds
                                                               expr       min         lq    median
                                diamondsDT[, mean(price), by = cut]  3395.753  4039.5700  4543.594
                                          diamondsDT[cut != "Fair"] 12315.943 15460.1055 16383.738
 diamondsDT %>% group_by(cut) %>% summarize(AvgPrice = mean(price))  9210.670 11486.7530 12994.073
                               diamondsDT %>% filter(cut != "Fair") 13003.878 15897.5310 17032.609

फ़िल्टरिंग तुलनीय गति की है, लेकिन समूहीकरण नहीं है। मेरा मानना ​​है कि अपराधी इस पंक्ति में है dplyr:::grouped_dt:

if (copy) {
    data <- data.table::copy(data)
}

जहां copyचूक TRUE(और आसानी से FALSE को बदला नहीं जा सकता है जो मैं देख सकता हूं)। यह संभावना अंतर के 100% के लिए जिम्मेदार नहीं है, लेकिन सामान्य ओवरहेड कुछ पर अकेले diamondsसबसे अधिक संभावना का आकार पूर्ण अंतर नहीं है।

मुद्दा यह है कि एक सुसंगत व्याकरण रखने के लिए, dplyrदो चरणों में समूहन करता है। यह पहले समूह से मेल खाने वाली मूल डेटा तालिका की एक प्रति पर चाबियाँ सेट करता है, और बाद में केवल समूह करता है। data.tableबस सबसे बड़े परिणाम समूह के लिए मेमोरी आवंटित करता है, जो इस मामले में सिर्फ एक पंक्ति है, जिससे यह पता चलता है कि कितनी मेमोरी को आवंटित करने की आवश्यकता है।

FYI करें, अगर किसी को परवाह है, तो मैंने इसका उपयोग करके पाया treeprof( install_github("brodieg/treeprof")), Rprofआउटपुट के लिए एक प्रायोगिक (और अभी भी बहुत अल्फा) ट्री दर्शक :

यहाँ छवि विवरण दर्ज करें

ऊपर ध्यान दें वर्तमान में केवल macs AFAIK पर काम करता है। इसके अलावा, दुर्भाग्य से, Rprofटाइप के कॉल packagename::funnameको गुमनाम के रूप में दर्ज किया गया है ताकि यह वास्तव में कोई भी हो और datatable::अंदर की सभी कॉल grouped_dtजिम्मेदार हों, लेकिन त्वरित परीक्षण से यह ऐसा दिखता थाdatatable::copy यह बड़ा है।

उस ने कहा, आप जल्दी से देख सकते हैं कि [.data.tableकॉल के आस-पास कितना ओवरहेड नहीं है , लेकिन समूहीकरण के लिए एक पूरी तरह से अलग शाखा भी है।


संपादित करें : प्रतिलिपि की पुष्टि करने के लिए:

> tracemem(diamondsDT)
[1] "<0x000000002747e348>"    
> diamondsDT %>% group_by(cut) %>% summarize(AvgPrice = mean(price))
tracemem[0x000000002747e348 -> 0x000000002a624bc0]: <Anonymous> grouped_dt group_by_.data.table group_by_ group_by <Anonymous> freduce _fseq eval eval withVisible %>% 
Source: local data table [5 x 2]

        cut AvgPrice
1      Fair 4358.758
2      Good 3928.864
3 Very Good 3981.760
4   Premium 4584.258
5     Ideal 3457.542
> diamondsDT[, mean(price), by = cut]
         cut       V1
1:     Ideal 3457.542
2:   Premium 4584.258
3:      Good 3928.864
4: Very Good 3981.760
5:      Fair 4358.758
> untracemem(diamondsDT)

यह बहुत बढ़िया है, धन्यवाद। क्या इसका मतलब है, dplyr :: group_by () आंतरिक डेटा कॉपी चरण की वजह से मेमोरी आवश्यकता (शुद्ध डेटाटेबल सिंटैक्स की तुलना में) को दोगुना कर देगा? मतलब अगर मेरी डेटाटेबल ऑब्जेक्ट का आकार 1GB है, और मैं मूल पोस्ट में एक के समान dplyr जंजीर सिंटैक्स का उपयोग करता हूं। परिणाम प्राप्त करने के लिए मुझे कम से कम 2GB मुफ्त मेमोरी की आवश्यकता होगी?
पोलीमरेज़

2
मुझे लगता है कि मैंने देव संस्करण में तय किया है?
हैडले

@ अहदले, मैं CRAN संस्करण से काम कर रहा था। देव को देखकर, ऐसा लगता है कि आपने समस्या को आंशिक रूप से संबोधित किया है, लेकिन वास्तविक प्रतिलिपि बनी हुई है (परीक्षण नहीं किया गया है, बस आर / समूहीकृत- dt.r में सी (20, 30:32) लाइनों को देख रहा है। यह शायद अब और तेज़ है, लेकिन मुझे लगता है कि धीमे कदम की नकल है।
ब्रॉडी जी

3
मैं data.table में उथले कॉपी फ़ंक्शन का भी इंतजार कर रहा हूं; तब तक मुझे लगता है कि तेजी से सुरक्षित रहना बेहतर है।
हैडले

2

आप उपयोग कर सकते हैं dtplyr अब है, जो का हिस्सा है tidyverse । यह आपको सामान्य रूप से dplyr शैली कथनों का उपयोग करने की अनुमति देता है, लेकिन आलसी मूल्यांकन का उपयोग करता है और हुड के तहत data.table कोड में अपने बयानों का अनुवाद करता है। अनुवाद में ओवरहेड न्यूनतम है, लेकिन आप सभी प्राप्त करते हैं, यदि नहीं, तो डेटा का अधिकांश लाभ। आधिकारिक git रेपो में अधिक जानकारी यहाँ और साफ पृष्ठ पर

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