Data.table में कुंजी सेट करने का उद्देश्य क्या है?


113

मैं data.table का उपयोग कर रहा हूं और ऐसे कई कार्य हैं जिनके लिए मुझे एक कुंजी (जैसे X[Y]) सेट करने की आवश्यकता है । जैसे, मैं यह समझना चाहता हूं कि मेरे डेटा तालिकाओं में कुंजियों को ठीक से सेट करने के लिए एक कुंजी क्या करती है।


एक स्रोत जो मैंने पढ़ा था ?setkey

setkey()a data.tableको सॉर्ट करें और इसे सॉर्ट करें। सॉर्ट किए गए कॉलम कुंजी हैं। कुंजी किसी भी क्रम में कोई भी कॉलम हो सकती है। स्तंभों को हमेशा बढ़ते क्रम में क्रमबद्ध किया जाता है। तालिका को संदर्भ द्वारा बदल दिया जाता है। अस्थायी रूप से काम करने वाली मेमोरी के अलावा कोई भी कॉपी एक कॉलम के रूप में बड़ी नहीं होती है।

यहाँ मेरा तात्पर्य यह है कि एक कुंजी data.table को "सॉर्ट" करेगी, जिसके परिणामस्वरूप बहुत ही समान प्रभाव होगा order()। हालाँकि, यह एक कुंजी होने के उद्देश्य की व्याख्या नहीं करता है।


Data.table FAQ 3.2 और 3.3 बताते हैं:

3.2 मेरे पास एक बड़ी मेज पर चाबी नहीं है, लेकिन समूह बनाना अभी भी बहुत जल्दी है। ऐसा क्यों है?

data.table मूलांक छँटाई का उपयोग करता है। यह अन्य प्रकार के एल्गोरिदम की तुलना में सांकेतिक रूप से तेज है। मूलांक केवल पूर्णांकों के लिए विशिष्ट रूप से है, देखें ?base::sort.list(x,method="radix")। यह भी एक कारण है कि setkey()जल्दी क्यों है। जब कोई कुंजी सेट नहीं होती है, या हम उस कुंजी से एक अलग क्रम में समूह बनाते हैं, तो हम इसे एक तदर्थ कहते हैं।

३. 3.3 कुंजी में कॉलम को समूह द्वारा विज्ञापन समूह की तुलना में अधिक तेज़ी से क्यों जोड़ा जाता है?

क्योंकि प्रत्येक समूह रैम में सन्निहित है, जिससे पेज भ्रूण कम हो जाते हैं, और मेमोरी को memcpyC में लूप के बजाय बल्क ( C) में कॉपी किया जा सकता है।

यहां से, मुझे लगता है कि कुंजी को किसी भी तरह सेट करने से आर अन्य एल्गोरिदम पर "रेडिक्स सॉर्टिंग" का उपयोग करने की अनुमति देता है, और इसीलिए यह तेज है।


10 मिनट की त्वरित शुरुआत गाइड में चाबियों पर एक गाइड भी है।

  1. चांबियाँ

आइए डेटा.फ्रेम, विशिष्ट रूप से पंक्तिबद्ध नाम (या अंग्रेजी में, पंक्ति नामों) पर विचार करके शुरू करें। यही है, एक पंक्ति से संबंधित कई नाम। एकल पंक्ति से संबंधित कई नाम? यह वह नहीं है जो हमें data.frame में उपयोग किया जाता है। हम जानते हैं कि प्रत्येक पंक्ति में एक ही नाम है। एक व्यक्ति के कम से कम दो नाम होते हैं, एक rst नाम और दूसरा नाम। उदाहरण के लिए, टेलीफ़ोन डायरेक्टरी को व्यवस्थित करना उपयोगी है, जिसे उपनाम से क्रमबद्ध किया जाता है, फिर rst नाम। हालाँकि, data.frame में प्रत्येक पंक्ति का केवल एक नाम हो सकता है।

एक कुंजी में एक या अधिक स्तंभों के स्तंभ होते हैं, जो केवल वर्ण नहीं, बल्कि पूर्णांक, कारक, वर्ण या कुछ अन्य वर्ग हो सकते हैं। इसके अलावा, पंक्तियों को कुंजी द्वारा क्रमबद्ध किया जाता है। इसलिए, एक डेटाटेबल में अधिकतम एक कुंजी हो सकती है, क्योंकि इसे एक से अधिक तरीकों से सॉर्ट नहीं किया जा सकता है।

विशिष्टता को लागू नहीं किया जाता है, अर्थात, डुप्लिकेट कुंजी मानों की अनुमति है। चूँकि पंक्तियों को कुंजी द्वारा क्रमबद्ध किया जाता है, कुंजी में कोई भी डुप्लिकेट लगातार दिखाई देगा

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


"मुझे लगता है कि किसी भी तरह एक कुंजी सेट करने से आर को अन्य एल्गोरिदम पर" मूलांक छँटाई "का उपयोग करने की अनुमति मिलती है" - मुझे यह मदद से नहीं मिलता है। मेरा पढ़ा है कि एक कुंजी द्वारा एक प्रमुख प्रकार की स्थापना। आप कुंजी की तुलना में अन्य कॉलमों के अनुसार "एड हॉक" कर सकते हैं, और यह तेज़ है, लेकिन उतना तेज़ नहीं है जितना कि आपने पहले ही छांटा था।
अरी बी। फ्रीडमैन

मुझे लगता है कि पंक्तियों का चयन करते समय द्विआधारी खोज वेक्टर स्कैन से तेज है। मैं एक कंप्यूटर वैज्ञानिक नहीं हूं, इसलिए मुझे नहीं पता कि वास्तव में इसका क्या मतलब है। एफएक्यू के अलावा, परिचय देखें ।
फ्रैंक

जवाबों:


125

मामूली अद्यतन: कृपया नए HTML विगनेट्स को भी देखें। यह समस्या अन्य विगनेट्स पर प्रकाश डालती है जिनकी हम योजना बनाते हैं।


मैंने इस जवाब को फिर से अपडेट किया है (फरवरी 2016) नई on=सुविधा के प्रकाश में जो तदर्थ जुड़ने की अनुमति देता है । पहले (पुराने) उत्तरों के लिए इतिहास देखें।

वास्तव में क्या करता setkey(DT, a, b)है?

यह दो काम करता है:

  1. हमेशा बढ़ते हुए क्रम में, संदर्भ द्वारा ( , बी ) प्रदान किए गए कॉलम (एस) द्वारा डेटाटेबल की पंक्तियों को फिर से व्यवस्थित करता है।DT
  2. उन स्तंभों को कुंजी कॉलम के रूप में चिह्नित करता है, जिन्हें एक विशेषता कहा जाता sortedहै DT

रीऑर्डरिंग दोनों तेज है ( डेटा के कारण। आंतरिक मूलांक छँटाई) और मेमोरी कुशल (टाइप डबल का केवल एक अतिरिक्त कॉलम आवंटित किया गया है)।

कब setkey()आवश्यक है?

समूह संचालन के लिए, setkey()कभी भी एक परम आवश्यकता नहीं थी। यही है, हम एक कोल्ड-बाय या एडहॉक-बाय कर सकते हैं

## "cold" by
require(data.table)
DT <- data.table(x=rep(1:5, each=2), y=1:10)
DT[, mean(y), by=x] # no key is set, order of groups preserved in result

हालाँकि, इससे पहले v1.9.6, सेट किए जाने वाले फॉर्म के x[i]जॉइन keyकरने की आवश्यकता होती है xV1.9.6 + से नए on=तर्क के साथ , यह अब सच नहीं है, और चाबियाँ सेट करना इसलिए यहां भी एक पूर्ण आवश्यकता नहीं है

## joins using < v1.9.6 
setkey(X, a) # absolutely required
setkey(Y, a) # not absolutely required as long as 'a' is the first column
X[Y]

## joins using v1.9.6+
X[Y, on="a"]
# or if the column names are x_a and y_a respectively
X[Y, on=c("x_a" = "y_a")]

ध्यान दें कि on=तर्क को स्पष्ट keyedरूप से और साथ ही जोड़ के लिए भी निर्दिष्ट किया जा सकता है ।

एकमात्र ऑपरेशन जिसे keyपूरी तरह से सेट करने की आवश्यकता होती है वह है फेवरलैप्स () फ़ंक्शन। लेकिन हम कुछ और फीचर्स पर काम कर रहे हैं जो किए जाने पर इस आवश्यकता को दूर करेंगे।

  • तो क्या on=तर्क को लागू करने का कारण है?

    इसके कुछ कारण हैं।

    1. यह दो डेटाटैब को शामिल करने वाले ऑपरेशन के रूप में ऑपरेशन को स्पष्ट रूप से अलग करने की अनुमति देता है । बस ऐसा X[Y]करना अलग नहीं है, हालांकि यह चर को उचित रूप से नाम देने से स्पष्ट हो सकता है।

    2. यह उन कॉलमों को समझने की अनुमति देता है जिन पर कोड की उस लाइन (और संबंधित लाइन पर ट्रेसबैक करने के लिए नहीं ) को देखकर तुरंत / सब्मिट किया जा रहा है setkey()

    3. उन ऑपरेशनों में जहां स्तंभों को जोड़ा जाता है या संदर्भ द्वारा अद्यतन किया जाता है , on=संचालन बहुत अधिक निष्पादित होते हैं क्योंकि इसमें संपूर्ण डेटा की आवश्यकता नहीं होती है। केवल स्तंभ / कॉलम जोड़ने / अपडेट करने के लिए इसे पुन: व्यवस्थित करने की आवश्यकता होती है। उदाहरण के लिए,

      ## compare 
      setkey(X, a, b) # why physically reorder X to just add/update a column?
      X[Y, col := i.val]
      
      ## to
      X[Y, col := i.val, on=c("a", "b")]

      दूसरे मामले में, हमें पुन: व्यवस्थित नहीं होना था। यह उस आदेश की गणना नहीं कर रहा है जो समय लेने वाला है, लेकिन रैम में data.table को भौतिक रूप से पुन: व्यवस्थित कर रहा है, और इसे टालने से, हम मूल आदेश को बनाए रखते हैं, और यह प्रदर्शन करने वाला भी है।

    4. अन्यथा, जब तक आप दोहराव में शामिल नहीं हो रहे हैं, तब तक एक कुंजी और तदर्थ सिक्के के बीच कोई ध्यान देने योग्य प्रदर्शन अंतर नहीं होना चाहिए ।

इससे यह सवाल होता है कि डेटाटेबल की कुंजी लगाने से क्या फायदा है ?

  • क्या कोई डेटा टाइप करने का कोई फायदा है।

    रैम में उन कॉलम (नों) के आधार पर एक डेटाटेबल को बदलना । आदेश की गणना आमतौर पर समय लेने वाला हिस्सा नहीं है, बल्कि खुद को पुनः व्यवस्थित करना है। हालाँकि, एक बार जब हम रैम में डेटा को सॉर्ट करते हैं, तो एक ही समूह से संबंधित पंक्तियाँ सभी रैम में सन्निहित हैं, और इसलिए यह बहुत ही कुशल है। यह क्रमबद्धता है जो कीटेड data.tables पर संचालन को गति देती है।

    इसलिए यह पता लगाना आवश्यक है कि यदि संपूर्ण डेटा को पुन: व्यवस्थित करने में लगने वाला समय खर्च किया जा सकता है। तो कैश-एफिशिएंट जॉइन / एग्रीगेशन करने का समय है। आमतौर पर, जब तक दोहरावदार समूहीकरण / ज्वाइन ऑपरेशन नहीं किए जाते हैं, तब तक एक ही कुंजी डेटा पर प्रदर्शन किया जा सकता है। ध्यान देने योग्य अंतर नहीं होना चाहिए।

इसलिए ज्यादातर मामलों में, अब चाबियाँ सेट करने की आवश्यकता नहीं होनी चाहिए। हम on=जहां तक ​​संभव हो, का उपयोग करने की सलाह देते हैं, जब तक कि कुंजी सेट करने से प्रदर्शन में नाटकीय सुधार नहीं होता है जिसे आप शोषण करना चाहते हैं।

प्रश्न: क्या आप एक की तुलना में की तरह प्रदर्शन किया जाएगा लगता है keyed में शामिल होने, अगर आप का उपयोग setorder()को पुन: व्यवस्थित करने के लिए data.table और उपयोग on=? यदि आपने इस प्रकार दूर तक पीछा किया है, तो आपको इसका पता लगाने में सक्षम होना चाहिए :-)।


3
धन्यवाद! अब तक, मैंने इस बारे में नहीं सोचा था कि "बाइनरी खोज" का वास्तव में क्या मतलब है, और न ही वास्तव में इसका कारण समझ में आया कि इसका उपयोग हैश के बजाय क्यों किया गया था।
फ्रैंक

@ अरुन, DT[J(1e4:1e5)]वास्तव में के बराबर है DF[DF$x > 1e4 & DF$x < 1e5, ]? क्या आप मुझे बता सकते हैं कि Jइसका क्या मतलब है? इसके अलावा कि खोज किसी भी पंक्तियों को वापस नहीं करेगी क्योंकि sample(1e4, 1e7, TRUE)इसमें 1e4 से ऊपर की संख्या शामिल नहीं है।
फिशटैंक

@fishtank, इस मामले में, यह होना चाहिए >=और <=- तय किया जाना चाहिए । J(और .) उपनाम हैं list(यानी, वे समतुल्य हैं)। आंतरिक रूप से जब iकोई सूची होती है, तो उसे डेटा में बदल दिया जाता है। निम्नलिखित के बाद बाइनरी खोज का उपयोग पंक्ति सूचकांकों की गणना करने के लिए किया जाता है। भ्रम से बचने के 1e4लिए निश्चित 1e5। हाजिर करने के लिए धन्यवाद। ध्यान दें कि हम सीधे on=कुंजी का उपयोग करने के बजाय द्विआधारी सबसेट को निष्पादित करने के लिए अब तर्क का उपयोग कर सकते हैं । नए HTML विगनेट्स से अधिक पढ़ें । और जुड़ने के लिए vignettes के लिए उस पृष्ठ पर नज़र रखें।
अरुण

शायद यह अधिक गहन अद्यतन के लिए जा सकता है? "जब आवश्यकता होती है" अनुभाग पुराना लगता है, जैसे
माइकलचिरिको

कौन सा फ़ंक्शन आपको उपयोग की जा रही कुंजी बताता है?
स्केन

20

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

यदि आप अनुक्रमित नहीं समझते हैं, तो इस पर विचार करें: एक फोन बुक नाम से "अनुक्रमित" है। इसलिए अगर मैं किसी का फोन नंबर देखना चाहता हूं, तो यह बहुत सीधा है। लेकिन मान लीजिए कि मैं फोन नंबर द्वारा खोज करना चाहता हूं (उदाहरण के लिए, किसी विशेष फोन नंबर को देखें)? जब तक मैं फोन नंबर द्वारा फोन बुक "री-इंडेक्स" नहीं कर सकता, तब तक बहुत समय लगेगा।

निम्नलिखित उदाहरण पर विचार करें: मान लें कि मेरे पास अमेरिका (> 33,000) में सभी ज़िप कोडों के साथ-साथ संबंधित जानकारी (शहर, राज्य, जनसंख्या, मध्यवर्गीय आय, आदि) है। यदि मैं एक विशिष्ट ज़िप कोड के लिए जानकारी देखना चाहता हूं, तो खोज (फ़िल्टर) यदि मैं setkey(ZIP,zipcode)पहली बार कर रहा हूं तो लगभग 1000 गुना तेज है ।

एक और लाभ जुड़ने से होता है। मान लीजिए कि डेटा तालिका में लोगों और उनके ज़िप कोड की सूची है (इसे "PPL" कहें), और मैं ज़िप तालिका (जैसे शहर, राज्य, और इसी तरह) से जानकारी जोड़ना चाहता हूं। निम्नलिखित कोड यह करेंगे:

setkey(ZIP,zipcode)
setkey(PPL,zipcode)
full.info <- PPL[ZIP, nomatch=F]

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

IMHO आपको सिर्फ पढ़ना नहीं चाहिए बल्कि एफएक्यू और इंट्रो सामग्री का अध्ययन करना चाहिए । अगर आपको इसे लागू करने के लिए कोई वास्तविक समस्या है, तो इसे समझना आसान है।

[@ फ्रैंक की टिप्पणी का जवाब]

पुन: क्रमबद्ध बनाम अनुक्रमण - इस प्रश्न के उत्तर के आधार पर , यह प्रतीत होता है कि setkey(...)वास्तव में तालिका में स्तंभों को पुनर्व्यवस्थित करता है (जैसे, एक भौतिक प्रकार), और डेटाबेस अर्थों में एक सूचकांक नहीं बनाता है। इसके कुछ व्यावहारिक निहितार्थ हैं: एक चीज के लिए यदि आप कुंजी को किसी तालिका में सेट करते हैं setkey(...)और फिर कुंजी स्तंभ में किसी भी मान को बदलते हैं, तो data.table केवल तालिका को अब छांटने ( sortedविशेषता को बंद करके ) घोषित करता है ; यह उचित प्रकार के क्रम को बनाए रखने के लिए गतिशील रूप से पुन: सूचकांक नहीं करता है (जैसा कि एक डेटाबेस में होता है)। इसके अलावा, "कुंजी को हटाने" का उपयोग करते हुए तालिका को मूल, बिना ऑर्डर के पुनर्स्थापित नहींsetky(DT,NULL) करता है ।

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


1
+1। आपके पहले वाक्य के बारे में ... यह पहले से ही सही है? और फ़िल्टर के एक विशेष मामले में शामिल नहीं है (या एक ऑपरेशन जो फ़िल्टरिंग को अपने पहले चरण के रूप में लेता है)? "बेहतर फ़िल्टरिंग" की तरह लगता है पूरे लाभ को sums।
फ्रैंक

1
या बेहतर स्कैनिंग मुझे लगता है।
गीले पैर

1
@jlhoward धन्यवाद मेरी पूर्व धारणा थी कि कुंजी सेट करने के लाभों में से छँटाई करना नहीं था (क्योंकि यदि आप छँटना चाहते हैं, तो आपको बस छाँटना चाहिए), और यह भी कि setkeyवास्तव में पंक्तियों को अपरिवर्तनीय रूप से पुन: व्यवस्थित करता है। यदि यह केवल प्रदर्शन के उद्देश्य के लिए है, तो मैं "सही" आदेश के अनुसार पहली दस पंक्तियों को कैसे प्रिंट करूं (जो कि मैंने पहले सेहत के लिए देखा होगा)? मुझे पूरा यकीन setkey(DT,NULL)है कि वह ऐसा नहीं करता है ... (प्रतियोगिता)
फ्रैंक

... (प्रतियोगिता।) इसके अलावा, मैंने पैकेज के लिए कोड को नहीं देखा है, लेकिन इसमें शामिल होने के लिए X[Y,...], आपको कुंजी का उपयोग करके एक्स की पंक्तियों को "फ़िल्टर" करने की आवश्यकता है। दी गई, उसके बाद अन्य सामान होता है (वाई के कॉलम उपलब्ध कराए जाते हैं, और इसका एक निहित अर्थ-रहित है), लेकिन मैं अभी भी वैचारिक रूप से अलग लाभ के रूप में नहीं देखता हूं। मुझे लगता है कि आपका जवाब उन आपरेशनों के संदर्भ में है जिन्हें आप करना चाहते हैं, हालांकि, जहां अंतर मददगार हो सकता है।
फ्रैंक

1
@ फ्रेंक - तो setkey(DT,NULL)कुंजी को हटाता है लेकिन सॉर्ट क्रम को प्रभावित नहीं करता है। इस बारे में एक प्रश्न यहाँ प्रस्तुत किया । चलो देखते हैं।
jlhoward
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.