data.table बनाम dplyr: क्या कोई कुछ अच्छी तरह से कर सकता है अन्य कोई खराब नहीं कर सकता या करता है?




(2)

अवलोकन

मैं dplyr . dplyr साथ अपेक्षाकृत परिचित हूँ, data.table साथ इतना ज्यादा नहीं। मैंने कुछ dplyr विगेट्स और उदाहरणों के माध्यम से पढ़ा है जो SO पर पॉप अप हो गए हैं, और अब तक मेरे निष्कर्ष यह हैं कि:

  1. data.table और dplyr गति में तुलनीय हैं, सिवाय इसके कि जब कई (यानी> 10-100 के) समूह होते हैं, और कुछ अन्य परिस्थितियों में (नीचे बेंचमार्क देखें)
  2. dplyr अधिक सुलभ वाक्यविन्यास है
  3. dplyr abstracts (या इच्छा) संभावित डीबी इंटरैक्शन
  4. कुछ मामूली कार्यक्षमता अंतर हैं (नीचे "उदाहरण / उपयोग" देखें)

मेरे दिमाग में 2. बहुत अधिक भार नहीं लेता है क्योंकि मैं data.table साथ काफी परिचित data.table , हालांकि मैं समझता हूं कि उपयोगकर्ताओं के लिए यह नया एक बड़ा कारक होगा। मैं इस बारे में एक तर्क से बचना चाहता हूं कि अधिक सहज ज्ञान युक्त है, क्योंकि यह मेरे विशिष्ट प्रश्न के लिए अप्रासंगिक है जिसे data.table पहले से परिचित किसी के परिप्रेक्ष्य से पूछा गया है। मैं इस बारे में एक चर्चा से बचना चाहता हूं कि कैसे "अधिक अंतर्ज्ञानी" तेजी से विश्लेषण की ओर जाता है (निश्चित रूप से सच है, लेकिन फिर से, जो मुझे यहां सबसे ज्यादा दिलचस्पी नहीं है)।

सवाल

मैं क्या जानना चाहता हूं:

  1. क्या ऐसे विश्लेषणात्मक कार्य हैं जो संकुल से परिचित लोगों के लिए एक या दूसरे पैकेज के साथ कोड करना बहुत आसान होते हैं (यानी कुंजीस्ट्रोक के कुछ संयोजन बनाम अनिवार्यता के आवश्यक स्तर बनाम, जहां प्रत्येक में से कम एक अच्छी बात है)।
  2. क्या विश्लेषणात्मक कार्य हैं जो एक पैकेज बनाम एक दूसरे में काफी कुशलतापूर्वक (यानी 2x से अधिक) प्रदर्शन किए जाते हैं।

हाल ही में एक सवाल ने मुझे इसके बारे में कुछ और सोचने के लिए dplyr किया, क्योंकि उस बिंदु तक मुझे नहीं लगता था कि dplyr में पहले से ही क्या कर सकता है उससे काफी कुछ प्रदान करेगा। यहां dplyr समाधान (क्यू के अंत में डेटा) है:

dat %.%
  group_by(name, job) %.%
  filter(job != "Boss" | year == min(year)) %.%
  mutate(cumu_job2 = cumsum(job2))

जो data.table समाधान पर मेरे हैक प्रयास से काफी बेहतर था। उस ने कहा, अच्छे data.table समाधान भी बहुत अच्छे हैं (धन्यवाद जीन-रॉबर्ट, अरुण, और यहां ध्यान दें कि मैंने कड़ाई से सबसे इष्टतम समाधान पर एकल कथन का पक्ष लिया है):

setDT(dat)[,
  .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], 
  by=list(id, job)
]

उत्तरार्द्ध के लिए वाक्यविन्यास बहुत गूढ़ प्रतीत हो सकता है, लेकिन यदि आप data.table (यानी कुछ अधिक गूढ़ चाल का उपयोग नहीं करते हैं) का उपयोग किया जाता है तो यह वास्तव में बहुत सरल है।

आदर्श रूप से मैं जो देखना चाहता हूं वह कुछ अच्छे उदाहरण हैं जो dplyr या data.table तरीका काफी अधिक संक्षिप्त है या काफी बेहतर प्रदर्शन करता है।

उदाहरण

प्रयोग
  • dplyr समूहबद्ध संचालन की अनुमति नहीं देता है जो पंक्तियों की मनमानी संख्या ( eddi के प्रश्न से , नोट: ऐसा लगता है कि यह dplyr 0.5 में लागू किया जाएगा, @beginneR @ eddi के प्रश्न के उत्तर में एक संभावित कार्य-आसपास दिखाता है) ।
  • data.table रोलिंग data.table का समर्थन करता है (धन्यवाद @ डोलस्टियस) साथ ही ओवरलैप ओवरलैप करता है
  • data.table आंतरिक इंडेक्सिंग के माध्यम से गति के लिए आंतरिक रूप से DT[col == value] या DT[col %in% values] अभिव्यक्ति को अनुकूलित करता है जो समान आधार आर सिंटैक्स का उपयोग करते समय बाइनरी खोज का उपयोग करता है। कुछ और विवरण और एक छोटे बेंचमार्क के लिए यहां देखें
  • dplyr कार्यों के मानक मूल्यांकन संस्करण प्रदान करता है (उदाहरण के लिए dplyr , summarize_each_ ) जो dplyr के प्रोग्रामेटिक उपयोग को सरल बना सकता है ( dplyr नोट प्रोग्रामेटिक उपयोग निश्चित रूप से संभव है, कम से कम मेरे ज्ञान के लिए कुछ सावधान विचार, प्रतिस्थापन / उद्धरण आदि की आवश्यकता है )
मानक

डेटा

प्रश्न अनुभाग में दिखाए गए पहले उदाहरण के लिए यह है।

dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane", 
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob", 
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L, 
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L, 
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager", 
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager", 
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L, 
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id", 
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA, 
-16L))

प्रश्न शीर्षक के सीधी प्रतिक्रिया में ...

dplyr निश्चित रूप से चीजें करता है कि data.table . data.table नहीं कर सकते हैं।

आपका बिंदु # 3

dplyr abstracts (या इच्छा) संभावित डीबी इंटरैक्शन

आपके अपने प्रश्न का सीधा जवाब है लेकिन इसे उच्च स्तर तक नहीं बढ़ाया गया है। dplyr वास्तव में एक से अधिक डेटा भंडारण तंत्र के लिए एक विस्तारणीय फ्रंट एंड है जहां डेटा. data.table एक के लिए एक विस्तार है।

एक ही व्याकरण का उपयोग कर सभी लक्ष्यों के साथ, बैक-एंड अज्ञेय इंटरफ़ेस के रूप में dplyr को देखें, जहां आप dplyr लक्ष्य और हैंडलर का विस्तार कर सकते हैं। data.table is, from the dplyr perspective, one of those targets.

You will never (I hope) see a day that data.table attempts to translate your queries to create SQL statements that operate with on-disk or networked data stores.

dplyr can possibly do things data.table will not or might not do as well.

Based on the design of working in-memory, data.table could have a much more difficult time extending itself into parallel processing of queries than dplyr .

In response to the in-body questions...

प्रयोग

Are there analytical tasks that are a lot easier to code with one or the other package for people familiar with the packages (ie some combination of keystrokes required vs. required level of esotericism, where less of each is a good thing).

This may seem like a punt but the real answer is no. People familiar with tools seem to use the either the one most familiar to them or the one that is actually the right one for the job at hand. With that being said, sometimes you want to present a particular readability, sometimes a level of performance, and when you have need for a high enough level of both you may just need another tool to go along with what you already have to make clearer abstractions.

प्रदर्शन

Are there analytical tasks that are performed substantially (ie more than 2x) more efficiently in one package vs. another.

Again, no. data.table excels at being efficient in everything it does where dplyr gets the burden of being limited in some respects to the underlying data store and registered handlers.

This means when you run into a performance issue with data.table you can be pretty sure it is in your query function and if it is actually a bottleneck with data.table then you've won yourself the joy of filing a report. This is also true when dplyr is using data.table as the back-end; you may see some overhead from dplyr but odds are it is your query.

When dplyr has performance issues with back-ends you can get around them by registering a function for hybrid evaluation or (in the case of databases) manipulating the generated query prior to execution.

Also see the accepted answer to when is plyr better than data.table?


अरुण के उत्तर की विस्तृत रूपरेखा के बाद, लेकिन अलग-अलग प्राथमिकताओं के आधार पर कुछ हद तक पुन: व्यवस्थित होने के बाद, dplyr परिप्रेक्ष्य से व्यापक उत्तर पर मेरा प्रयास यहां दिया गया है।

वाक्य - विन्यास

सिंटैक्स के लिए कुछ विषय-वस्तु है, लेकिन मैं अपने बयान से खड़ा हूं कि डेटाटेबल की सहमति से सीखना मुश्किल हो जाता है और पढ़ने में मुश्किल होती है। यह आंशिक रूप से है क्योंकि dplyr एक बहुत ही आसान समस्या हल कर रहा है!

एक वास्तव में महत्वपूर्ण बात यह है कि आपके लिए dplyr करता है कि यह आपके विकल्पों को बाधित करता है। मेरा दावा है कि "एकल समूह" adverb के साथ-साथ, केवल एक ही तालिका की समस्याओं को हल करने के लिए केवल पांच कुंजी क्रियाएं फ़िल्टर, चयन, उत्परिवर्तन, व्यवस्था और संक्षेप में हल किया जा सकता है। जब आप डेटा हेरफेर सीख रहे हों तो यह बाधा एक बड़ी मदद है, क्योंकि इससे समस्या के बारे में आपकी सोच को ऑर्डर करने में मदद मिलती है। Dplyr में, इन क्रियाओं में से प्रत्येक एक समारोह में मैप किया गया है। प्रत्येक कार्य एक नौकरी करता है, और अलगाव में समझना आसान है।

आप इन साधारण परिचालनों को %>% साथ एक साथ पाइप करके जटिलता बनाते हैं। अरुण से जुड़े पदों में से एक का उदाहरण यहां दिया गया है :

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

भले ही आपने कभी पहले (या यहां तक ​​कि आर!) को कभी भी नहीं देखा है, फिर भी आप क्या हो रहा है इसके बारे में जानकारी प्राप्त कर सकते हैं क्योंकि कार्य सभी अंग्रेजी क्रियाएं हैं। अंग्रेजी क्रियाओं का नुकसान यह है कि उन्हें [ से अधिक टाइपिंग की आवश्यकता होती है, लेकिन मुझे लगता है कि इसे बेहतर स्वतः पूर्ण करके कम किया जा सकता है।

समकक्ष डेटाटेबल कोड यहां दिया गया है:

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

जब तक आप डेटा.table से पहले से परिचित नहीं हैं, तब तक इस कोड का पालन करना कठिन होता है। (मैं यह भी नहीं समझ सका कि बार-बार कैसे इंडेंट करना है [ जिस तरह से मेरी आंखों के लिए अच्छा लग रहा है)। निजी तौर पर, जब मैं 6 महीने पहले लिखा था कोड को देखता हूं, तो यह एक अजनबी द्वारा लिखे गए कोड को देखने जैसा है, इसलिए यदि वर्बोज़, कोड है तो मैं सीधा पसंद करता हूं।

दो अन्य मामूली कारक जो मुझे लगता है कि थोड़ा कम पठनीयता:

  • चूंकि लगभग हर डेटा टेबल ऑपरेशन का उपयोग करता है [ आपको यह पता लगाने के लिए अतिरिक्त संदर्भ की आवश्यकता है कि क्या हो रहा है। उदाहरण के लिए, x[y] दो डेटा तालिकाओं में शामिल होना या डेटा फ्रेम से कॉलम निकालना है? यह केवल एक छोटा मुद्दा है, क्योंकि अच्छी तरह से लिखित कोड में वेरिएबल नामों का सुझाव देना चाहिए कि क्या हो रहा है।

  • मुझे लगता है कि group_by() में एक अलग ऑपरेशन है। यह मौलिक रूप से गणना को बदलता है, इसलिए मुझे लगता है कि कोड को group_by() करते समय स्पष्ट होना चाहिए, और [.data.table तर्क के मुकाबले group_by() को स्थान group_by() आसान है।

मुझे यह भी पसंद है कि पाइप सिर्फ एक पैकेज तक ही सीमित नहीं है। आप अपने डेटा को tidyr साथ tidyr शुरू कर सकते हैं, और ggvis में एक साजिश के साथ खत्म हो सकता है । और आप मेरे द्वारा लिखे गए संकुल तक सीमित नहीं हैं - कोई भी एक फ़ंक्शन लिख सकता है जो डेटा मैनिपुलेशन पाइप का एक निर्बाध हिस्सा बनाता है। असल में, मैं पिछले डेटा.table कोड को %>% साथ फिर से लिखना पसंद करता हूं:

diamonds %>% 
  data.table() %>% 
  .[cut != "Fair", 
    .(AvgPrice = mean(price),
      MedianPrice = as.numeric(median(price)),
      Count = .N
    ), 
    by = cut
  ] %>% 
  .[order(-Count)]

और %>% साथ पाइपिंग का विचार केवल डेटा फ्रेम तक सीमित नहीं है और आसानी से अन्य संदर्भों के लिए सामान्यीकृत किया जाता है: इंटरैक्टिव वेब ग्राफिक्स , वेब स्क्रैपिंग , gists , रन-टाइम अनुबंध , ...)

मेमोरी और प्रदर्शन

मैंने इन्हें एक साथ लम्बा कर दिया है, क्योंकि, मेरे लिए, वे महत्वपूर्ण नहीं हैं। अधिकांश आर उपयोगकर्ता डेटा के 1 मिलियन पंक्तियों के साथ अच्छी तरह से काम करते हैं, और dplyr डेटा के उस आकार के लिए पर्याप्त तेज़ पर्याप्त है जिसे आप समय प्रसंस्करण के बारे में नहीं जानते हैं। हम मध्यम डेटा पर अभिव्यक्ति के लिए dplyr अनुकूलित करते हैं; बड़े डेटा पर कच्चे गति के लिए डेटा.table का उपयोग करने के लिए स्वतंत्र महसूस करें।

Dplyr की लचीलापन का भी अर्थ है कि आप एक ही वाक्यविन्यास का उपयोग करके प्रदर्शन विशेषताओं को आसानी से बदल सकते हैं। यदि डेटा फ्रेम बैकएंड के साथ dplyr का प्रदर्शन आपके लिए पर्याप्त नहीं है, तो आप डेटा.table बैकएंड का उपयोग कर सकते हैं (यद्यपि कार्यक्षमता के कुछ सीमित सेट के साथ)। यदि आप जिस डेटा के साथ काम कर रहे हैं वह स्मृति में फिट नहीं है, तो आप डेटाबेस बैकएंड का उपयोग कर सकते हैं।

जो कुछ भी कहा गया है, लंबे समय तक प्रदर्शन का प्रदर्शन बेहतर होगा। हम निश्चित रूप से डेटा के कुछ महान विचारों को लागू करेंगे जैसे रैडिक्स ऑर्डरिंग और जॉइन और फ़िल्टर के लिए एक ही इंडेक्स का उपयोग करना। हम समांतरता पर भी काम कर रहे हैं ताकि हम कई कोर का लाभ उठा सकें।

विशेषताएं

कुछ चीजें जिन्हें हम 2015 में काम करने की योजना बना रहे हैं:

  • readr पैकेज, फ़ाइलों को डिस्क से और मेमोरी में प्राप्त करना आसान बनाता है, जो readr fread() समान होता है।

  • गैर-इक्विटी-जॉइन के लिए समर्थन सहित अधिक लचीला जुड़ता है।

  • बूटस्ट्रैप नमूने, रोलअप और अधिक जैसे अधिक लचीला समूह

मैं आर के डेटाबेस कनेक्टर , वेब एपिस से बात करने की क्षमता, और एचटीएमएल पृष्ठों को स्क्रैप करना आसान बनाने में भी समय निवेश कर रहा हूं।





dplyr